Three.js--》探尋Cannon.js構建震撼的3D物理交互體驗(一)

我們用three.js可以繪制出各種酷炫的畫面,但是當我們想要一個更加真實的物理效果的話,這個時候我們就需要一個物理的庫,接下來我們就講解一下今天要學習的canon,它可以給我們提供一個更加真實的物理效果,像物體的張力、摩擦力、拉伸、反彈等等各種真實的物理效果。該庫都能夠有一個非常好的模擬。

PS:目前博主在一家互聯網公司工作,該公司的編碼風格是vue+tsx,所以接下來的項目以該編碼風格進行舉例,詳細了解參考我之前的文章:地址?。

目錄

canon基本使用

基礎碰撞使用

材質與摩擦系數設置

彈性與接觸材質設置

碰撞與碰撞組


canon基本使用

Cannon 是一種輕量級的 JavaScript 3D 物理引擎,用于實現虛擬世界中的物理模擬和交互。它提供了一套強大的功能,能夠處理剛體碰撞、力學模擬、約束系統等物理效果,使開發者能夠在 Web 應用程序和游戲中實現逼真的物理行為。

Cannon的官網:地址?,提供了一系列關于物理運動在three世界的實現,實現案例?的效果非常直觀,展示了物理運動的魅力,如下:

接下來我們在three.js的vue項目中使用Cannon,終端執行如下命令安裝,具體參考:官網

npm i cannon-es

接下來我們通過tsx風格語法撰寫three基礎項目實現:

import { defineComponent } from "vue";
import * as THREE from 'three'
import { OrbitControls }  from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import './index.scss'
import { div } from "three/examples/jsm/nodes/Nodes.js";export default defineComponent({setup() {// 初始化物理世界const world = new CANNON.World()// 初始化物理世界的重力world.gravity.set(0, -9.82, 0)// 初始化3D世界const scene = new THREE.Scene()// 初始化相機const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 3// 初始化渲染器const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)// 初始化控制器const controls = new OrbitControls(camera, renderer.domElement)controls.enableDamping = true// 渲染let clock = new THREE.Clock()const animate = () => {let delta = clock.getDelta()controls.update()renderer.render(scene, camera)requestAnimationFrame(animate)}animate()return () => {<div></div>}}
})

接下來我們需要給場景添加一些物體,如下:

// 創建一個物理球體,半徑為0.5
const sphereShape = new CANNON.Sphere(0.5)
// 創建一個剛體
const sphereBody = new CANNON.Body({mass: 1,shape: sphereShape,position: new CANNON.Vec3(0, 5, 0)
})
// 將剛體添加到物理世界中
world.addBody(sphereBody)
// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個球
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32) // 創建一個幾何體
const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }) // 創建一個球體材質
const shpereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial) // 創建一個球體網格
// 將網格添加到3D場景中
scene.add(shpereMesh)

打開控制臺,可以看到我們的球體已經顯示出來了:

接下來我們要把物理世界的球體給到我們three渲染出來的球體,讓兩者開始關聯起來。在每一幀中,根據物理引擎模擬的結果來更新 Three.js 場景中球體網格的位置和旋轉狀態,從而實現了基于物理引擎的球體模擬效果,如下:

得到的結果如下:

基礎碰撞使用

上面我們實現了一個物體的自由下落,接下來我們實現物體與平面的碰撞效果。如下添加平面:

// 創建一個物理世界的平面
const planeShape = new CANNON.Plane()
// 創建一個剛體
const planeBody = new CANNON.Body({mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0, 0)
})
// 設置剛體旋轉(設置旋轉X軸)
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
// 將剛體添加到物理世界當中
world.addBody(planeBody)
// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面
const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10
// 創建一個平面材質
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })
// 創建一個平面網格
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
// 旋轉平面90度讓其平鋪
planeMesh.rotation.x = -Math.PI / 2
// 將網格添加到3D場景當中
scene.add(planeMesh)

當然除了我們設置平面質量為0之外,我們也可以設置平面為靜態效果,也不受碰撞影響:

最終得到的效果如下:

那我們讓物理世界和渲染世界的平面傾斜度加上0.1,小球是否會滑落而掉下去呢?測試一下:

