three.js cannon.js物理引擎之齒輪動畫

郭先生的部落格發表於2021-03-05

郭先生今天繼續說一說cannon.js物理引擎,並用之前已經學習過的知識實現一個小動畫,知識點包括ConvexPolyhedron多邊形、Shape幾何體、Body剛體、HingeConstraint鉸鏈約束等等知識。因為我之前用純three.js 的THREEBSP實現過一個靜態的齒輪,現在就想配合cannon.js讓它的轉動更加的符合實際而不是勻速轉動。下面我們來說說我們要做的這個案例,這個小案例是由5個齒輪組成,最左面的是有動力的齒輪,我們可以控制它的速度,而右面三個齒輪是可以移動的,我們可以自由移動(有點類似於變速箱),嗯就是怎麼簡單。下面我們來說一說這個是怎麼實現的,效果如下圖,線上案例請點選部落格原文

1. three.js 實現齒輪模型

three.js幾何體在最前面我已經說過了,實現一個這樣的模型(在不考慮引用模型的情況下),我們可以考慮直接使用Geometry來繪製或者向THREE.Group()物件中新增基礎幾何體來繪製,這裡我們使用第二種方法。這裡的思路就是使用ConvexGeometry凸包做出鋸齒的齒,然後使用圓柱填充即可,如下圖,

下面是相關部分程式碼

var meshA, meshB, meshC, meshD, meshE;
var bodyA, bodyB, bodyC, bodyD, bodyE;
var axleA, axleB, axleC, axleD, axleE;
var pos = [-8.5, 0], pos1 = [6.4, 8], pos2 = [8.5, 0], pos3 = [10.6, -13];
var radius1 = 2, radius2 = 4, radius3 = 6;
var length = 0.5, thickness = 3;
var sin375 = 0.075, cos375 = 0.997;
var sin75 = 0.131, cos75 = 0.991;
var sin15 = 0.259, cos15 = 0.966;
var params = {
    positionX: 0,
    positionY: 0,
    speed: 1,
}

var triangleMesh1 = this.getTriangleMesh(sin15, cos15, radius1);
var triangleMesh2 = this.getTriangleMesh(sin75, cos75, radius2);
var triangleMesh3 = this.getTriangleMesh(sin375, cos375, radius3);
meshA = new THREE.Group();
meshB = new THREE.Group();
meshC = new THREE.Group();
meshD = new THREE.Group();
meshE = new THREE.Group();
for(let i=0; i<24; i++) {
    let angle = Math.PI * 2 * i * 15 / 360;
    let itemMesh = triangleMesh2.clone();
    itemMesh.rotation.z = angle;
    meshA.add(itemMesh.clone());
    meshB.add(itemMesh.clone());
    meshD.add(itemMesh.clone());
}
for(let i=0; i<12; i++) {
    let angle = Math.PI * 2 * i * 30 / 360;
    let itemMesh = triangleMesh1.clone();
    itemMesh.rotation.z = angle;
    meshC.add(itemMesh.clone());
}
for(let i=0; i<48; i++) {
    let angle = Math.PI * 2 * i * 7.5 / 360;
    let itemMesh = triangleMesh3.clone();
    itemMesh.rotation.z = angle;
    meshE.add(itemMesh.clone());
}
let cylindMesh1 = new THREE.Mesh(new THREE.CylinderGeometry(radius1,radius1,thickness,32,1), new THREE.MeshNormalMaterial());
let cylindMesh2 = new THREE.Mesh(new THREE.CylinderGeometry(radius2,radius2,thickness,32,1), new THREE.MeshNormalMaterial());
let cylindMesh3 = new THREE.Mesh(new THREE.CylinderGeometry(radius3,radius3,thickness,32,1), new THREE.MeshNormalMaterial());
cylindMesh1.rotation.x = Math.PI / 2;
cylindMesh2.rotation.x = Math.PI / 2;
cylindMesh3.rotation.x = Math.PI / 2;
meshA.add(cylindMesh2.clone());
meshB.add(cylindMesh2.clone());
meshC.add(cylindMesh1.clone());
meshD.add(cylindMesh2.clone());
meshE.add(cylindMesh3.clone());

