初探 Threejs 物理引擎CANNON,解鎖 3D 動態(tài)魅力

簡介

Cannon.js 是一個基于 JavaScript 的物理引擎,它可以在瀏覽器中模擬物理效果。它支持碰撞檢測、剛體動力學、約束等物理效果,可以用于創(chuàng)建逼真的物理場景和交互。

參考文檔

官方示例

原理

Cannon.js 使用了歐拉角來表示物體的旋轉(zhuǎn),而不是四元數(shù)。這使得它在處理旋轉(zhuǎn)時更加直觀和易于理解。Cannon.js 還支持多種碰撞檢測算法,包括離散碰撞檢測和連續(xù)碰撞檢測。

Cannon.js 還支持多種約束類型,包括固定約束、滑動約束和鉸鏈約束等。這些約束可以用于限制物體的運動,例如將兩個物體固定在一起,或者將一個物體限制在一個滑動路徑上。這使得 Cannon.js 可以創(chuàng)建逼真的物理場景和交互。

安裝

npm install cannon-es

使用

需要要使用 Cannon.js,首先需要創(chuàng)建一個物理世界,然后向其中添加物體和約束。接下來,在每個時間步長中更新物理世界,并使用物理世界中的物體和約束來計算物體的位置和旋轉(zhuǎn),由于在物理世界中創(chuàng)建的物體是看不到的,所以需要將這些位置和旋轉(zhuǎn)應(yīng)用到 Three.js 場景中的物體上。

引入

import * as CANNON from "cannon-es";

創(chuàng)建物理世界

const world = new CANNON.World();
world.gravity.set(0, -9.82, 0); // 設(shè)置重力

word.gravity: 屬性用于設(shè)置物理世界的重力。

  • x: 重力的 x 分量。
  • y: 重力的 y 分量。
  • z: 重力的 z 分量。

創(chuàng)建物體

//物理世界物體
const sphereShape = new CANNON.Sphere(1); // 創(chuàng)建一個球體形狀
const sphereBody = new CANNON.Body({
  mass: 1,
  shape: sphereShape,
  position: new CANNON.Vec3(0, 5, 0),
}); // 創(chuàng)建一個球體物體
world.addBody(sphereBody); // 將球體添加到物理世界中
//Three.js 場景中的物體
const sphereGeometry = new THREE.SphereGeometry(1, 32, 32); // 創(chuàng)建一個球體幾何體
const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 創(chuàng)建一個球體材質(zhì)
const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial); // 創(chuàng)建一個球體網(wǎng)格
scene.add(sphereMesh); // 將球體網(wǎng)格添加到場景中

CANNON.Sphere(): 方法用于創(chuàng)建一個球體形狀。

  • radius: 球體的半徑。

CANNON.Body(): 方法用于創(chuàng)建一個物體。

  • mass: 物體的質(zhì)量。
  • shape: 物體的形狀。
  • position: 物體的位置。

world.addBody(): 方法用于將物體添加到物理世界中。

更新物理世界

function animate() {
  requestAnimationFrame(animate);
  world.step(1 / 60); // 更新物理世界
  // 將物理世界中的球體位置應(yīng)用到 Three.js 場景中的球體網(wǎng)格上
  sphereMesh.position.copy(sphereBody.position);
  // 將物理世界中的球體旋轉(zhuǎn)應(yīng)用到 Three.js 場景中的球體網(wǎng)格上
  sphereMesh.quaternion.copy(sphereBody.quaternion);
  renderer.render(scene, camera); // 渲染場景
}
animate();

world.step(): 方法用于更新物理世界中的物體和約束的狀態(tài)。

  • dt: 時間步長,用于計算物體的運動和碰撞。
  • time: 當前時間,用于計算物體的運動和碰撞。
  • correction: 時間步長修正,用于修正時間步長導致的誤差。

copy(): 方法用于將一個向量的值復制到另一個向量中。

  • v: 要復制的向量。
cannon1.gif

碰撞檢測