得到的效果如下,可見小球是不會掉落下去的,因為物理世界的平面是無限衍生的,即使渲染世界的平面有限,小球仍然會走物理世界的規律,如下:

如果我們想在物理世界有一個有限大的平面的話, 我們可以通過構建一個立方體,然后把立方體壓扁形成一個平面來使用,因為立方體已經有高度了,所以我們也不需要在旋轉90度了,稍微給點傾斜度0.1即可,代碼如下:

得到的效果如下,可見到我們的小球已經實現了掉落的效果:

上面兩標題的案例代碼如下:

import { defineComponent } from "vue";
import * as THREE from 'three'
import { OrbitControls }  from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import './index.scss'export default defineComponent({setup() {// 初始化物理世界const world = new CANNON.World()// 初始化物理世界的重力world.gravity.set(0, -9.82, 0)// 初始化3D世界const scene = new THREE.Scene()// 初始化相機const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 3// 初始化渲染器const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)// 初始化控制器const controls = new OrbitControls(camera, renderer.domElement)controls.enableDamping = true// 創建一個物理球體,半徑為0.5const sphereShape = new CANNON.Sphere(0.5)// 創建一個剛體const sphereBody = new CANNON.Body({mass: 1,shape: sphereShape,position: new CANNON.Vec3(0, 5, 0)})// 將剛體添加到物理世界中world.addBody(sphereBody)// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個球const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32) // 創建一個幾何體const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }) // 創建一個球體材質const shpereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial) // 創建一個球體網格// 將網格添加到3D場景中scene.add(shpereMesh)// 創建一個物理世界的平面// const planeShape = new CANNON.Plane()const planeShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5))// 創建一個剛體const planeBody = new CANNON.Body({// mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0, 0),type: CANNON.Body.STATIC // 設置物體為靜態,不受碰撞的影響})// 設置剛體旋轉(設置旋轉X軸)planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1)// 將剛體添加到物理世界當中world.addBody(planeBody)// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面// const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10const planeGeometry = new THREE.BoxGeometry(10, 0.2, 10)// 創建一個平面材質const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })// 創建一個平面網格const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)// 旋轉平面90度讓其平鋪planeMesh.rotation.x = 0.1// 將網格添加到3D場景當中scene.add(planeMesh)// 渲染let clock = new THREE.Clock()const animate = () => {// 獲取了兩次渲染之間的時間差,通常用于控制動畫和物理模擬。let delta = clock.getDelta()// 使用時間差來推進物理世界的模擬world.step(delta)// 更新球體網格的位置和旋轉// 將物理引擎中球體的位置賦值給 Three.js 中球體網格(shpereMesh)的位置,從而將物理模擬的結果更新到可視化場景中。shpereMesh.position.copy(sphereBody.position)// 將物理引擎中球體的旋轉狀態賦值給 Three.js 中球體網格(shpereMesh)的旋轉狀態,確保網格的旋轉與物理模擬一致。shpereMesh.quaternion.copy(sphereBody.quaternion)controls.update()renderer.render(scene, camera)requestAnimationFrame(animate)}animate()return () => {<div></div>}}
})

材質與摩擦系數設置

cannon的材質可以模擬我們現實生活當中的物理的效果,比如說我們可以設置它的摩擦系數,彈性系數來實現我們這個物體的滑動的有摩擦的效果。借助上面的案例,我們將球體換成立方體,因為要創建多個立方體,這里我們設置一個變量用于存儲

// 創建網格數組
let phyMeshes: any[] = [] // 物理世界
let meshes: any[] = [] // 渲染世界// 創建物理立方體
const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))
// 設置立方體的材質
const boxMaterialCon = new CANNON.Material("boxMaterial")
// 創建一個剛體
const boxBody = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(0, 15, 0),mass: 1,material: boxMaterialCon
})
// 將剛體添加到物理世界當中
world.addBody(boxBody)
phyMeshes.push(boxBody)
// 創建立方體幾何體
const boxGeometry = new THREE.BoxGeometry(1, 1, 1)
// 創建立方體材質
const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
// 創建立方體網格
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial)
// 將網格添加到3D場景當中
scene.add(boxMesh)
meshes.push(boxMesh)

