美文网首页
threejs 物理引擎ammo自学

threejs 物理引擎ammo自学

作者: squidbrother | 来源:发表于2023-03-23 16:56 被阅读0次

概念性的描述

概述
  • Ammo.js 使用Emscripten将 Bullet物理引擎 直接移植到JavaScript。源代码被直接翻译成JavaScript,未进行人工重写,因此功能与原始项目相同。
  • Bullet Physics是一个开源的物理模拟引擎,世界三大物理引擎之一(另外两种是Havok和PhysX)。
ammo引擎相关示例资源查看

官方网址
基于ammon.js的演示应用程序基类和3D物理演示。支持多种场景图,包括Three.js和SceneJS

  1. 官网示例地址 - http://schteppe.github.io/ammo.js-demos/

  2. 物理引擎在github上的地址 - https://github.com/kripken/ammo.js/#readme
    注:

  • github地址下载的demo比ammo官网demo多一些
  • 这是来自Bullet的HelloWorld.cpp,翻译成JavaScript。该目录中的其他示例可能也很有用。特别请参阅中的WebGL
    示例演示目录 - ammo.js-main/examples

※ 下落的盒子 - ammo.js-main/examples/webgl_demo/ammo.wasm.html

下落的盒子

※ 彩色的墙 - ammo.js-main/examples/webgl_demo_gimpact_chain/index.html

彩色的墙

※ 柔软的布料 - ammo.js-main/examples/webgl_demo_softbody_cloth/index.html

柔软的布料

※ 绳子 - ammo.js-main/examples/webgl_demo_softbody_rope/index.html

绳子

※ 网格体积 - ammo.js-main/examples/webgl_demo_softbody_volume2/index.html - (渲染很卡)

网格体积

※ 建立地形 - ammo.js-main/examples/webgl_demo_terrain/index.html

建立地形

※ 射线测试 - ammo.js-main/examples/webgl_demo_test_ray/index.html

射线测试

※ 小车移动 - ammo.js-main/examples/webgl_demo_vehicle/index.html

小车移动
  1. 使用ammo引擎开发示例帖子地址 - https://juejin.cn/post/6985033373857579045 - (推荐!)
  • 一个3d球体在平台移动,涉及物理控制、移动端摇杆适配、场景内物品、交互镜头使用....


    项目截图
其他物理引擎

3D引擎

  • cannon引擎官网地址 - https://pmndrs.github.io/cannon-es/

2D引擎

  • matter - https://brm.io/matter-js/docs/
  • p2 - https://github.com/schteppe/p2.js#demos

物理引擎基本绑定流程

创建一个带物理属性的模型的流程
ammo引擎 - 物理模型与图形模型绑定 - 模型渲染更新
一些关键词说明
  • mass -- 质量
  • inertia -- 惯性
    ...

模型的物理形状

  • 学习的文档来源: http://www.dwenzhao.cn/profession/netbuild/ammoegine.html
物理模型的形状构成
  1. 球体形状
  • 使用范围: 任何球体使用的例子
  • 创建参数: 球体碰撞形状构造器,radius为球半径
Ammo.btSphereShape( ballRadius )
  • 方法:
setMargin(margin) -- 设置碰撞形状边缘数
getMargin() -- 获取碰撞形状边缘数
getRadius( ) -- 获取球的半径 
  1. 长方体形状
  • 使用范围: 用于盒子、箱子等规则物体
  • 创建参数: 构造器,boxHalfExtents表示立方体盒子的半区域
Ammo.btBoxShape(btVector3 boxHalfExtents) 
  • 方法:
setMargin(margin)  -- 设置碰撞形状边缘数
getMargin()  -- 获取碰撞形状边缘数
  1. 圆柱形状
  • 使用范围: 杆、金币、石柱等都可以采用此类,但碰撞计算量较大,不如胶囊
  • 创建参数: 圆柱对象构造器,halfExtents为圆柱的半区域,三维分量,第1和3维表示圆柱的长短半径,第2维是长度
Ammo.btCylinderShape(btVector3 halfExtents) 
  • 方法:
getRadius( )  -- 获取圆柱的半径
  1. 胶囊形状
  • 使用范围: 碰撞计算量比圆柱小,旗杆、铅笔
  • 创建参数: 胶囊碰撞形状对象构造器,参数radius为两端球面的半径,height为中间圆柱的长度
Ammo.btCapsuleShape(float radius, float height) 
  • 方法:
getRadius( )  -- 获取胶囊截面的半径 
getHalfHeight( )  -- 获取中间圆柱部分长度值的一半 
  1. 圆锥形状
  • 使用范围: 碰撞计算量比圆柱小,旗杆、铅笔
  • 创建参数: 圆锥碰撞形状对象构造器,参数radius为圆锥的半径,height为圆锥的高度
Ammo.btConeShape(float radius, float height) 
  • 方法:
getRadius( )  -- 获取胶囊截面的半径 
  1. 复合形状
  • 复合形状构造器
btCompoundShape() 
  • 方法:
