threejs 物理引擎ammo自学

概念性的描述

概述
  • 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 - 物理属性

未完待续....

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容