游戏物理引擎之静态碰撞
最近在编写一些简单的物理引擎,在编写静态碰撞的时候遇到了小问题,经人指点后终于弄懂了,现在写出来分享下。
物理引擎主要是用来模拟大自然的力的作用。在我看来,物理引擎主要有两个机制:
- 作用力发生器。就是来模拟诸如重力、阻力、弹簧弹力等常见的牛顿力。
- 碰撞。这里的碰撞是非常广义的,基本上作用力发生器模拟不了的情况都能用碰撞模拟。
这里扯一个题外话,在计算机动画里,特别是物理引擎中主要处理的是质点的位置和速度,很多大自然的物理都是近似看成由多个质点组成的。所以,碰撞主要解决的是三类问题:
- 碰撞过程中质点的速度变化,这个很简单,由动量守恒公式(即m1v1+m2v2=m1'v1'+m2'v2')就可以模拟出来。
- 碰撞过程中质点的位置变化,这个属于碰撞检测问题,有许多成熟的算法,这里不做详述。
- 静态碰撞问题。就是如何利用碰撞来模拟物理静止的过程。我们今天就来讨论一下这个问题。
在计算机图形学中,我们可以利用OpenGL等图形API在三维场景中绘制一些三维图形,比如地面、桌子、小球等。但是计算机本身不知道什么是地面,什么是桌子,所以在当我们给地面上添加一个受重力作用的静止小球,我们看到的只会是小球一直下落,而不是静止在地面上。当然我们可以加一些几何约束(y<0时,y=0),强行让小球在地面上,但是我们用物理引擎模拟显然不能做,我们可以将这种情况模拟成碰撞,这一类的碰撞都称为静态碰撞。如图1,可以看第2帧的时候小球位于地面下面,我们可以认为这时小球是与地面相交的,生成碰撞。
图1
我们肯定不希望我们的程序中物体没有静止在地面上,反而穿过地面继续下坠。我们需要对碰撞行为进行检测。如上图,我们可以看出在第1帧状态下,小球的位置是没有问题,但到了第2帧,小球与地面相交了。所以我们要寻找一个通过两帧的碰撞信息就能判断出第1帧状态下小球是否是静止的。我们结合上图分析,因为小球只受到重力作用,即小球的速度仅由其重力引发,对此,可简单的将多用力乘以第1帧和第2帧之间的时间间隔(duration),进而得到基于当前作用力下的速度数据,这个速度我们写作Vg。由于我们将小球作碰撞处理,所以由碰撞机制可以得到小球在第2帧的真实速度V。我们比较Vg和V,如果实际速度V小于Vg,则可知小球在第1帧中处于静止状态。
那么问题来了,为什么比较这两个速度就可以确定小球在前一帧就是静止状态?我们结合图2来分析一下。在小球第1帧静止在在地面V=0,但是我们计算一下如果按碰撞处理的话,小球肯定会产生一个方向向上的速度V'。这时我们再跟Vg进行比较,V'<Vg,说明小球并没有向上运动的趋势,所以小球的初始前一帧状态要么是静止要么是速度非常非常小,都可以近似作静态碰撞处理。
图2
最后贴一段处理这个过程的代码。
double newSepVelocity = -separatingVelocity * restitution; CVector3 accCausedVelocity = particle[0]->GetAcceleration(); if (particle[1]) accCausedVelocity -= particle[1]->GetAcceleration(); double accCausedSepVelocity = accCausedVelocity * contactNormal * duration; /*比较合外力产生的速度和碰撞后物体的速度*/ if (accCausedSepVelocity < 0) { /*newSepVelocity为正值*/ newSepVelocity += restitution * accCausedSepVelocity; if (newSepVelocity < 0) newSepVelocity = 0; }