//-- 向组合形状中添加子形状,localTransform为子形状的变换,shape为添加的子形状
addChildShape ( btTransform localTransform, btCollisionShape shape)
//-- 从组合形状中删除指定的子形状, childShapeindex为子形状索引
removeChildShape( childShapeindex)
//-- 获取当前组合形状中子形状的数量
getNumChildShapes()
//-- 获取组合形状中指定索引编号的子形状,index为子形状索引 
getChildShape(index)

模型的物理属性配置以及受力方法

btRigidBody - 刚体 - 运动的方法
  1. 为刚体设置摩擦力
btRigidBody实例.setFriction(数字)
  1. 获取刚体的加速度
btRigidBody实例.getAngularVelocity()
  1. 刚体设置线速度 - 使物体朝着某个坐标线性运动过去
  • 先坐标变化,再使用setLinearVelocity,移动物体过去
//-- 三维向量
const pos = new THREE.Vector3(1,1,1);
//-- 设置(x,y,z)、三维中每个值乘以14
pos.multiplyScalar( 14 );
btRigidBody实例.setLinearVelocity( new Ammo.btVector3( pos ) );
  1. 获取线速度
btRigidBody实例.getLinearVelocity()

线性速度实际使用过程中

  • 为刚体添加摩擦力
  • 通过Vector3上的copy方法,获取上一时刻的坐标信息,赋值给Vector3的实例pos
  • 通过Vector3上的multiplyScalar方法,将pos坐标信息(x,y,z)乘以某个数值
  • 通过给 刚体实例.setLinearVelocity( new Ammo.btVector3( pos.x, pos.y, pos.z ) ) 给刚体添加速度
  • 通过getLinearVelocity查看实例的线性速度返回字符串格式为 - {"hy":8466696}
  • 通过getAngularVelocity获取了加速度,发现值略比getLinearVelocity大一点 - {"hy":8466712}
  1. 添加一个外力 - 参数(应用的力, 施加力的位置 )
  • 参照cannon物理引擎,从空间中的一个特殊点对刚体施加力(不一定在刚体的表面)
  • 如: 自然中的风
btRigidBody实例.applyForce(btVector3格式作用力, btVector3格式坐标点)
  1. 添加一个刚体局部坐标系中的力
btRigidBody实例.applyCentralLocalForce(btVector3格式坐标点)

物理世界启动与渲染配置

物理世界启动
  1. 物理世界的类型
  • 为了模拟不同材质的物体,物理世界也对应出了不同的分支


    物理世界的种类构成
  1. 物理世界的配置
  • 创建物理世界,需要传入不同参数,定制化其效果
  • 刚体世界 示例代码:
//完全碰撞检测算法
let collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
// 重叠对/碰撞的调度计算
let dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
// 所有可能碰撞对的宽相位碰撞检测列表
let broadphase = new Ammo.btDbvtBroadphase();
// 使物体正确地交互,考虑重力、力、碰撞等
let solver = new Ammo.btSequentialImpulseConstraintSolver();
physicsWorld = new Ammo.btDiscreteDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration);
普通刚体物理世界
  1. 物理世界中 碰撞参数与物理世界之间的对应关系


    碰撞算法与物理世界

    | 碰撞算法 | 物理世界 |
    | btDefaultCollisionConfiguration | btDiscreteDynamicsWorld |
    | btSoftBodyRigidBodyCollisionConfiguration | btSoftRigidDynamicsWorld |

  2. 既支持刚体也支持软体的物理世界配置
    基本代码:

//-- 物理世界变量
const gravityConstant = - 9.8;

//-- 启动物理世界
initPhysics();
function initPhysics() {

    //-- 设置物理引擎的碰撞类型 - 软体与刚体碰撞
    const collisionConfiguration = new Ammo.btSoftBodyRigidBodyCollisionConfiguration();
    //-- dispatcher为碰撞检测算法分配器引用
        const dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration );
      
        //-- 为碰撞粗测算法接口
    const broadphase = new Ammo.btDbvtBroadphase();
        
        //-- 配置约束解决器 - (序列脉冲约束解决器)
        //-- 使物体正确地交互,考虑重力、力、碰撞等
    const solver = new Ammo.btSequentialImpulseConstraintSolver();
        //-- 配置约束解决器 - (软体约束解决器)
    const softBodySolver = new Ammo.btDefaultSoftBodySolver();

        //-- 创建一个支持软体、刚体的物理世界
    physicsWorld = new Ammo.btSoftRigidDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration, softBodySolver );

       //-- 设置物理世界的重力
    physicsWorld.setGravity( new Ammo.btVector3( 0, gravityConstant, 0 ) );
    physicsWorld.getWorldInfo().set_m_gravity( new Ammo.btVector3( 0, gravityConstant, 0 ) );
}
物理世界的渲染
  • 说明:
    模型配置了物理碰撞属性,在物理世界里就拥有了运动轨迹,可以实时查看位置