在渲染函數處,我們變量數組來推進物理世界模擬:

最終得到是效果如下:

接下來我們添加第二個物體,將第二個物體的摩擦系數設置為0,第一個物體和平面的摩擦系數設置為0.7,代碼如下:

// 創建網格數組
let phyMeshes: any[] = [] // 物理世界
let meshes: any[] = [] // 渲染世界// 創建物理立方體
const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))
// 設置立方體的材質
const boxMaterialCon = new CANNON.Material("boxMaterial")
boxMaterialCon.friction = 0.7
// 創建一個剛體
const boxBody = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(0, 15, 0),mass: 1,material: boxMaterialCon
})
// 將剛體添加到物理世界當中
world.addBody(boxBody)
phyMeshes.push(boxBody)
// 創建立方體幾何體
const boxGeometry = new THREE.BoxGeometry(1, 1, 1)
// 創建立方體材質
const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
// 創建立方體網格
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial)
// 將網格添加到3D場景當中
scene.add(boxMesh)
meshes.push(boxMesh)// 創建第二個物理立方體(使用第一個物理立方體的內容,材質不同)
const boxSlipperyMaterial = new CANNON.Material("boxSlipperyMaterial")
boxSlipperyMaterial.friction = 0 // 摩擦系數為0
// 創建剛體
const boxBody2 = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(1, 5, 0), // 區別于第一個物體,位置改變一下mass: 1,material: boxSlipperyMaterial
})
// 將剛體添加到物理世界當中
world.addBody(boxBody2)
phyMeshes.push(boxBody2)
// 創建立方體幾何體(使用第一個物體的內容)
const boxMesh2 = new THREE.Mesh(boxGeometry, boxMaterial)
// 將網格添加到3D場景當中
scene.add(boxMesh2)
meshes.push(boxMesh2)// 創建一個物理世界的平面
// const planeShape = new CANNON.Plane()
const planeShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5))
// 創建一個剛體
const planeBody = new CANNON.Body({// mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0, 0),type: CANNON.Body.STATIC, // 設置物體為靜態,不受碰撞的影響material: boxMaterialCon
})
// 設置剛體旋轉(設置旋轉X軸)
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1)
// 將剛體添加到物理世界當中
world.addBody(planeBody)
// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面
// const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10
const planeGeometry = new THREE.BoxGeometry(10, 0.2, 10)
// 創建一個平面材質
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })
// 創建一個平面網格
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
// 旋轉平面90度讓其平鋪
planeMesh.rotation.x = 0.1
// 將網格添加到3D場景當中
scene.add(planeMesh)

最終得到的效果如下,可以看到我們設置的第二個物體因為很光滑,所以很容易就滑落下去:

彈性與接觸材質設置

上文我們介紹了摩擦效果的操作,接下來我們繼續開始講解物體的彈性操作,我們根據上文的代碼,再創建第三個立方體,然后給該立方體添加彈性系數

// 創建第三個物理立方體(使用第一個物理立方體的內容,材質不同)
const boxBouncyMaterial = new CANNON.Material("boxBouncyMaterial")
boxBouncyMaterial.friction = 0.1
boxBouncyMaterial.restitution = 1 // 設置彈性系數為1
// 創建剛體
const boxBody3 = new CANNON.Body({shape: boxShape,mass: 1,position: new CANNON.Vec3(2, 5, 3),material: boxBouncyMaterial
})
// 將剛體添加到物理世界當中
world.addBody(boxBody3)
phyMeshes.push(boxBody3)
// 創建幾何體(使用第一個立方體的內容以及材質)
const boxMesh3 = new THREE.Mesh(boxGeometry, boxMaterial) // 添加網格
// 將網格添加到3D場景當中
scene.add(boxMesh3)
meshes.push(boxMesh3)

給立方體設置彈性系數之后,如果我們想讓彈性效果奏效的話,我們也需要給平面網格設置相同的彈性系數,因為平面網格使用的材質是第一個立方體的材質,所以我們只要給第一個立方體設置彈性系數即可,如下:

最終得到的效果如下,可以看到設置高度高的物體,從高處下落反彈的效果是很直觀的:

