我的答案有两个警告,我将首先避开:它仅处理非旋转边界框。它假定您正在尝试处理隧道问题,即由对象高速移动引起的问题。
一旦确定了MTV,便知道需要测试的边缘/表面法线。您还知道互穿对象的线速度矢量。
一旦你已经确定在一些点在帧期间,发生了一个路口,就可以执行二进制半步操作,基于以下出发点:确定该帧中第一穿透了顶点:
vec3 vertex;
float mindot = FLT_MAX;
for ( vert : vertices )
{
if (dot(vert, MTV) < mindot)
{
mindot = dot(vert, MTV);
vertex = vert;
}
}
一旦确定了顶点,二分之一半步就变得便宜得多:
//mindistance is the where the reference edge/plane intersects it's own normal.
//The max dot product of all vertices in B along the MTV will get you this value.
halfstep = 1.0f;
vec3 cp = vertex;
vec3 v = A.velocity*framedurationSeconds;
float errorThreshold = 0.01f; //choose meaningful value here
//alternatively, set the while condition to be while halfstep > some minimum value
while (abs(dot(cp,normal)) > errorThreshold)
{
halfstep*=0.5f;
if (dot(cp,normal) < mindistance) //cp is inside the object, move backward
{
cp += v*(-1*halfstep);
}
else if ( dot(cp,normal) > mindistance) //cp is outside, move it forward
{
cp += v*(halfstep);
}
}
return cp;
这是相当准确的,但在单个情况下将仅提供单个碰撞点。
事实是,通常可以提前告知一个对象是否每帧移动得足够快,从而能够像这样进行隧穿,所以最好的建议是沿着速度识别前导顶点并沿着速度矢量进行射线测试。在旋转物体的情况下,您必须执行某种二进制的半步跟踪,以声明正确的接触点。
但是,在大多数情况下,可以安全地假定场景中的大多数对象移动得不够快,无法在单个帧中穿透那么远,因此不需要半步步进,并且离散碰撞检测就足够了。移动速度太快以至于看不见的高速物体(如子弹)可以被追踪到接触点。
有趣的是,这种半步方法还可以为您提供(几乎)对象在帧中发生的确切时间:
float collisionTime = frametimeSeconds * halfstep;
如果您要进行某种物理冲突解决,则可以通过以下方法校正A的位置:
v - (v*halfstep)
那么您就可以从那里正常进行物理学了。不利的一面是,如果对象运动得相当快,您将看到它沿其速度矢量向后移动。