Cannon.js 支持多種碰撞檢測算法,包括離散碰撞檢測和連續(xù)碰撞檢測。離散碰撞檢測是在每個時間步長中檢查物體之間的碰撞,而連續(xù)碰撞檢測則是在物體之間可能發(fā)生碰撞的路徑上插入額外的檢查點。這使得 Cannon.js 可以處理復雜的碰撞場景,例如物體之間的穿透和重疊。

const groundShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5));
const groundBody = new CANNON.Body({
  shape: groundShape,
  position: new CANNON.Vec3(0, 0, 0),
  type: CANNON.Body.STATIC,
});
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1);
world.addBody(groundBody);

//創(chuàng)建一個平面的幾何體
const groundGeometry = new THREE.BoxGeometry(10, 0.2, 10);
//創(chuàng)建一個平面的材質(zhì)
const groundMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
//創(chuàng)建一個平面的網(wǎng)格
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
//設(shè)置平面的位置
ground.position.x = 0.1;
//將平面添加到場景中
scene.add(ground);

CANNON.Plane(): 方法用于創(chuàng)建一個平面形狀。

CANNON.Box(): 方法用于創(chuàng)建一個盒子形狀。

setFromAxisAngle(): 方法用于設(shè)置物體的旋轉(zhuǎn)。

  • axis: 旋轉(zhuǎn)軸。
  • angle: 旋轉(zhuǎn)角度。

body.type: 物體的類型

  • CANNON.Body.KINEMATIC: 物體是動態(tài)的,可以移動和旋轉(zhuǎn)。
  • CANNON.Body.STATIC: 物體是靜態(tài)的,不能移動和旋轉(zhuǎn)。
  • CANNON.Body.DYNAMIC: 物體是動態(tài)的,可以移動和旋轉(zhuǎn),并且受到物理世界的重力和其他力的作用。
const groundBody = new CANNON.Body({
  shape: groundShape,
  position: new CANNON.Vec3(0, 0, 0),
  type: CANNON.Body.STATIC, // 設(shè)置物體類型為靜態(tài)
}); 

示例

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
//引入CannonJS
import * as CANNON from "cannon-es";

// 創(chuàng)建場景
const scene = new THREE.Scene();
// 創(chuàng)建相機
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
camera.position.y = 5;
camera.position.x = -10;

// 創(chuàng)建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 創(chuàng)建物理世界
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);

// 創(chuàng)建一個球體
const sphereShape = new CANNON.Sphere(1);
const sphereBody = new CANNON.Body({
  mass: 1,
  shape: sphereShape,
  position: new CANNON.Vec3(0, 5, 0),
});
world.addBody(sphereBody);

// 創(chuàng)建一個平面
const groundShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5));
const groundBody = new CANNON.Body({
  shape: groundShape,
  position: new CANNON.Vec3(0, 0, 0),
  type: CANNON.Body.STATIC,
});
groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1);
world.addBody(groundBody);

//創(chuàng)建一個球體的幾何體
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32);
//創(chuàng)建一個球體的材質(zhì)
const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
//創(chuàng)建一個球體的網(wǎng)格
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
//將球體添加到場景中
scene.add(sphere);

//創(chuàng)建一個平面的幾何體
const groundGeometry = new THREE.BoxGeometry(10, 0.2, 10);
//創(chuàng)建一個平面的材質(zhì)
const groundMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
//創(chuàng)建一個平面的網(wǎng)格
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
//設(shè)置平面的位置
ground.position.x = 0.1;
//將平面添加到場景中
scene.add(ground);

//創(chuàng)建控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 啟用阻尼效果
controls.enableDamping = true;

function animate() {
  requestAnimationFrame(animate);
  world.step(1 / 60);
  sphere.position.copy(sphereBody.position);
  sphere.quaternion.copy(sphereBody.quaternion);
  renderer.render(scene, camera);
}

animate();

//監(jiān)聽窗口大小變化
window.addEventListener("resize", () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});
cannon2.gif

書洞筆記

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容