Three.js 结合 cannon-es 实现物理引擎基础示例

作者:资源驿站

时间:2026-10-31

浏览:266

本文详解如何在Three.js项目中集成cannon-es物理引擎。内容涵盖安装引入、创建物理世界与刚体、设置重力加速度及约束系统,并通过球体自由落体与地面碰撞的完整代码示例,展示如何在渲染循环中同步物理数据与3D视觉表现,实现浏览器端真实的3D物理模拟效果。

文章目录

Cannon.js 是一款轻量级且高性能的物理引擎,其代码结构简洁,易于理解和二次开发。

它具备轻量级与高性能特性,专为快速运算和轻量级应用设计,源码清晰直观。

该引擎提供完整的3D物理模拟能力,涵盖刚体碰撞检测、力学计算及约束求解,能够还原真实世界的物理交互效果。

其约束系统具有高度灵活性,支持距离约束、弹簧约束、旋转约束等多种类型,开发者可根据具体需求定制复杂的物理连接。

Cannon.js 基于 WebGL 技术构建,能够无缝集成到各类 WebGL 应用中,轻松在浏览器环境中呈现复杂的3D物理交互效果。

该引擎具备良好的跨平台兼容性,支持主流桌面浏览器及移动设备,确保物理模拟应用在不同终端上稳定运行。

cannon-es 的安装与引入流程

首先,通过 npm 命令行工具安装 cannon-es 模块,将其添加至项目依赖中。

npm install --save cannon-es

在 HTML 和 JavaScript 文件中分别引入 cannon-es、three 以及 three/jsm 模块,其中 three/jsm 用于引入 Three.js 的特定实例组件。

<script type="importmap">
    {
      "imports": {
        "three": "../js/three.module.js",
        "three/jsm/": "../js/jsm/",
        "cannon-es": "../node_modules/cannon-es/dist/cannon-es.js"
      }
    }
</script>

在 JavaScript 代码中,通过全局引入方式加载 cannon-es 模块,以便在后续逻辑中调用其物理引擎功能。

import * as Cannon from 'cannon-es'

初始工作:3D环境搭建与物体准备

首先构建3D场景并引入必要的3D实例,为后续物理模拟搭建基础环境。

import * as THREE from 'three'
import * as Cannon from 'cannon-es'
import { OrbitControls } from '../../js/jsm/controls/OrbitControls.js'
// 目标:物理引擎 common-es 使用
// 1. 下载引入 cannon-es 包
// 2. 准备球体,地面,灯光和阴影
// 3. 基于 cannon 创建物理世界,创建物理世界的小球(图形+材质)
// 4. 在渲染循环中更新物理世界内的球体运动轨迹,并影响到 3D 物体(核心思想)
let scene, camera, renderer, controls, ball, floor, world, sphereBody
function init() {
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
    camera.position.set(0, 0, 30)
    renderer = new THREE.WebGLRenderer({ antialias: true })
    renderer.setSize(window.innerWidth, window.innerHeight)
    document.querySelector('#app').appendChild(renderer.domElement)
    renderer.shadowMap.enabled = true
    controls = new OrbitControls(camera, renderer.domElement)
    const axesHelper = new THREE.AxesHelper(5)
    scene.add(axesHelper)
}

第一步是准备球体模型。在3D场景中,网格模型(Mesh)需通过几何体(Geometry)定义外形;同理,在物理世界中,你需要设置物体Body的几何形状。CannonJS 提供了多种定义几何体形状的API,例如长方体Box、球体Sphere等,开发者可根据需求选择合适的形状。

由于场景中已存在视觉球体,接下来需在 Cannon.js 的 world 中创建对应的物理球体。这需要使用 Body 类,它支持自由落体运动及与其他 Body 的碰撞检测。在创建 Body 之前,必须先定义一个 Shape(形状对象)。

//创建屋里小球
const sphereShape = new Cannon.Sphere(1)
// 创建物理小球材质
const sphereMaterial = new Cannon.Material();

第二步,通过 Cannon.World 类创建物理世界,并定义其物理属性,例如设置重力加速度。重力加速度的属性gravity类似于 Body 的位置,是一个三维向量Vec3。该向量的 x、y、z 分量值需根据项目坐标系设定;假设场景中 y 轴竖直向上,则重力加速度应设置为 y 轴负方向。


world = new Cannon.World({
   // 自由落体加速度
   gravity: new Cannon.Vec3(0, -9.8, 0)
})

第三步,使用方法.addBody()将物体添加到物理世界。将物理球体 Body 加入 World 后,该 Body 将直接受物理世界加速度(如重力)的影响。

// 创建物理世界
    sphereBody = new Cannon.Body({
        shape: sphereShape,  // 物体形状
        material: sphereMaterial, // 物位材质
        position: new Cannon.Vec3(0,0,0,),
        mass: 1
    })
    // 把物理小球加入到物理世界
    world.addBody(sphereBody)

第四步,创建一个地面模型,作为球体自由落体的参照物,用于接收碰撞效果。

