Three.js--》探寻Cannon.js构建震撼的3D物理交互体验(一)

2024-03-05 00:52

本文主要是介绍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更加深入讲解!!!

这篇关于Three.js--》探寻Cannon.js构建震撼的3D物理交互体验(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/774809

相关文章

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

深入浅出SpringBoot WebSocket构建实时应用全面指南

《深入浅出SpringBootWebSocket构建实时应用全面指南》WebSocket是一种在单个TCP连接上进行全双工通信的协议,这篇文章主要为大家详细介绍了SpringBoot如何集成WebS... 目录前言为什么需要 WebSocketWebSocket 是什么Spring Boot 如何简化 We

Spring Boot Maven 插件如何构建可执行 JAR 的核心配置

《SpringBootMaven插件如何构建可执行JAR的核心配置》SpringBoot核心Maven插件,用于生成可执行JAR/WAR,内置服务器简化部署,支持热部署、多环境配置及依赖管理... 目录前言一、插件的核心功能与目标1.1 插件的定位1.2 插件的 Goals(目标)1.3 插件定位1.4 核

使用Python构建一个高效的日志处理系统

《使用Python构建一个高效的日志处理系统》这篇文章主要为大家详细讲解了如何使用Python开发一个专业的日志分析工具,能够自动化处理、分析和可视化各类日志文件,大幅提升运维效率,需要的可以了解下... 目录环境准备工具功能概述完整代码实现代码深度解析1. 类设计与初始化2. 日志解析核心逻辑3. 文件处

使用Docker构建Python Flask程序的详细教程

《使用Docker构建PythonFlask程序的详细教程》在当今的软件开发领域,容器化技术正变得越来越流行,而Docker无疑是其中的佼佼者,本文我们就来聊聊如何使用Docker构建一个简单的Py... 目录引言一、准备工作二、创建 Flask 应用程序三、创建 dockerfile四、构建 Docker

使用Python绘制3D堆叠条形图全解析

《使用Python绘制3D堆叠条形图全解析》在数据可视化的工具箱里,3D图表总能带来眼前一亮的效果,本文就来和大家聊聊如何使用Python实现绘制3D堆叠条形图,感兴趣的小伙伴可以了解下... 目录为什么选择 3D 堆叠条形图代码实现:从数据到 3D 世界的搭建核心代码逐行解析细节优化应用场景:3D 堆叠图

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名

全屋WiFi 7无死角! 华硕 RP-BE58无线信号放大器体验测评

《全屋WiFi7无死角!华硕RP-BE58无线信号放大器体验测评》家里网络总是有很多死角没有网,我决定入手一台支持Mesh组网的WiFi7路由系统以彻底解决网络覆盖问题,最终选择了一款功能非常... 自2023年WiFi 7技术标准(IEEE 802.11be)正式落地以来,这项第七代无线网络技术就以超高速

基于Python构建一个高效词汇表

《基于Python构建一个高效词汇表》在自然语言处理(NLP)领域,构建高效的词汇表是文本预处理的关键步骤,本文将解析一个使用Python实现的n-gram词频统计工具,感兴趣的可以了解下... 目录一、项目背景与目标1.1 技术需求1.2 核心技术栈二、核心代码解析2.1 数据处理函数2.2 数据处理流程

Python FastMCP构建MCP服务端与客户端的详细步骤

《PythonFastMCP构建MCP服务端与客户端的详细步骤》MCP(Multi-ClientProtocol)是一种用于构建可扩展服务的通信协议框架,本文将使用FastMCP搭建一个支持St... 目录简介环境准备服务端实现(server.py)客户端实现(client.py)运行效果扩展方向常见问题结