BulletPhysics物理引擎手册(1)

Bullet讨论群:533030320

笔者最近在写一个的物理引擎Chocolate3D/2D,受bullet引擎的启发,并简化bullet的复杂流程(虽然bullet高度可自定义),实现即用既得的效果.. 其实为了广大的new birds,在这个时代,学习成本往往是最大的发展瓶颈吧…

哪吒自语:
请原谅笔者在文中大量穿插“英语” 因为有些专业词汇实在不能用中文表达,而且笔者自认为文学素养比较低,也创造不出什么高雅的词,所以直接用英语代替“简单粗暴”,好吧,让我们开始?

在这个章节中,我会详细描述Bullet库的初始化和如何build我们第一个物理刚体模拟程序
当然首先你得去github下载库,并自己运行几个小程序?并且你也要一些openGL功底?其实不需要也无所谓,但是你必须怀着满腔激情…(此处省略5万多字)…为了中国图形事业的发展…(再次省略5万字)

什么是核心组件?

Bullet从设计之初就被定义为一个可以高度自定义的物理引擎。每个引擎使用的主要task都被独立成一个单独的组件,可以被很轻易的替换(只要符合Bullet定义的接口规范)

大部分的应用都可以直接使用Bullet默认配置,当然如果我们需要一些更高级的,优化的,或一些技术最优的方法我们可以自己打造一个组件添加入bullet中,总而言之,bullet是可以高度自定义化的基于插件的架构(不过调试起来很麻烦…笔者在深入研究时很有体会,因为基于同样的接口规范,所以可能有几百种实现方式,当你向上索引调用方法时就会明白… 但不可否则这种复杂度在良好的架构面前绝对不值一提)

在初始化bullet时我们需要创建一些必要的组件,并且需要被结合调用在一起hooked together。 在这一系列文中我将会涵盖大部分的组件核心理论原理,并walk through整个代码流程。

上帝说要有世界

在bullet模拟流程中,当然所有事情都发生在“世界”之中,就像我们的现实世界一样,所有模拟的程序都在控制世界的btDynamicsWorld中发生。我们向世界中创造的所有物体都在这个类所定义的规则下运行着。我们创造了规则,物理法则和一切.. 所以我们定义的这个类就是世界。bullet为我们定义了多种btDynamicsWorld来给你选择自己所需要的物理模拟,其中有:
btSimpleDynamicsWorld: public btDynamicsWorld
btDiscreteDynamicsWorld: public btDynamicsWorld
当然你可以自己继承btDynamicsWorld来自定义某些行为。
在文中我就用btDiscreteDynamicsWorld来做演示,这个世界会随着时间的推进离散得(真实世界就是离散得)进行物体的移动模拟。
当然这个类不会去定义如何进行碰撞检测,或者碰撞响应。它只定义了随着模拟的进行,物体将会如何移动,仅此而已。而碰撞检测,碰撞响应都有单独的组件完成,并且这些组件是可以自定义的。

粗碰撞或者说broad phase?

物理模拟是实时的,所以每次运算都必须严格的控制时间的消耗,算法,策略都必须苛刻到最优。才能让用户感觉到实时! 在模拟的过程中的每一步,都会移动物体,并计算碰撞,如果碰撞了,就要执行相应的响应操作,这一切都必须发生在电光火石之间。

但是由于算法的限制(笔者感觉是数学工具的限制),计算碰撞是非常耗时的操作。如果我们把一个物体和世界中其他物体每个都一 一碰撞检测过来,估计等的花儿都谢了吧。如果我们有N个物体,那就是NxN次碰撞,这种o(N^2)的复杂度是不能接受的,如果我们有100个物体我们就要进行接近一万次检测..及其恐怖的次方级别复杂度。

所以聪明的人们发明了一个数据结构来定位物体,如果两个物体的距离很远,他们压根就不会碰撞就不会去检测了。这就是所谓的粗碰撞阶段(broad phase)。

大部分的碰撞都使用 Axis-aligned bounding boxes(AABBs)进行碰撞检测,当然还有其他的,比如oriented-aligned bounding boxesk-dops bounding boxes
哪吒自语:
笔者对这些都有研究,其中k-dops算法曾操刀于国内某家公司中….但平心而论AABB虽然算不上是最精确的包围盒,但确实是最快的技术,在模型不是异常复杂的情况下,没太大必要用其他的包围盒,有些东西不是越复杂越好的…

BulletPhysics物理引擎手册(1)_第1张图片

