使用 Three.js 和 Cannon.js 构建真实 3D 骰子动画
使用 Three.js 和 Cannon.js 构建真实 3D 骰子动画
3D 场景搭建
Cannon.js
-
设置重力。
this.world.gravity.set(0, 0, -9.8 * 16); -
设置
骰子、地面以及墙体三种材质,并两两设置联系材质。this.diceBodyMaterial = new CANNON.Material(); const deskBodyMaterial = new CANNON.Material(); const barrierBodyMaterial = new CANNON.Material(); this.world.addContactMaterial(new CANNON.ContactMaterial(deskBodyMaterial, this.diceBodyMaterial, {friction: 0.01,restitution: 0.5, })); this.world.addContactMaterial(new CANNON.ContactMaterial(barrierBodyMaterial, this.diceBodyMaterial, {friction: 0,restitution: 0.5, })); this.world.addContactMaterial(new CANNON.ContactMaterial(this.diceBodyMaterial, this.diceBodyMaterial, {friction: 0,restitution: 0.5, })); -
添加地面以及墙体模型
this.world.addBody(new CANNON.Body({mass: 0,shape: new CANNON.Plane(),material: deskBodyMaterial, }));let barrier = new CANNON.Body({mass: 0,shape: new CANNON.Plane(),material: barrierBodyMaterial, }) barrier.position.set(0, this.dimentions.height * 0.93, 0); barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI / 2); this.world.addBody(barrier);barrier = new CANNON.Body({mass: 0,shape: new CANNON.Plane(),material: barrierBodyMaterial, }) barrier.position.set(0, -this.dimentions.height * 0.93, 0); barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2); this.world.addBody(barrier);barrier = new CANNON.Body({mass: 0,shape: new CANNON.Plane(),material: barrierBodyMaterial, }) barrier.position.set(this.dimentions.width * 0.93, 0, 0); barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), -Math.PI / 2); this.world.addBody(barrier);barrier = new CANNON.Body({mass: 0,shape: new CANNON.Plane(),material: barrierBodyMaterial, }) barrier.position.set(-this.dimentions.width * 0.93, 0, 0); barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), Math.PI / 2); this.world.addBody(barrier);
Three.js
-
创建场景
this.scene = new THREE.Scene(); -
设置环境光源
const light = new THREE.AmbientLight(0xf0f5fb, 0.8); this.scene.add(light); -
添加渲染器,初始化抗锯齿与透明功能
this.renderer = new THREE.WebGLRenderer({canvas: this.canvas,antialias: true,alpha: true, }); -
配置渲染器
this.renderer.shadowMap.enabled = true; this.renderer.shadowMap.type = THREE.PCFShadowMap; this.renderer.setClearColor(0x000000, 0); this.renderer.setPixelRatio(devicePixelRatio); -
设置渲染尺寸
this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight); -
添加相机
const aspect = Math.min(this.canvas.clientWidth / this.dimentions.width, this.canvas.clientHeight / this.dimentions.height); const cameraZ = this.canvas.clientHeight / aspect / Math.tan(10 * Math.PI / 180); if (this.camera) this.scene.remove(this.camera); const y = this.camera ? this.camera.position.y * cameraZ / this.camera.position.z : -10; this.camera = new THREE.PerspectiveCamera(20, this.canvas.clientWidth / this.canvas.clientHeight, 1, cameraZ * 2); this.camera.position.set(0, y, cameraZ); -
添加轨道控制器并限制角度
this.control = new OrbitControls(this.camera, this.canvas); this.control.enableDamping = true; this.control.dampingFactor = 0.1; this.control.enableZoom = false; this.control.enablePan = false; this.control.minPolarAngle = Math.PI * 0.5; this.control.maxPolarAngle = Math.PI * 0.9; this.control.minAzimuthAngle = 0; this.control.maxAzimuthAngle = 0; -
添加点光源
const longSide = Math.max(this.dimentions.width, this.dimentions.height); if (this.light) this.scene.remove(this.light); this.light = new THREE.SpotLight(0xefdfd5, 2); this.light.position.set(-longSide / 2, longSide / 2, longSide * 2); this.light.target.position.set(0, 0, 0); this.light.distance = longSide * 5; this.light.castShadow = true; this.light.shadow.camera.near = longSide / 10; this.light.shadow.camera.far = longSide * 5; this.light.shadow.camera.fov = 50; this.light.shadow.bias = 0.001; this.light.shadow.mapSize.set(4096, 4096); this.scene.add(this.light); -
添加桌面并设置阴影,导入材质
if (this.desk) this.scene.remove(this.desk); const loader = new THREE.TextureLoader(); this.desk = new THREE.Mesh( new THREE.PlaneGeometry(this.dimentions.width * 2, this.dimentions.height * 2, 1, 1), new THREE.MeshPhongMaterial({map: loader.load('/assets/model/WoodFineDark.jpg'),reflectivity: 1,specular: 0x222222,shininess: 20,}), ); this.desk.receiveShadow = true; this.scene.add(this.desk);
开始渲染
场景初始化完成后即可开始渲染,这里通过 requestAnimationFrame 递归调用函数进行动画渲染
function render() {component.tick();requestAnimationFrame(render);component.renderer.render(component.scene, component.camera);
}
未完待续
- 骰子模型的导入
- 骰子的运动模型生成(实现可复现运动,多机视觉一致性,力度及方向控制)
- 骰子的点数识别(通过四元数结算实现)
- 骰子点数指定(通过运动模拟实现)
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