let cylindMesh = new THREE.Mesh(new THREE.CylinderGeometry(0.5,0.5,10,6,1), new THREE.MeshPhongMaterial({color: 0xffcc77, flatShading: true}));
cylindMesh.rotation.x = Math.PI / 2;
meshA.add(cylindMesh.clone());
meshB.add(cylindMesh.clone());
meshC.add(cylindMesh.clone());
meshD.add(cylindMesh.clone());
meshE.add(cylindMesh.clone());


scene.add(meshA);
scene.add(meshB);
scene.add(meshC);
scene.add(meshD);
scene.add(meshE);

getTriangleMesh(sin, cos, radius) {
    var pointsArr = [[sin * radius, cos * radius, thickness / 2], [sin * radius, cos * radius, -thickness / 2], [-sin * radius, cos * radius, thickness / 2], [-sin * radius, cos * radius, -thickness / 2], [0, radius + length, thickness / 2], [0, radius + length, -thickness / 2]];
    var points = pointsArr.map(d => new THREE.Vector3(d[0],d[1],d[2]));
    var triangle = new ConvexGeometry(points);
    return new THREE.Mesh(triangle, new THREE.MeshNormalMaterial());;
},

2. 新增cannon.js物理引擎

這裡就要用到前面兩篇的內容了,一個是HingeConstraint鉸鏈約束,另一是ConvexPolyhedron多邊形。齒輪沿著軸旋轉,我們通過鉸鏈約束把齒輪和軸約束到一起,讓齒輪僅沿著軸的方向轉動,接下來是通過ConvexPolyhedron多邊形制作出齒輪的齒,然後通過Body的addShape方法,將齒輪的齒新增到剛體中。思路其實是很簡單的,不過裡面有一些小計算。
下面我們看程式碼

//齒輪齒的shape
let verts = [
    new CANNON.Vec3(-sin75 * radius1, -(radius1 + length - cos75 * radius1) / 2, thickness / 2),
    new CANNON.Vec3(0, (radius1 + length - cos75 * radius1) / 2, thickness / 2),
    new CANNON.Vec3(sin75 * radius1, -(radius1 + length - cos75 * radius1) / 2, thickness),
    new CANNON.Vec3(-sin75 * radius1, -(radius1 + length - cos75 * radius1) / 2, -thickness / 2),
    new CANNON.Vec3(0, (radius1 + length - cos75 * radius1) / 2, -thickness / 2),
    new CANNON.Vec3(sin75 * radius1, -(radius1 + length - cos75 * radius1) / 2, -thickness / 2),
];
let faces = [[0,2,1], [3,4,5], [0,1,3], [1,4,3], [5,4,1], [2,5,1], [3,5,2], [3,2,0]];
let shape = new CANNON.ConvexPolyhedron(verts, faces);
//齒輪的軸
axleA = new CANNON.Body({
    mass: 0,
    position: new CANNON.Vec3(pos[0], pos[1], 0),
    shape: new CANNON.Cylinder(0.1, 0.1, 1, 3),
})
//齒輪剛體
bodyA = new CANNON.Body({
    mass: 1,
    position: new CANNON.Vec3(pos[0], pos[1], 0)
})
//為齒輪剛體新增齒
for(let i=0; i<12; i++) {
    let angle = i / 12 * Math.PI * 2;
    let x = Math.sin(angle) * (cos15 * radius1 + (radius1 + length - cos15 * radius1) / 2);
    let y = Math.cos(angle) * (cos15 * radius1 + (radius1 + length - cos15 * radius1) / 2);
    bodyC.addShape(shape, new CANNON.Vec3(x, y, 0), new CANNON.Quaternion().setFromEuler(0, 0, -angle));
}
//為輪和齒設定鉸鏈約束
var c1 = new CANNON.HingeConstraint(axleA, bodyA, { 
    pivotA: new CANNON.Vec3(0, 0, 0),
    axisA: new CANNON.Vec3(0, 0, 1),
    pivotB: new CANNON.Vec3(0, 0, 0),
    axisB: new CANNON.Vec3(0, 0, 1),
    maxForce: 100
});
world.addConstraint(c1);
//讓齒輪不斷的動起來
bodyA.angularVelocity.set(0, 0, params.speed);

然後在製作其他四個齒輪,然後放在不同的位置,在通過gui設定一些可操作項。這樣我們就完成了一個簡單的齒輪機組了,這一系列的cannon.js的文章應該還剩一兩篇。希望大家都掌握好。

 

轉載請註明地址:郭先生的部落格

 

相關文章