使用 Three.js 和 Cannon.js 构建真实 3D 骰子动画

使用 Three.js 和 Cannon.js 构建真实 3D 骰子动画


3D 场景搭建

Cannon.js

  1. 设置重力。

    this.world.gravity.set(0, 0, -9.8 * 16);
    
  2. 设置 骰子地面 以及 墙体 三种材质,并两两设置联系材质。

    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,
    }));
    
  3. 添加地面以及墙体模型

    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

  1. 创建场景

    this.scene = new THREE.Scene();
    
  2. 设置环境光源

    const light = new THREE.AmbientLight(0xf0f5fb, 0.8);
    this.scene.add(light);
    
  3. 添加渲染器,初始化抗锯齿与透明功能

    this.renderer = new THREE.WebGLRenderer({canvas: this.canvas,antialias: true,alpha: true,
    });
    
  4. 配置渲染器

    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = THREE.PCFShadowMap;
    this.renderer.setClearColor(0x000000, 0);
    this.renderer.setPixelRatio(devicePixelRatio);
    
  5. 设置渲染尺寸

    this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
    
  6. 添加相机

    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);
    
  7. 添加轨道控制器并限制角度

    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;
    
  8. 添加点光源

    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);
    
  9. 添加桌面并设置阴影,导入材质

    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);
}

未完待续

  1. 骰子模型的导入
  2. 骰子的运动模型生成(实现可复现运动,多机视觉一致性,力度及方向控制)
  3. 骰子的点数识别(通过四元数结算实现)
  4. 骰子点数指定(通过运动模拟实现)


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部