Cannon.js -- 3d物理引擎

这篇具有很好参考价值的文章主要介绍了Cannon.js -- 3d物理引擎。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本篇将介绍Cannon.js -- 3d物理引擎的基础使用,并用Cannon.jsthree.js写一个简单的demo


一、关于Cannon.js

  • Q:什么是Cannon.js?
    A:Cannon.js是一个3d物理引擎,它能实现常见的碰撞检测,各种体形,接触,摩擦和约束功能。
  • Q:为什么要使用Cannon.js?
    A:1.比许多移植的物理引擎轻量级、更小的文件大小。2.100% 开源 JavaScript,从头开始编写。3使用迭代 Gauss-Seidel 求解器来求解约束。4使用SPOOK步进器…

二、Cannon.js的使用

Cannon.js的使用非常简单,只需要像其它js一样简单的引入或者用es模块的方式引用即可
在使用Cannon.js时通常会与其它3d库同时使用,因为Cannon.js就和后端差不多只负责数据 3d库则负责展示效果。我们这里将使用three.js来进行演示
首先我们需要下载Cannon.js 我们可以直接下载js文件或者使用CDN也可以使用npm直接安装

  • 直接下载js
  • 使用CDN:html中引用unpkg cannon-es地址
  • 使用npm:npm install cannon-es

相关示例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/public/normalize.css">
    <title>Document</title>
</head>

<body>

    <script type="module">
        // 引入cannon-es
        import * as CANNON from './lib/cannon-es.js'

        // cannon.js variables
        let world
        // cannon.js 刚体 variables
        let sphereBody


        initCannon()
        animate()

        // document.addEventListener('keydown',e=>{
        //     if(e.code=='Space') animate()
        // })


        function initCannon() {
            world = new CANNON.World() //初始化一个CANNON对象
            // 设置CANNON的引力  相当与地球的引力(您可以x轴也可以设置y轴或者z轴 负数则会向下掉,正数则向上)
            world.gravity.set(0, -9.82, 0)

            /**
             * 定义项目需要用到的材质
             */
             const concreteMaterial = new CANNON.Material('concrete') //创建混凝土材质
             const plasticMaterial = new CANNON.Material('plastic') //创建塑料材质

            /**
             *创建刚体,刚体就相当于现实生活中的物体(实体)一样 例如:桌子、板凳、大树、乒乓球等 
             */
            // 
            sphereBody = new CANNON.Body({
                mass: 10,//质量
                position: new CANNON.Vec3(0, 15, 0),//位置
                //刚体的形状。 CANNON.Sphere为圆球体  CANNON.Box为立方体 更多形状查看文档:https://pmndrs.github.io/cannon-es/docs/classes/Shape.html
                shape: new CANNON.Sphere(2),
                //材质  材质决定了物体(刚体)的弹力和摩擦力,默认为null,无弹力无摩擦。 plastic为塑料材质  concrete为混泥土材质。相关文档地址:https://pmndrs.github.io/cannon-es/docs/classes/Material.html
                material: plasticMaterial,
            })
            //添加外力,这有点类似于风力一样,在某个位置向某物吹风
            // 该方法接收两个参数:force:力的大小(Vec3)    relativePoint:相对于质心施加力的点(Vec3)。
            sphereBody.applyForce(new CANNON.Vec3(100, 0, 0), sphereBody.position)

            world.addBody(sphereBody) //向world中添加刚体信息


            /**
             * 创建地板刚体
             */
            const floorBody = new CANNON.Body()
            floorBody.mass = 0//质量  质量为0时表示该物体是一个固定的物体即不可破坏
            floorBody.addShape(new CANNON.Plane())//设置刚体的形状为CANNON.Plane 地面形状
            floorBody.material = concreteMaterial//设置材质
            // 由于平面初始化是是竖立着的,所以需要将其旋转至跟现实中的地板一样 横着
            // 在cannon.js中,我们只能使用四元数(Quaternion)来旋转,可以通过setFromAxisAngle(…)方法,第一个参数是旋转轴,第二个参数是角度
            floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(-1, 0, 0), Math.PI * 0.5)
            world.addBody(floorBody)

            /**
             * 设置两种材质相交时的效果  相当于设置两种材质碰撞时应该展示什么样的效果 例如:篮球在地板上反弹
             */
            //创建一个接触材质
            const concretePlasticMaterial = new CANNON.ContactMaterial(
                concreteMaterial,//材质1
                plasticMaterial,//材质2
                {
                    friction: 0.1,//摩擦力
                    restitution: 0.7,//反弹力
                }
            )
            //添加接触材质配置
            world.addContactMaterial(concretePlasticMaterial)
        }

        // Start the simulation loop
        function animate() {
            requestAnimationFrame(animate)

            world.fixedStep()

            // the sphere y position shows the sphere falling
            console.log(`Sphere position: ${sphereBody.position}`)
        }

        
    </script>