floor = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), new THREE.MeshStandardMaterial({color: 0xffff00,}));
    floor.position.set(0, -10, 0);
    floor.rotation.set(-Math.PI/2, 0, 0);
    floor.receiveShadow = true;

第五步,所有3D实例和物体都需要光照支持,否则无法在视口中显示。因此,需要添加环境光和定向灯光以照亮场景。

//环境光
const ambientLingth = new THREE.AmbientLight(0xffffff, 1)
//灯光
const directionLight = new THREE.DirectionalLight(0xffffff, 1)
directionLight.position.set(0, 15, 0)
directionLight.castShadow = true

第六步,将球体、地面、环境光及定向灯光全部添加到场景中,完成视觉资源的整合。

scene.add(ball)
scene.add(floor)
scene.add(ambientLingth)
scene.add(directionLight)

第七步,在渲染循环中持续更新物理世界内球体的运动轨迹,并将物理数据同步给3D物体,这是实现物理与视觉同步的核心思想。

const clock = new THREE.Clock()
function renderLoop() {
    // 4. 在渲染循环中更新物理世界内的球体运动轨迹,并影响到 3D 物体(核心思想)
    const time = clock.getDelta()
    // 物理世界不断渲染
    // 参数1:固定步长,间隔多久重绘物理世界
    // 参数2:自上次调用后要经过多长时间
    world.step(1 / 160, time)
    // 把物理世界小球最新位置更新给 3D 物体小球位置
    ball.position.copy(sphereBody.position)
    renderer.render(scene, camera)
    requestAnimationFrame(renderLoop)
}

完整代码和效果图展示

Cannon.js 是一款轻量级且高性能的物理引擎,其代码结构简洁,易于理解和二次开发。

import * as THREE from 'three'
import * as Cannon from 'cannon-es'
import { OrbitControls } from '../../js/jsm/controls/OrbitControls.js'
// 目标:物理引擎 common-es 使用
// 1. 下载引入 cannon-es 包
// 2. 准备球体,地面,灯光和阴影
// 3. 基于 cannon 创建物理世界,创建物理世界的小球(图形+材质)
// 4. 在渲染循环中更新物理世界内的球体运动轨迹,并影响到 3D 物体(核心思想)
let scene, camera, renderer, controls, ball, floor, world, sphereBody
function init() {
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
    camera.position.set(0, 0, 30)
    renderer = new THREE.WebGLRenderer({ antialias: true })
    renderer.setSize(window.innerWidth, window.innerHeight)
    document.querySelector('#app').appendChild(renderer.domElement)
    renderer.shadowMap.enabled = true
    controls = new OrbitControls(camera, renderer.domElement)
    const axesHelper = new THREE.AxesHelper(5)
    scene.add(axesHelper)
}
const clock = new THREE.Clock()
function renderLoop() {
    // 4. 在渲染循环中更新物理世界内的球体运动轨迹,并影响到 3D 物体(核心思想)
    const time = clock.getDelta()
    // 物理世界不断渲染
    // 参数1:固定步长,间隔多久重绘物理世界
    // 参数2:自上次调用后要经过多长时间
    world.step(1 / 160, time)
    // 把物理世界小球最新位置更新给 3D 物体小球位置
    ball.position.copy(sphereBody.position)
    renderer.render(scene, camera)
    requestAnimationFrame(renderLoop)
}
// 2. 准备球体,地面,灯光和阴影
function createBase () {
    // 球体
    ball = new THREE.Mesh(new THREE.SphereGeometry(1,32, 16), new THREE.MeshStandardMaterial({color: 0xffff00,}));
    ball.castShadow = true
    // 地面
    floor = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), new THREE.MeshStandardMaterial({color: 0xffff00,}));
    floor.position.set(0, -10, 0);
    floor.rotation.set(-Math.PI/2, 0, 0);
    floor.receiveShadow = true;
    //环境光
    const ambientLingth = new THREE.AmbientLight(0xffffff, 1)
    //灯光
    const directionLight = new THREE.DirectionalLight(0xffffff, 1)
    directionLight.position.set(0, 15, 0)
    directionLight.castShadow = true
    scene.add(ball)
    scene.add(floor)
    scene.add(ambientLingth)
    scene.add(directionLight)
}
// 3. 基于 cannon 创建物理世界
function createWorld () {
    world = new Cannon.World({
        // 自由落体加速度
        gravity: new Cannon.Vec3(0, -9.8, 0)
    })
    //创建屋里小球
    const sphereShape = new Cannon.Sphere(1)
    // 创建物理小球材质
    const sphereMaterial = new Cannon.Material();
    // 创建物理世界
    sphereBody = new Cannon.Body({
        shape: sphereShape,  // 物体形状
        material: sphereMaterial, // 物位材质
        position: new Cannon.Vec3(0,0,0,),
        mass: 1
    })
    // 把物理小球加入到物理世界
    world.addBody(sphereBody)
}
init()
createBase()
createWorld()
setTimeout(() => {
    renderLoop()
},1000)

至此,关于 Three.js 结合 cannon-es 基础使用的教程介绍完毕。如需了解更多相关使用技巧,请查阅本站往期文章或继续浏览后续内容,感谢支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章