上图显示了每个物体的包围盒,AABB只在世界坐标轴x,yz向上进行一个最大最小的判断

所以非常容易判断两个物体的包围盒是否相交,最大最小值是否重叠?

然后我们对整个空间进行分割,这样可以更快速的检测最临近的物体是哪个。而数据的组织方式就是一个二叉树

OK我们了解了粗碰撞大概是个什么东西就可以了。如果要细细品味数据结构的组织方式,插入,删除,移动节点等…尽情期待哪吒的第二个科普系列贴 实时碰撞检测系统
当然还没上线哈哈.. 等我写完这个系列后会开始细究引擎的内部实现。期待吧。。

等等还没说既然这个碰撞检测是作为一个组件被bullet使用的那么这个组件的接口名称叫什么?
btBroadPhaseInterface,有了这个接口标准我们就可以自定义一个碰撞检测类了,bullet就负责调用这个接口中的method,当然实现我们要实现并在这些方法中提供数据。什么?我还没那么NB,写不出来碰撞检测系统?那我们就用bullet内置的吧..btDbvtBroadphase – 全称是bullet Dynamic Bounding volume tree Broadphase 其中实现了标准的碰撞检测,包括树啊,代理规则啊,事件分发器啊等等,比如静态的物体不能和静态的碰撞啥的.. 都封装好了实现,这也是个巨大的东东.. 有兴趣可以自己进源代码看,笔者贴出主要一些数据结构来给看看,让你们领略下这简单背后的复杂…

碰撞也需要配置

这是一个看上去简单简单,其实超复杂的组件,他负责的主要功能是决定Bullet使用何种内存管理方式,在遇到box-box, sphere-box, 和其他不同的碰撞时该用什么算法,其实它在内部会搜索合适的算法进行使用。下表摘自bullet开发手册
BulletPhysics物理引擎手册(1)_第2张图片

For each pair of shape types, Bullet will dispatch a certain collision algorithm, by using the dispatcher.By default, the entire matrix is filled with the following algorithms. Note that Convex represents convex polyhedron, cylinder, cone and capsule and other GJK compatible primitives. GJK stands for Gilbert, Johnson and Keerthi, the people behind this convex distance calculation algorithm. It is combined with EPA for penetration depth calculation. EPA stands for Expanding Polythope Algorithm by Gino van den Bergen. Bullet has its own free implementation of GJK and EPA.

当然还有决策图

BulletPhysics物理引擎手册(1)_第3张图片

还有粗碰撞后的结果被称作 Manifolds 我们会在之后讲到
在这里为了使new birds入门我就用那bullet默认的碰撞配置object btDefaultCollisionConfiguration 当然如果你再后面也可自定义继承接口btCollisionConfiguration进行深度开发。这是后话了

碰撞完了我们要分发事件 (collision dispatcher)

碰撞分发器是如此重要,它负责通知程序哪些物体互相碰撞了,碰撞点在哪里等信息。 同样的bullet内置了一个基本的分发器 btCollisionDispatcher,它还负责寻找碰撞的算法..不对啊,上文不是说由碰撞配置object负责么? 其实他们俩是协同的,碰撞配置负责提供各种算法,当发现两个物体碰撞时把两个物体交付给分发器,分发器寻找碰撞配置中提供的算法,并应用在物体上。为什么要这么做呢?因为要解耦合,配置只管提供数据,而有dispather这个控制器决定如何碰撞。有学过MVC概念的同学大概就能理解了,配置是Model而分发器则是controller。 所以分发器需要配置提供的算法,
btCollisionDispatcher (btCollisionConfiguration* collisionConfiguration);
这是他的构造函数,配置作为参数传递进dispather中,所以迫使我们必须在配置创建之后创建控制器。

世界需要限制 constraint solver

constraint solver的主要功能就是让我们的世界中的物体对特定的限制进行响应,比如门的铰链不能超过120度啊,被钩子勾住的物体不能自由下落啊,轮子不能从汽车上飞走啊…等等,这都属于限制条件.而constraint solver则解决类似的问题而存在。
我们在项目中使用btSequentialImpulseConstraintSolver,上述所有的组件都能被替换,这个结构的好处是如果我们在写一个基于手机或者平板的软件时我们随时可以替换掉默认的内存控制器,来优化内存..众所周知移动端的内存非常吃紧…或者替换掉复杂的碰撞规则,当然吃紧的不只是内存还有CPU。。。总而言之,bullet是一个高度可自定义的引擎,只要你够牛,就没什么做不到的

你可能感兴趣的:(物理引擎)