</body>

</html>

以上代码就是Cannonjs的基础使用他主要模拟了一个球体掉落到地面上的效果,有反弹有摩擦也有外力的影响我们打开浏览器并查看控制台会看到如下的效果(这是球体的坐标信息,我们可以看到当y值为0后【即球体下降到地面上】会向上增加【即反弹了】)
Cannon.js -- 3d物理引擎
2.由于Cannonjs物理引擎库他只是负责数据而不负责显示,这时我们需要使用其它3d库来进行数据效果的显示
首先我们这里先使用three.js搭建球和地面的场景
相关代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/public/normalize.css">
    <title>Document</title>
</head>

<body>
    <script type="module">
        import * as THREE from './lib/three.module.js'
        import { OrbitControls } from './lib/OrbitControls.js'

        // three.js variables
        let camera, scene, renderer, controls
        let sphere

        initThree()
        animate()

        function initThree() {
            scene = new THREE.Scene();//step 1 创建场景

            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
            camera.position.y = 30;
            camera.position.z = 20;
            camera.lookAt(0, 5, 0);
            scene.add(camera); //step 2 场景中添加相机

            // 灯光
            const light = new THREE.PointLight(0xe6dbd1, 1, 100);
            light.position.set(2, 20, 1);
            light.castShadow = true; // default false 阴影
            scene.add(light);

            // 渲染器
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.shadowMap.enabled = true;
            renderer.setClearColor(0xbfd1e5);
            document.body.appendChild(renderer.domElement) //step 4 dom中添加渲染器

            // 地板
            let groundGeom = new THREE.BoxGeometry(40, 0.2, 40);
            let groundMate = new THREE.MeshPhongMaterial({ color: 0xdddddd })
            let ground = new THREE.Mesh(groundGeom, groundMate);
            ground.position.y = -0.1;
            ground.receiveShadow = true;
            scene.add(ground); //step 5 添加地面网格

            // 几何体
            // 圆球体
            const geometry = new THREE.SphereGeometry(2, 32, 16);
            const material = new THREE.MeshPhongMaterial({ color: 0xffff00 });
            sphere = new THREE.Mesh(geometry, material);
            sphere.position.y = 15
            sphere.name = 'ball'
            sphere.castShadow = true; //default is false 阴影


            controls = new OrbitControls(camera, renderer.domElement);
            controls.update()

            //Create a helper for the shadow camera (optional)
            // const helper = new THREE.CameraHelper(light.shadow.camera);
            // scene.add(helper);

            scene.add(sphere)

            window.addEventListener('resize',e=>{
                onWindowResize()
            })
        }

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
        }

        function animate() {
            requestAnimationFrame(animate)
            
            // 动画 让小球下降
            sphere.position.y-=0.08

            controls.update();
            // Render three.js
            renderer.render(scene, camera)
        }
    </script>
</body>

</html>

以上代码使用three.js创建了一个地板和一个球体,球体一直向下掉(但球体到达地板时并没有反弹or停止 如果我们要用代码去实现的话需要加许多的判断 如:判断当球体到达地板时他的y轴增加到一定的值然后有减少... 这样虽然也能实现,但你会发现一个问题:你需要加n多的if,而且需要加许多的物理公式并且效果还不算太好) 效果如下图:
Cannon.js -- 3d物理引擎

最后

最后我们将Cannonjs代码与three.js代码合并

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/public/normalize.css">
    <title>Document</title>
</head>