當然我們也沒有必要單獨設置一下立方體和平面的彈性和摩擦系數,我們也可以通過接觸材質的系數設置兩個材質之間的一個彈性和摩擦系數,來實現相應的效果,如下:

// 創建第三個物理立方體(使用第一個物理立方體的內容,材質不同)
const boxBouncyMaterial = new CANNON.Material("boxBouncyMaterial")
// boxBouncyMaterial.friction = 0.1
// boxBouncyMaterial.restitution = 1 // 設置彈性系數為1
// 創建剛體
const boxBody3 = new CANNON.Body({shape: boxShape,mass: 1,position: new CANNON.Vec3(2, 5, 3),material: boxBouncyMaterial
})
// 將剛體添加到物理世界當中
world.addBody(boxBody3)
phyMeshes.push(boxBody3)
// 創建幾何體(使用第一個立方體的內容以及材質)
const boxMesh3 = new THREE.Mesh(boxGeometry, boxMaterial) // 添加網格
// 將網格添加到3D場景當中
scene.add(boxMesh3)
meshes.push(boxMesh3)
// 定義接觸材質
const material3toplane = new CANNON.ContactMaterial(boxMaterialCon,boxBouncyMaterial,{friction: 0,restitution:  1}
)
// 將接觸材質添加到物理世界當中
world.addContactMaterial(material3toplane)

最終呈現的效果依然很明顯:

碰撞與碰撞組

Cannon中的碰撞指的是游戲開發中物體之間的相互作用,通常包括物體之間的碰撞檢測和碰撞響應兩個部分。碰撞檢測用于判斷物體是否發生了碰撞,而碰撞響應則是在發生碰撞時對物體進行相應的處理,比如改變物體的速度、方向等。如下我們設置代碼來實現:

依次創建立方體、球體、圓柱體到場景當中,舉例代碼如下:

接下來我們給創建到場景的立方體添加一個初速度使其運動來碰撞另外兩個物體,如下:

這里給出完整的代碼來給大家進行學習:

