03 - Cannon 材质与摩擦系数设置
本节目标
- 理解什么是物理材质(Material)
- 学会设置摩擦系数
friction
- 使用
ContactMaterial
控制材质间交互 - 观察摩擦力对运动的影响
材质与摩擦介绍
在 Cannon.js 中:
Material
是一个刚体的物理属性载体,包括摩擦、弹性等。- 只有在
ContactMaterial
中组合两个Material
才能生效。 ContactMaterial
可定义:friction
摩擦系数restitution
弹性系数(可反弹)
举个例子:
js
const wood = new CANNON.Material('wood')
const ice = new CANNON.Material('ice')
const contactMat = new CANNON.ContactMaterial(wood, ice, {
friction: 0.05,
restitution: 0.1
})
world.addContactMaterial(contactMat)
示例:三个立方体摩擦不同的滑动对比
我们创建一个倾斜的平面,然后放上三个摩擦系数不同的立方体,观察它们滑动速度的不同。
完整 Vue3 示例(使用 cannon-es)
vue
<script setup>
import { onMounted, ref } from 'vue'
import * as THREE from 'three'
import * as CANNON from 'cannon-es'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
const canvasRef = ref()
onMounted(() => {
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.set(0, 5, 15)
const renderer = new THREE.WebGLRenderer({ canvas: canvasRef.value })
renderer.setSize(window.innerWidth, window.innerHeight)
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
const world = new CANNON.World()
world.gravity.set(0, -9.82, 0)
// 创建基础材质
const groundMaterial = new CANNON.Material('ground')
const matLow = new CANNON.Material('low')
const matMid = new CANNON.Material('mid')
const matHigh = new CANNON.Material('high')
// 设置接触材质(摩擦系数不同)
world.addContactMaterial(new CANNON.ContactMaterial(groundMaterial, matLow, { friction: 0.0 }))
world.addContactMaterial(new CANNON.ContactMaterial(groundMaterial, matMid, { friction: 0.3 }))
world.addContactMaterial(new CANNON.ContactMaterial(groundMaterial, matHigh, { friction: 1.0 }))
// 添加地面(倾斜)
const groundGeo = new THREE.BoxGeometry(20, 1, 10)
const groundMat = new THREE.MeshStandardMaterial({ color: 0x888888 })
const groundMesh = new THREE.Mesh(groundGeo, groundMat)
groundMesh.position.set(0, -0.5, 0)
groundMesh.rotation.z = -0.3
scene.add(groundMesh)
const groundBody = new CANNON.Body({
mass: 0,
shape: new CANNON.Box(new CANNON.Vec3(10, 0.5, 5)),
position: new CANNON.Vec3(0, -0.5, 0),
material: groundMaterial
})
groundBody.quaternion.setFromEuler(0, 0, -0.3)
world.addBody(groundBody)
// 立方体基础设置
const cubeGeo = new THREE.BoxGeometry(1, 1, 1)
const cubeShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))
const cubes = []
const bodies = []
const settings = [
{ x: -4, color: 0xff0000, material: matLow },
{ x: 0, color: 0xffff00, material: matMid },
{ x: 4, color: 0x00ff00, material: matHigh }
]
for (let i = 0; i < 3; i++) {
const { x, color, material } = settings[i]
const mesh = new THREE.Mesh(
cubeGeo,
new THREE.MeshStandardMaterial({ color })
)
mesh.position.set(x, 2, 0)
scene.add(mesh)
const body = new CANNON.Body({
mass: 1,
shape: cubeShape,
position: new CANNON.Vec3(x, 2, 0),
material
})
world.addBody(body)
cubes.push(mesh)
bodies.push(body)
}
// 添加灯光
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(10, 20, 10)
scene.add(light)
const clock = new THREE.Clock()
const timeStep = 1 / 60
function animate() {
requestAnimationFrame(animate)
const delta = clock.getDelta()
world.step(timeStep, delta)
for (let i = 0; i < cubes.length; i++) {
cubes[i].position.copy(bodies[i].position)
cubes[i].quaternion.copy(bodies[i].quaternion)
}
groundMesh.quaternion.copy(groundBody.quaternion)
controls.update()
renderer.render(scene, camera)
}
animate()
})
</script>
<template>
<canvas ref="canvasRef"></canvas>
</template>

观察结果
- 绿色立方体:
friction = 0
,滑得最远 - 黄色立方体:
friction = 0.3
,滑行距离适中 - 红色立方体:
friction = 1.0
,几乎立即停止
这就是摩擦系数对运动的控制作用。
小结
- 使用
Material
可以控制物体的物理属性 - 必须用
ContactMaterial
来定义两个材质之间的行为 friction
越大,滑动越慢甚至停止- 可以使用多组材质,设置多种不同摩擦情况
- 倾斜地面是检验摩擦的最佳手段之一