<body>
    <script type="module">
        import * as THREE from './lib/three.module.js'
        import { OrbitControls } from './lib/OrbitControls.js'

        // 引入cannon-es
        import * as CANNON from './lib/cannon-es.js'

        // three.js variables
        let camera, scene, renderer, controls
        let sphere

        // cannon.js variables
        let world
        // cannon.js 刚体 variables
        let sphereBody

        initScene()
        initCannon()
        animate()

        function initScene() {
            scene = new THREE.Scene();//step 1 创建场景

            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
            camera.position.y = 30;
            camera.position.z = 20;
            camera.lookAt(0, 5, 0);
            scene.add(camera); //step 2 场景中添加相机

            // 灯光
            const light = new THREE.PointLight(0xe6dbd1, 1, 100);
            light.position.set(2, 20, 1);
            light.castShadow = true; // default false 阴影
            scene.add(light);

            // 渲染器
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.shadowMap.enabled = true;
            renderer.setClearColor(0xbfd1e5);
            document.body.appendChild(renderer.domElement) //step 4 dom中添加渲染器

            // 地板
            let groundGeom = new THREE.BoxGeometry(40, 0.2, 40);
            let groundMate = new THREE.MeshPhongMaterial({ color: 0xdddddd })
            let ground = new THREE.Mesh(groundGeom, groundMate);
            ground.position.y = -0.1;
            ground.receiveShadow = true;
            scene.add(ground); //step 5 添加地面网格

            // 几何体
            // 圆球体
            const geometry = new THREE.SphereGeometry(2, 32, 16);
            const material = new THREE.MeshPhongMaterial({ color: 0xffff00 });
            sphere = new THREE.Mesh(geometry, material);
            sphere.position.y = 15
            sphere.name = 'ball'
            sphere.castShadow = true; //default is false 阴影


            controls = new OrbitControls(camera, renderer.domElement);
            controls.update()

            //Create a helper for the shadow camera (optional)
            // const helper = new THREE.CameraHelper(light.shadow.camera);
            // scene.add(helper);

            scene.add(sphere)
        }

        function initCannon() {
            world = new CANNON.World() //初始化一个CANNON对象
            // 设置CANNON的引力  相当与地球的引力(您可以x轴也可以设置y轴或者z轴 负数则会向下掉,正数则向上)
            world.gravity.set(0, -9.82, 0)

            /**
             * 定义项目需要用到的材质
             */
             const concreteMaterial = new CANNON.Material('concrete') //创建混凝土材质
             const plasticMaterial = new CANNON.Material('plastic') //创建塑料材质

            /**
             *创建刚体,刚体就相当于现实生活中的物体(实体)一样 例如:桌子、板凳、大树、乒乓球等 
             */
            // 
            sphereBody = new CANNON.Body({
                mass: 10,//质量
                position: new CANNON.Vec3(0, 15, 0),//位置
                //刚体的形状。 CANNON.Sphere为圆球体  CANNON.Box为立方体 更多形状查看文档:https://pmndrs.github.io/cannon-es/docs/classes/Shape.html
                shape: new CANNON.Sphere(2),
                //材质  材质决定了物体(刚体)的弹力和摩擦力,默认为null,无弹力无摩擦。 plastic为塑料材质  concrete为混泥土材质。相关文档地址:https://pmndrs.github.io/cannon-es/docs/classes/Material.html
                material: plasticMaterial,
            })
            //添加外力,这有点类似于风力一样,在某个位置向某物吹风
            // 该方法接收两个参数:force:力的大小(Vec3)    relativePoint:相对于质心施加力的点(Vec3)。
            sphereBody.applyForce(new CANNON.Vec3(100, 0, 0), sphereBody.position)

            world.addBody(sphereBody) //向world中添加刚体信息


            /**
             * 创建地板刚体
             */
            const floorBody = new CANNON.Body()
            floorBody.mass = 0//质量  质量为0时表示该物体是一个固定的物体即不可破坏
            floorBody.addShape(new CANNON.Plane())//设置刚体的形状为CANNON.Plane 地面形状
            floorBody.material = concreteMaterial//设置材质
            // 由于平面初始化是是竖立着的,所以需要将其旋转至跟现实中的地板一样 横着
            // 在cannon.js中,我们只能使用四元数(Quaternion)来旋转,可以通过setFromAxisAngle(…)方法,第一个参数是旋转轴,第二个参数是角度
            floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(-1, 0, 0), Math.PI * 0.5)
            world.addBody(floorBody)

            /**
             * 设置两种材质相交时的效果  相当于设置两种材质碰撞时应该展示什么样的效果 例如:篮球在地板上反弹
             */
            //创建一个接触材质
            const concretePlasticMaterial = new CANNON.ContactMaterial(
                concreteMaterial,//材质1
                plasticMaterial,//材质2
                {
                    friction: 0.1,//摩擦力
                    restitution: 0.7,//反弹力
                }
            )
            //添加接触材质配置
            world.addContactMaterial(concretePlasticMaterial)
        }

        

        function animate() {
            requestAnimationFrame(animate)

            world.fixedStep()//动态更新world
            
            sphere.position.copy(sphereBody.position)//设置threejs中的球体位置
            // // 动画 让小球下降
            // sphere.position.y-=0.08

            controls.update();
            // Render three.js
            renderer.render(scene, camera)
        }


        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
        }
    </script>