import { defineComponent } from "vue";
import * as THREE from 'three'
import { OrbitControls }  from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import './index.scss'export default defineComponent({setup() {// 初始化物理世界const world = new CANNON.World()// 初始化物理世界的重力world.gravity.set(0, -9.82, 0)// 初始化3D世界const scene = new THREE.Scene()// 初始化相機const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 8camera.position.y = 5camera.position.x = 2// 初始化渲染器const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)// 初始化控制器const controls = new OrbitControls(camera, renderer.domElement)controls.enableDamping = true// 創建網格數組let phyMeshes: any[] = [] // 物理世界let meshes: any[] = [] // 渲染世界// 創建物理立方體const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))// 設置立方體的材質const boxMaterialCon = new CANNON.Material("boxMaterial")boxMaterialCon.friction = 0// 創建一個剛體const boxBody = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(2, 0.8, 0),mass: 1,material: boxMaterialCon})// 將剛體添加到物理世界當中world.addBody(boxBody)phyMeshes.push(boxBody)// 創建立方體幾何體const boxGeometry = new THREE.BoxGeometry(1, 1, 1)// 創建立方體材質const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })// 創建立方體網格const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial)// 將網格添加到3D場景當中scene.add(boxMesh)meshes.push(boxMesh)// 創建物理球const spereShape = new CANNON.Sphere(0.5)// 創建一個剛體const sphereBody = new CANNON.Body({shape: spereShape,position: new CANNON.Vec3(0, 0.8, 0),mass: 1,material: boxMaterialCon})// 將剛體添加到物理世界當中world.addBody(sphereBody)phyMeshes.push(sphereBody)// 創建球的幾何體const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32)// 創建球的材質const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff })// 創建球網格const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial)// 將網格添加到3D場景當中scene.add(sphereMesh)meshes.push(sphereMesh)// 創建物理圓柱體const cylinderShape = new CANNON.Cylinder(0.5, 0.5, 1, 32)// 創建一個剛體const cylinderBody = new CANNON.Body({shape: cylinderShape,position: new CANNON.Vec3(-2, 0.8, 0),mass: 1,material: boxMaterialCon})// 將剛體添加到物理世界當中world.addBody(cylinderBody)phyMeshes.push(cylinderBody)// 創建圓柱體幾何體const cylinderGeometry = new THREE.CylinderGeometry(0.5 ,0.5, 1, 32)// 創建圓柱體材質const cylinderMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 })// 創建圓柱體網格const cylinderMesh = new THREE.Mesh(cylinderGeometry, cylinderMaterial)// 將網格添加到3D場景當中scene.add(cylinderMesh)meshes.push(cylinderMesh)// 創建一個物理世界的平面// const planeShape = new CANNON.Plane()const planeShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5))// 創建一個剛體const planeBody = new CANNON.Body({// mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0, 0),type: CANNON.Body.STATIC, // 設置物體為靜態,不受碰撞的影響material: boxMaterialCon})// 設置剛體旋轉(設置旋轉X軸)// planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1)// 將剛體添加到物理世界當中world.addBody(planeBody)// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面// const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10const planeGeometry = new THREE.BoxGeometry(10, 0.2, 10)// 創建一個平面材質const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })// 創建一個平面網格const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)// 旋轉平面90度讓其平鋪// planeMesh.rotation.x = 0.1// 將網格添加到3D場景當中scene.add(planeMesh)// 設置立方體的初始速度boxBody.velocity.set(-2, 0, 0)// 渲染let clock = new THREE.Clock()const animate = () => {// 獲取了兩次渲染之間的時間差,通常用于控制動畫和物理模擬。let delta = clock.getDelta()world.step(delta)// 使用時間差來推進物理世界的模擬for(let i = 0; i < phyMeshes.length; i++) {meshes[i].position.copy(phyMeshes[i].position)meshes[i].quaternion.copy(phyMeshes[i].quaternion)}controls.update()renderer.render(scene, camera)requestAnimationFrame(animate)}animate()return () => {<div></div>}}
})

接下來實現碰撞組,碰撞組是為了更高效地管理和處理碰撞而引入的概念。通過將具有相似碰撞特性的物體分組,可以在碰撞檢測和碰撞響應時只考慮同一組內的物體之間的碰撞,從而減少不必要的計算量,提高游戲的性能和效率。代碼如下:

我們設置立方體為組1,然后碰撞掩碼就是能夠和誰發生碰撞,我們設置立方體可以和所有物體碰撞:

在球體的分組當中,我們設置碰撞掩碼如下,可以看到我們的球不能碰撞圓柱體:

最終呈現的效果如下:

給出案例的完整代碼供大家學習:

import { defineComponent } from "vue";
import * as THREE from 'three'
import { OrbitControls }  from 'three/examples/jsm/controls/OrbitControls.js'
import * as CANNON from 'cannon-es'
import './index.scss'export default defineComponent({setup() {// 初始化物理世界const world = new CANNON.World()// 初始化物理世界的重力world.gravity.set(0, -9.82, 0)// 初始化3D世界const scene = new THREE.Scene()// 初始化相機const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 8camera.position.y = 5camera.position.x = 2// 初始化渲染器const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)// 初始化控制器const controls = new OrbitControls(camera, renderer.domElement)controls.enableDamping = true// 創建網格數組let phyMeshes: any[] = [] // 物理世界let meshes: any[] = [] // 渲染世界// 設置碰撞組,數值要用2的冪const GROUP1 = 1 // 分組立方體const GROUP2 = 2 // 分組球體const GROUP3 = 4 // 分組圓柱體const GROUP4 = 8 // 分組平面// 創建物理立方體const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5))// 設置立方體的材質const boxMaterialCon = new CANNON.Material("boxMaterial")boxMaterialCon.friction = 0// 創建一個剛體const boxBody = new CANNON.Body({shape: boxShape,position: new CANNON.Vec3(2, 0.8, 0),mass: 1,material: boxMaterialCon,collisionFilterGroup: GROUP1, // 設置碰撞組collisionFilterMask: GROUP2 | GROUP3 | GROUP4, // 碰撞掩碼,可以和二組和三、四組碰撞})// 將剛體添加到物理世界當中world.addBody(boxBody)phyMeshes.push(boxBody)// 創建立方體幾何體const boxGeometry = new THREE.BoxGeometry(1, 1, 1)// 創建立方體材質const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 })// 創建立方體網格const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial)// 將網格添加到3D場景當中scene.add(boxMesh)meshes.push(boxMesh)// 創建物理球const spereShape = new CANNON.Sphere(0.5)// 創建一個剛體const sphereBody = new CANNON.Body({shape: spereShape,position: new CANNON.Vec3(0, 0.8, 0),mass: 1,material: boxMaterialCon,collisionFilterGroup: GROUP2, // 設置碰撞組collisionFilterMask: GROUP1 | GROUP4, // 碰撞掩碼,可以和一、四組碰撞})// 將剛體添加到物理世界當中world.addBody(sphereBody)phyMeshes.push(sphereBody)// 創建球的幾何體const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32)// 創建球的材質const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff })// 創建球網格const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial)// 將網格添加到3D場景當中scene.add(sphereMesh)meshes.push(sphereMesh)// 創建物理圓柱體const cylinderShape = new CANNON.Cylinder(0.5, 0.5, 1, 32)// 創建一個剛體const cylinderBody = new CANNON.Body({shape: cylinderShape,position: new CANNON.Vec3(-2, 0.8, 0),mass: 1,material: boxMaterialCon,collisionFilterGroup: GROUP3, // 設置碰撞組collisionFilterMask: GROUP1 | GROUP4, // 碰撞掩碼,可以和一、四組碰撞})// 將剛體添加到物理世界當中world.addBody(cylinderBody)phyMeshes.push(cylinderBody)// 創建圓柱體幾何體const cylinderGeometry = new THREE.CylinderGeometry(0.5 ,0.5, 1, 32)// 創建圓柱體材質const cylinderMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 })// 創建圓柱體網格const cylinderMesh = new THREE.Mesh(cylinderGeometry, cylinderMaterial)// 將網格添加到3D場景當中scene.add(cylinderMesh)meshes.push(cylinderMesh)// 創建一個物理世界的平面// const planeShape = new CANNON.Plane()const planeShape = new CANNON.Box(new CANNON.Vec3(5, 0.1, 5))// 創建一個剛體const planeBody = new CANNON.Body({// mass: 0, // 設置質量為0,不受碰撞的影響shape: planeShape,position: new CANNON.Vec3(0, 0.1, 0),type: CANNON.Body.STATIC, // 設置物體為靜態,不受碰撞的影響material: boxMaterialCon,collisionFilterGroup: GROUP4, // 設置碰撞組collisionFilterMask: GROUP1 | GROUP2 | GROUP3, // 碰撞掩碼,可以和一組、二組和三組碰撞})// 設置剛體旋轉(設置旋轉X軸)// planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), 0.1)// 將剛體添加到物理世界當中world.addBody(planeBody)// 物理世界創建的東西不顯示,所以我們要再通過three.js再創建一個平面// const planeGeometry = new THREE.PlaneGeometry(10, 10) // 因為渲染的東西不是無限衍生,這里給10x10const planeGeometry = new THREE.BoxGeometry(10, 0.2, 10)// 創建一個平面材質const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 })// 創建一個平面網格const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)// 旋轉平面90度讓其平鋪// planeMesh.rotation.x = 0.1// 將網格添加到3D場景當中scene.add(planeMesh)// 設置立方體的初始速度boxBody.velocity.set(-2, 0, 0)// 渲染let clock = new THREE.Clock()const animate = () => {// 獲取了兩次渲染之間的時間差,通常用于控制動畫和物理模擬。let delta = clock.getDelta()world.step(delta)// 使用時間差來推進物理世界的模擬for(let i = 0; i < phyMeshes.length; i++) {meshes[i].position.copy(phyMeshes[i].position)meshes[i].quaternion.copy(phyMeshes[i].quaternion)}controls.update()renderer.render(scene, camera)requestAnimationFrame(animate)}animate()return () => {<div></div>}}
})

本篇文章對canon的學習暫時結束,下篇文章將對canon更加深入講解!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/719732.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/719732.shtml
英文地址,請注明出處:http://en.pswp.cn/news/719732.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

YOLOv8姿態估計實戰:訓練自己的數據集

課程鏈接&#xff1a;https://edu.csdn.net/course/detail/39355 YOLOv8 基于先前 YOLO 版本的成功&#xff0c;引入了新功能和改進&#xff0c;進一步提升性能和靈活性。YOLOv8 同時支持目標檢測和姿態估計任務。 本課程以熊貓姿態估計為例&#xff0c;將手把手地教大家使用C…

Mysql實戰(2)之MySQL執行流程

-- 查看mysql當前有多少連接 show global status like Thread%; /* Threads_cached&#xff1a;緩存中的線程連接數 Threads_connected&#xff1a;當前打開的連接數 Threads_created&#xff1a;為處理連接創建的線程數 Threads_running&#xff1a;非睡眠狀態的連接數&…

windows部署mariadb-11.3

因為需要用到數據庫來處理一些東西,所以決定在windows上安裝一下MariaDB. 隨著版本升級,安裝已經不是那么復雜了.對應的.其實網上一大堆的檢索結果,很多并不可用. 由于是開發環境,這里一切從簡了. 下載安裝包.并解壓進入bin目錄,使用mysql_install_db.exe程序來進行安裝.執行 m…

MSCKF5講:后端代碼分析

MSCKF5講&#xff1a;后端代碼分析 文章目錄 MSCKF5講&#xff1a;后端代碼分析1 初始化initialize()1.1 加載參數1.2 初始化IMU連續噪聲協方差矩陣1.3 卡方檢驗1.4 接收與訂閱話題createRosIO() 2 IMU靜止初始化3 重置resetCallback()4 featureCallback4.1 IMU初始化判斷4.2 I…

【文末送書】智能計算:原理與實踐

歡迎關注博主 Mindtechnist 或加入【智能科技社區】一起學習和分享Linux、C、C、Python、Matlab&#xff0c;機器人運動控制、多機器人協作&#xff0c;智能優化算法&#xff0c;濾波估計、多傳感器信息融合&#xff0c;機器學習&#xff0c;人工智能等相關領域的知識和技術。關…

Linux系統運維腳本:一鍵添加防火墻規則(開啟服務和網絡端口)

目 錄 一、要求 二、解決方案 &#xff08;一&#xff09;解決思路 &#xff08;二&#xff09;方案 三、腳本程序實現 &#xff08;一&#xff09;腳本代碼和解釋 1、腳本代碼 2、代碼解釋 &#xff08;二&#xff09;腳本驗證 1、腳本編輯 2、給予執行權限…

NumPy數據處理詳解的筆記2

NumPy數據處理詳解的筆記2 第1章NumPy基礎 NumPy是用于處理多維數組的數值運算庫&#xff0c;不僅可用于 機器學習&#xff0c;還可以用于圖像處理&#xff0c;語言處理等任務。 1.2 多維數據結構ndarray的基礎 在學習NumPy的過程中&#xff0c;只要理解了ndarray的相關知識…

java 關于 Object 類中的 wait 和 notify 方法。(生產者和消費者模式!)

4、關于 Object 類中的 wait 和 notify 方法。&#xff08;生產者和消費者模式&#xff01;&#xff09; 第一&#xff1a;wait 和 notify 方法不是線程對象的方法&#xff0c;是 java 中任何一個 java 對象都有的方法&#xff0c;因為這兩個方法是 Object 類中自帶的。 wait 方…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的停車位檢測系統(Python+PySide6界面+訓練代碼)

摘要&#xff1a;開發停車位檢測系統對于優化停車資源管理和提升用戶體驗至關重要。本篇博客詳細介紹了如何利用深度學習構建一個停車位檢測系統&#xff0c;并提供了完整的實現代碼。該系統基于強大的YOLOv8算法&#xff0c;并結合了YOLOv7、YOLOv6、YOLOv5的性能對比&#xf…

HarmonyOS端云體化開發—創建端云一體化開發工程

云開發工程模板 DevEco Studio目前提供了兩種云開發工程模板&#xff1a;通用云開發模板和商城模板。您可根據工程向導輕松創建端云一體化開發工程&#xff0c;并自動生成對應的代碼和資源模板。在創建端云一體化開發工程前&#xff0c;請提前了解云開發工程模板的相關信息。 …

前端學習之HTML(第一天)

什么是HTML HTML是一種用來描述網頁的一種語言&#xff0c;HTML不是一種編程語言&#xff0c;而是一種標記語言。 HTML標簽 HTML 標簽是由尖括號包圍的關鍵詞&#xff0c;比如 <html> HTML 標簽通常是成對出現的&#xff0c;比如 <b> 和 </b> 標簽對中的…

ROS 2基礎概念#3:主題(Topic)| ROS 2學習筆記

在ROS&#xff08;Robot Operating System&#xff09;中&#xff0c;主題&#xff08;Topics&#xff09;是實現節點之間通信的主要機制之一。節點&#xff08;Node&#xff09;可以發布&#xff08;publish&#xff09;消息到話題&#xff0c;或者訂閱&#xff08;subscribe&…

市場復盤總結 20240304

僅用于記錄當天的市場情況&#xff0c;用于統計交易策略的適用情況&#xff0c;以便程序回測 短線核心&#xff1a;不參與任何級別的調整&#xff0c;采用龍空龍模式 一支股票 10%的時候可以操作&#xff0c; 90%的時間適合空倉等待 二進三&#xff1a; 進級率中 20% 最常用的…

格兩例12345

osu/Lucky Roll gaming 周末osu有道題&#xff1a;lcg已知低位 def lcg(s, a, b, p):return (a * s b) % pp getPrime(floor(72.7)) a randrange(0, p) b randrange(0, p) seed randrange(0, p) print(f"{p }") print(f"{a }") print(f"{b …

冪等性設計

目錄 前言 冪等性設計 冪等性設計處理流程 HTTP 冪等性 消息隊列冪等性 基于kafka 前言 冪等性設計&#xff0c;就是說&#xff0c;一次和多次請求某一個資源應該具有同樣的副作用。為什么我們要有冪等性操作&#xff1f;說白了&#xff0c;就兩點&#xff1a;1、網絡的…

LeetCode第125場雙周賽個人題解

目錄 100231. 超過閾值的最少操作數 I 原題鏈接 思路分析 AC代碼 100232. 超過閾值的最少操作數 II 原題鏈接 思路分析 AC代碼 100226. 在帶權樹網絡中統計可連接服務器對數目 原題鏈接 思路分析 AC代碼 100210. 最大節點價值之和 原題鏈接 思路分析 AC代碼 10023…

大話C++之:對象內存模型

一般繼承(無虛函數覆蓋) 只有一個虛指針&#xff0c;指向一個虛表&#xff0c;虛函數按順序從祖先節點開始插入到虛表上。字段按順序從祖先節點開始插入到對象內存上 一般繼承(有虛函數覆蓋) 只有一個虛指針&#xff0c;指向一個虛表&#xff0c;虛函數按順序從祖先節點開始&a…

桂院校園導航 靜態項目 二次開發教程 2.0

Gitee代碼倉庫&#xff1a;桂院校園導航小程序 GitHub代碼倉庫&#xff1a;GLU-Campus-Guide 靜態項目 2.0版本 升級日志 序號 板塊 詳情 1 首頁 重做了首頁&#xff0c;界面更加高效和美觀 2 校園頁 新增了 “校園指南” 功能&#xff0c;可以搜索和瀏覽校園生活指南…

【金三銀四】每日一點面試題(Java--JVM篇)

1、說一下 JVM 的主要組成部分及其作用&#xff1f; JVM&#xff08;Java虛擬機&#xff09;是Java程序運行的核心組件&#xff0c;它負責將Java字節碼翻譯成底層操作系統能夠執行的指令。JVM由以下幾個主要組成部分構成&#xff1a; 類加載器&#xff08;Class Loader&#…

Modbus協議詳細解析與案例分享

目錄 Modbus協議詳細解析與案例分享1. Modbus協議簡介2. Modbus RTU和Modbus TCP的區別3. Modbus幀結構4. Modbus協議示例5. Modbus應用場景結語 Modbus協議詳細解析與案例分享 1. Modbus協議簡介 Modbus是一種串行通信協議&#xff0c;用于實現工業自動化設備之間的通信。它…