*知识点:

  • 获取运动状态信息:
    通过btTransform类:变换类
    该类由位置和方向组合而成,用来表示刚体的变换,如平移、旋转等

  • 通过刚体的getMotionState获取刚体形状
    getMotionState() - 获取刚体的形状,返回值为获取的形状指针

  • 通过刚体形状的getWorldTransform( btTransform实例transformAux1 )获得最新的transformAux1信息

  • 通过transformAux1的getOrigin()获取网格模型的位置坐标

  • 通过transformAux1的getRotation()获取网格模型的旋转角度

  • 渲染网格的最新位置与渲染姿态

ms.getWorldTransform( transformAux1 );
const p = transformAux1.getOrigin();
const q = transformAux1.getRotation();
objThree.position.set( p.x(), p.y(), p.z() );
objThree.quaternion.set( q.x(), q.y(), q.z(), q.w() );
  • 示例代码:
//-- 物理世界变量
const gravityConstant = - 9.8;
let transformAux1;

//-- 启动物理世界
initPhysics();
animate();
function initPhysics() {
        ...
        //-- 创建一个支持软体、刚体的物理世界
    physicsWorld = new Ammo.btSoftRigidDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration, softBodySolver );

       //-- 设置物理世界的重力
        physicsWorld.setGravity( new Ammo.btVector3( 0, gravityConstant, 0 ) );
    physicsWorld.getWorldInfo().set_m_gravity( new Ammo.btVector3( 0, gravityConstant, 0 ) );

    //-- 刚体运动监测对象
    transformAux1 = new Ammo.btTransform();
}

//-- 添加网格模型,挂载物理外形到自定义属性physicsBody
function addRigidMass(){
    ...
    //-- 创建一个与网格几何模型形状发相似的物理几何模型
    const body = new Ammo.btRigidBody( rbInfo );
    threeObject.userData.physicsBody = body;
    ...
    //-- 添加网格模型到场景
    scene.add( threeObject );
    //-- 有质量的物体会参与碰撞,将其添加到一个数组rigidBodies中
    //-- 源码中如果mass为0,则为地板类型刚体,不参与运动计算
    if ( mass > 0 ) {
        rigidBodies.push( threeObject );
    }
    //-- 添加物理模型到物理世界
    physicsWorld.addRigidBody( body );
}
function animate() {
      //-- 渲染
      const deltaTime = clock.getDelta();
      //-- 渲染物理世界
      updatePhysics( deltaTime );
      renderer.render( scene, camera );
      requestAnimationFrame( animate );
}
function updatePhysics( deltaTime ) {
        // Step world
    physicsWorld.stepSimulation( deltaTime, 10 );

    //-- rigidBodies为带有自定义属性
    for ( let i = 0, il = rigidBodies.length; i < il; i ++ ) {
        const objThree = rigidBodies[ i ];
        const objPhys = objThree.userData.physicsBody;
        //-- 通过物理模型,获取刚体的形状
        const ms = objPhys.getMotionState();
        if ( ms ) {
            //-- 获取物理世界中的运动姿态
            ms.getWorldTransform( transformAux1 );
            const p = transformAux1.getOrigin();
            const q = transformAux1.getRotation();
            objThree.position.set( p.x(), p.y(), p.z() );
            objThree.quaternion.set( q.x(), q.y(), q.z(), q.w() );
        }
    }
}

ammo.wasm.js待整理的方法

  1. set - 设置系列
* getLinearVelocity - 获取线性速度
* setLinearVelocity - 设置线性速度
* setLinearFactor - 设置线性系数
* getAngularVelocity - 获取角速度
* setAngularVelocity - 设置角速度
* setAngularFactor- 设置角度系数
* setCenterOfMassTransform - 设置质心变化
* setSleepingThresholds - ?
* setDamping - 设置阻尼
* setFriction - 设置摩擦力
* setRollingFriction - 设置滚动摩擦
* setAnisotropicFriction - 设置异性摩擦
* setMassProps - 设置道具质量
* setMotionState - 设置运动状态
* setGravity - 设置重力
* setContactProcessingThreshold - ?
* setActivationState - 设置激活状态
* setRestitution - ?
* setCollisionFlags - 设置碰撞标记
* setCollisionShape - 设置碰撞形状
* setWorldTransform - 设置世界变化
* setCcdMotionThreshold - ?
* setCcdSweptSphereRadius - ?
* setUserIndex - 设置使用者索引
* setUserPointer - 设置用户坐标

待学习

http://schteppe.github.io/ammo.js-demos/ - ammo引擎官方
https://juejin.cn/post/7200039970575941693 - 物理引擎差异文章( 优先看!!! )
https://juejin.cn/post/7095621578976657421 - 动画
https://www.cnblogs.com/lxiang/archive/2012/09/13/2683220.html - ApplyForce、ApplyImpulse、SetLinearVelocity
https://pmndrs.github.io/cannon-es/docs/classes/Trimesh.html - 模型添加碰撞形状
http://threejs.org/examples/index.html#physics_ammo_volume - 官方示例软体模型与刚体模型
http://threejs.org/examples/physics_ammo_instancing.html - 发射子弹与指定碰撞地点
http://threejs.org/examples/physics_ammo_break.html - 物理属性

未完待续....

相关文章

网友评论

      本文标题:threejs 物理引擎ammo自学

      本文链接:https://www.haomeiwen.com/subject/djxbrdtx.html