</body>

</html>

效果如下:(非常接近现实了)
Cannon.js -- 3d物理引擎

注意点:

  • cannon.js中创建box与three.js创建box不一样。
    在three.js中,创建几何体BoxBufferGeometry只需要直接提供立方体的宽高深就行,而在cannon.js中,它是根据立方体对角线距离的一半来计算生成形状,因此其宽高深必须乘以0.5 ,否则立方体会有悬空效果
    Cannon.js -- 3d物理引擎
//cannon.js中创建立方体
const shape = new CANNON.Box(new CANNON.Vec3(width * 0.5,height * 0.5,depth * 0.5))

优化

1.粗测阶段(BroadPhase)
cannon.js会一直测试物体是否与其他物体发生碰撞,这非常消耗CPU性能,这一步成为BroadPhase。当然我们可以选择不同的BroadPhase来更好的提升性能。
NaiveBroadphase(默认) —— 测试所有的刚体相互间的碰撞。
GridBroadphase —— 使用四边形栅格覆盖world,仅针对同一栅格或相邻栅格中的其他刚体进行碰撞测试。
SAPBroadphase(Sweep And Prune) —— 在多个步骤的任意轴上测试刚体。
默认broadphase为NaiveBroadphase,建议切换到SAPBroadphase。
当然如果物体移动速度非常快,最后还是会产生一些bug。
切换到SAPBroadphase只需如下代码

world.broadphase = new CANNON.SAPBroadphase(world)

2.睡眠Sleep
虽然我们使用改进的BroadPhase算法,但所有物体还是都要经过测试,即便是那些不再移动的刚体。
因此我们需要当刚体移动非常非常缓慢以至于看不出其有在移动时,我们说这个刚体进入睡眠,除非有一股力施加在刚体上来唤醒它使其开始移动,否则我们不会进行测试。
只需以下一行代码即可

world.allowSleep = true

当然我们也可以通过Body的sleepSpeedLimit属性或sleepTimeLimit属性来设置刚体进入睡眠模式的条件。
sleepSpeedLimit ——如果速度小于此值,则刚体被视为进入睡眠状态。
sleepTimeLimit —— 如果刚体在这几秒钟内一直处于沉睡,则视为处于睡眠状态。

事件

我们可以监听刚体事件如:碰撞colide睡眠sleep或唤醒wakeup

body.addEventListener('collide',e=>{
	console.log(e)
})

其他

Web Worker
由于JavaScript是单线程模型,即所有任务只能在同一个线程上面完成,前面的任务没有做完,后面的就只能等待,这对于日益增强的计算能力来说不是一件好事。所以在HTML5中引入了Web Worker的概念,来为JavaScript创建多线程环境,将其中一些任务分配给Web Worker运行,二者可以同时运行,互不干扰。Web Worker是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。

在计算机中做物理运算的是CPU,负责WebGL图形渲染的是GPU。现在我们的所有事情都是在CPU中的同一个线程完成的,所以该线程可能很快就过载,而解决方案就是使用worker。
我们通常把进行物理计算的部分放到worker里面,具体可看这个例子的源码web worker example

cannonjs常用工具:

  • three-to-cannon:用于生成3d模型的刚体信息

  • cannon-es-debugger:用于调试cannon.js,效果如下:
    Cannon.js -- 3d物理引擎

  • Stats调试工具:
    FPS:每一秒的帧数,越大越流畅
    MS:渲染一帧需要的时间(毫秒),越低越好
    MB:占用内存信息

  • AxesHelper 坐标轴调试模式
    AxesHelper 是在场景的中心点,添加一个坐标轴(红色:X 轴、绿色:Y 轴、蓝色:Z 轴),方便辨别方向

  • Light Helper 光源调试模式
    聚光灯开启 Light Helper 调试模式后,可以直观的看到 distance 、 angle 的参数效果。

  • Camera Helper 摄像机调试模式
    开启 Camera Helper 调试模式后,可以直观的看到 Camera 的 Fov 、 Nera 、 Far 的参数效果。

本文完整代码下载:

https://gitcode.net/z1783883121/three.js-cannonjs

相关链接:

api文档链接:Cannon.js
参考链接:https://blog.csdn.net/weixin_43990650/article/details/121815208、https://new.qq.com/rain/a/20220408A092WB00文章来源地址https://www.toymoban.com/news/detail-408423.html

