今天郭先生说的是一个物理引擎,它十分小巧并且操作简单,没错他就是cannon.js。这些优点都源自于他是基于js编写的,对于js使用者来说cannon.js拥有其他物理引擎没有的纯粹性。从学习成本来看,cannon.js的学习成本比较低,对于新手来说比较友好,因为它有相对完善的api,学习cannon.js之前我们不妨来看看cannon.js的官方网站以及他的API,对于js学习者来说这是十分必要的。官网上面有一些example,他们十分典型并囊括了大多数的知识点,配合api一起学习是个不错的选择。在线案例请点击博客原文。效果如下图,接下来以一个小案例,简单的介绍一下cannon.js。

three.js 之cannon.js物理引擎-LMLPHP

1. 初始化three场景

创建three场景(或者说three世界)来作为物理世界的载体,这一步很简单,主要就是添加渲染器、场景、相机和网格等three元素,没必要多说。

scene = new THREE.Scene();//step 1 创建场景

camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.y = 30;
camera.position.z = 20;
camera.lookAt(0,5,0);
scene.add( camera ); //step 2 场景中添加相机

scene.add(new THREE.AmbientLight(0x888888));
const light = new THREE.DirectionalLight(0xbbbbbb, 1);
light.position.set(6, 30, 6);
scene.add(light); //step 3 场景中添加另种光源

renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
renderer.setClearColor(0xbfd1e5);
this.$refs.box.appendChild(renderer.domElement); //step 4 dom中添加渲染器

let groundGeom = new THREE.BoxBufferGeometry(40, 0.2, 40);
let groundMate = new THREE.MeshPhongMaterial({color: 0xdddddd, map: texture})
ground = new THREE.Mesh(groundGeom, groundMate);
ground.position.y = -0.1;
ground.receiveShadow = true;
scene.add(ground); //step 5 添加地面网格

2. 初始化物理世界

这里是初始化物理世界,我们详细的讲一下他们的用法。

initCannon() {
    world = new CANNON.World(); //该方法初始化物理世界,里面包含着物理世界的相关数据(如刚体数据,世界中所受外力等等)
    world.gravity.set(0,-9.8,0); //设置物理世界的重力为沿y轴向上-9.8米每二次方秒
    world.broadphase = new CANNON.NaiveBroadphase();//NaiveBroadphase是默认的碰撞检测方式,该碰撞检测速度比较高
    world.solver.iterations = 5;//解算器的迭代次数,更高的迭代次数意味着更加精确同时性能将会降低

    bodyGround = new CANNON.Body({ //创建一个刚体(物理世界的刚体数据)
        mass: 0, //刚体的质量,这里单位为kg
        position: new CANNON.Vec3(0, -0.1, 0), //刚体的位置,单位是米
        shape: new CANNON.Box(new CANNON.Vec3(20, 0.1, 20)), //刚体的形状(这里是立方体,立方体的参数是一个包含半长、半宽、半高的三维向量,具体我们以后会说)
        material: new CANNON.Material({friction: 0.05, restitution: 0}) //材质数据,里面规定了摩擦系数和弹性系数
    });
    ground.userData = bodyGround; //将刚体的数据赋值给地面网格的userData属性
    world.addBody(bodyGround); //物理世界添加地面刚体
},

3. 向场景中添加网格并向物理世界中添加刚体数据

这里我们通过setInterval函数我们定时向场景中添加网格并向物理世界中添加刚体数据,

interval = setInterval(() => {
    this.createBox(); //创建网格和刚体的方法
}, 200);

下面是具体的方法

createBox() {
    let x = Math.random() * 10 - 5;
    let z = Math.random() * 10 - 5;
    let box = new THREE.Mesh( geometry, this.createRandomMaterial() ); //createRandomMaterial创建随机颜色的材质
    box.position.set(x, 20, z);
    scene.add( box ); //创建box,并添加到场景

    let bodyBox = new CANNON.Body({
        mass: 1,
        position: new CANNON.Vec3(x, 20, z),
        shape: new CANNON.Box(new CANNON.Vec3(1,1,1)),
        material: new CANNON.Material({friction: 0.1, restitution: 0})
    });//创建一个质量为1kg,位置为(x,20,z),形状为halfSize为1,1,1的正方形刚体,材质中摩擦系数为0.1,弹性系数为0。
    box.userData = bodyBox;//给box的userData属性添加刚体数据
    world.addBody(bodyBox);//在物理世界中添加该刚体

    setTimeout(() => { //10秒钟之后在场景中移除box,并在物理世界中移除该刚体
        scene.remove(box);
        box.material.dispose();
        box.geometry.dispose();
        world.removeBody(bodyBox);
    }, 10000)
},

4. 根据物理引擎的数据更新three网格数据

这一步我们逐帧根据物理引擎的数据渲染three场景

animation() { //requestAnimationFrame动画中调用render方法
    this.globalID = requestAnimationFrame(this.animation);
    this.render();
},
render() { //更新性能插件,根据物理引擎数据更新网格数据,最后渲染场景
    stats.update();
    this.updatePhysics();
    renderer.render( scene, camera );
},
updatePhysics() { // world.step
    world.step(timeStep); //第一个参数是以固定步长更新物理世界参数(详情请看api)
    scene.children.forEach(d => {//遍历场景中的子对象,如果对象的isMesh属性为true,我们就将更新改对象的position和quaternion属性(他们对应的刚体数据存在对应的userData中)。
        if(d.isMesh == true) {
            d.position.copy(d.userData.position);
            d.quaternion.copy(d.userData.quaternion);
        }
    })
}

这样我们就将cannon.js物理引擎应用到了three中。不出意外的话,接下来我会讲解一下官方的examples。

转载请注明地址:郭先生的博客

01-19 02:51