到了这里,关于Cannon.js -- 3d物理引擎的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity3D 实现基于物理引擎的绳子关节解析详解

    在游戏开发中,有时候我们需要实现绳子关节效果,比如在射击游戏中射击绳子,或者在平衡游戏中使用绳子作为支撑。本文将详细介绍如何使用Unity3D的物理引擎实现绳子关节效果。 对惹,这里有一 个游戏开发交流小组 ,希望大家可以点击进来一起交流一下开发经验呀 首

    2024年02月21日
    浏览(68)
  • 深入分析物理引擎后,他写了一个轻量的 Cocos 3D 碰撞检测优化方案

    引言: 碰撞检测是游戏开发中一个非常重要的技术点,优化碰撞检测性能,是提升游戏体验不可或缺的一环。开发者「我叫98K」写了一个轻量碰撞系统,用以改善 3D 游戏在不同平台遇到的碰撞性能问题和包体问题。下载和在线体验地址见文末。 98K物理-轻量碰撞系统是一个高

    2024年02月03日
    浏览(30)
  • 3D渲染引擎介绍

    专业处理视觉呈现的渲染库。 3D引擎从商业属性上分为:商业引擎和开源引擎,从业务领域上分为:游戏引擎、GIS引擎、仿真引擎等,部分引擎可能具备多种领域组合,开发语言涉及包括:C++、C#、Java、JavaScript、GLSL及各类脚本等。 UE4游戏引擎-商业引擎(源码开源)-游戏引

    2024年02月11日
    浏览(31)
  • 【JS/TS游戏开发实战】LayaAir 全平台 3D 引擎

    LayaAir3.0引擎,包括引擎代码、项目开发工具、项目发布,三大部分。

    2024年02月07日
    浏览(33)
  • WebGL入门之基于WebGL的3D可视化引擎介绍

    WebGL(Web Graphics Library)是一个JavaScript API,用于在任何兼容的Web浏览器中渲染高性能交互式3D和2D图形,而无需使用插件。WebGL通过引入一个非常符合OpenGL ES 2.0的API来实现这一点,该API可以在HTML 元素中使用。这种一致性使 API 可以利用用户设备提供的硬件图形加速。WebGL完全集

    2024年02月11日
    浏览(31)
  • 一文扫荡:3D可视化项目所有技术栈,重点讲建模和js引擎库

    Hi,我是贝格前端工场,发过不少3D可视化的项目,各位老铁们最感兴趣的是用了哪些技术栈,本文和盘托出,并重点讲解建模渲染的知识,以及常用js库,文章最后有更加详细的往期回顾。 3D可视化项目是指利用3D技术和工具来将数据、场景或概念以可视化的方式呈现出来,

    2024年04月12日
    浏览(32)
  • node.js 什么是模板引擎?(具体介绍underscore)

    前言:在 Web 开发中,数据的呈现通常是基于 HTML 和 CSS 的,而数据的变化又是非常频繁的,需要根据数据动态生成 HTML 标记。手动拼接 HTML 标记显然是一种非常低效的方式,不仅容易出错,而且难以维护。使用,我们使用到了模板引擎来解决这一问题。  模板引擎是一种将数

    2024年04月23日
    浏览(25)
  • Node.js、Chrome V8 引擎、非阻塞式I/O介绍

    👍 点赞,你的认可是我创作的动力! ⭐️ 收藏,你的青睐是我努力的方向! ✏️ 评论,你的意见是我进步的财富! Node.js 是一个基于 Chrome V8 引擎的开源服务器端 JavaScript 运行环境,它允许开发者使用 JavaScript 编写服务器端应用程序。以下是关于 Node.js 的详细介绍: 特点

    2024年02月05日
    浏览(32)
  • 如何基于three.js(webgl)引擎架构,研发一套通过配置就能自动生成的3D机房系统

    序: 这几年观察下来,大部分做物联网三维可视化解决方案的企业或个人, 基本都绕不开3D机房。包括前面也讲过这样的案例《使用webgl(three.js)创建自动化抽象化3D机房,3D机房模块详细介绍(抽象版一)》  《 使用webgl(three.js)创建科技版3D机房,3D机房微模块详细介绍(升级版

    2024年02月08日
    浏览(27)
  • 3D数字孪生 - Three.js 项目介绍与基础环境搭建(一)

    根据WMS系统基础仓库数据以及RCS调度坐标系统,生成3D可视化仓库地图,能够实时监控仓库库位坐标、调度任务状态、车辆位置等信息。 社区对于threejs的实战案例太少,于是,花了一个月的时间,手撕了这个需求。此篇重点不会对threejs做深入讲解,毕竟我也是刚上车不到一

    2024年04月11日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包