欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

游戏碰撞检测 AABB包围体 sphere 圆形包围体计算

程序员文章站 2022-03-13 13:47:46
...
包围体Bounding Volumes

有时候我们想要计算一个mesh模型的Bounding Volume。一般使用圆形和AABB。如下图,左边AABB,右边圆形:

游戏碰撞检测 AABB包围体 sphere 圆形包围体计算

圆形是由它的圆心和半径确定其位置,AABB是有其最小和最大顶点确定位置。

使用包围体的原因是可以只测试一个图形就确定这个包围体里面的所有meshes的碰撞状态。
如果希望更加准确的碰撞,那么就可以测试到包围体重合之后,就测试其meshes中的三角形是否重合,重合即碰撞。三角形(ray/triangle intersection)测试会很消耗时间。

我们可以手工计算AABB和圆形包围体,但是Direc3D的D3DX库里面提供了自动计算一个mehs模型的AABB和圆形的。只要知道其顶点数组,那么就可以计算了。

HRESULT D3DXComputeBoundingSphere(
      LPD3DXVECTOR3 pFirstPosition,
      DWORD NumVertices,
      DWORD dwStride,
      D3DXVECTOR3* pCenter,
      FLOAT* pRadius
);

dwStride: 这个参数有点难理解,就是可以定义其往前跳跃的float数,比如顶点的位置格式是FLOAT3,那么就是占3个float位置,如果带了normal单位法向量信息格式FLOAT3,那么也是占3个float位置,但是计算bounding volume不需要normal的信息,只需要位置信息,那么就可以跳过normal的信息,所以设置dwStride为6。

AABB:

HRESULT D3DXComputeBoundingBox(
      LPD3DXVECTOR3 pFirstPosition,
      DWORD NumVertices,
      DWORD dwStride,
      D3DXVECTOR3* pMin,
      D3DXVECTOR3* pMax
);

一些特别的常量:

const float INFINITY = FLT_MAX;//float的最大值
const float EPSILON = 0.001f;//少于这个数的就当做0.不直接设置为零,因为计算机表示浮点数本来就不是概念中那么精确的。
 

如下使用EPSILON::

bool Equals(float lhs, float rhs)
{
      // if lhs == rhs their difference should be zero
      return fabs(lhs - rhs) < EPSILON ? true : false;
}


Bounding Volume类型

可以如下自定义类:

struct AABB
{
      // Initialize to an infinitely small AABB.
      AABB()
            : minPt(INFINITY, INFINITY, INFINITY),
              maxPt(-INFINITY, -INFINITY, -INFINITY){}

      D3DXVECTOR3 center()
      {
            return 0.5f*(minPt+maxPt);
      }

      D3DXVECTOR3 minPt;
      D3DXVECTOR3 maxPt;
};

struct BoundingSphere
{
      BoundingSphere()
            : pos(0.0f, 0.0f, 0.0f), radius(0.0f){}

      D3DXVECTOR3 pos;
      float radius;
};

碰撞包围盒例子:

LoadXFile("skullocc.x", &mMesh, mMtrl, mTex);//自动转换mMesh顶点属性为(position, normal, texture)带位置单位法向量和纹理的格式。
D3DXMatrixIdentity(&mWorld);

// Compute the bounding box.
VertexPNT* v = 0;
HR(mMesh->LockVertexBuffer(0, (void**)&v));

HR(D3DXComputeBoundingBox(&v[0].pos, mMesh->GetNumVertices(),
      sizeof(VertexPNT), &mBoundingBox.minPt, &mBoundingBox.maxPt));//是用sizeof计算其往前跳跃数

HR(mMesh->UnlockVertexBuffer());

float width  = mBoundingBox.maxPt.x - mBoundingBox.minPt.x;
float height = mBoundingBox.maxPt.y - mBoundingBox.minPt.y;
float depth  = mBoundingBox.maxPt.z - mBoundingBox.minPt.z;

// Build a box mesh so that we can render the bounding box visually.
HR(D3DXCreateBox(gd3dDevice, width, height, depth, &mBox, 0));

mBoxID3DXMesh 对象,存储一个由AABB为边的四方形,然后可以把这个四方形显示出来,当然一般AABB都只是用作碰撞检测,不会直接显示出来的。下面为了显示这个AABB,需要额外功夫去渲染:

transforme mBox.

D3DXVECTOR3 center = mBoundingBox.center();
      D3DXMatrixTranslation(&mBoundingBoxOffset,
            center.x, center.y, center.z);

定义材质:

// Define the box material--make semi-transparent.
mBoxMtrl.ambient   = D3DXCOLOR(0.0f, 0.0f, 1.0f, 1.0f);
mBoxMtrl.diffuse   = D3DXCOLOR(0.0f, 0.0f, 1.0f, 0.5f);
mBoxMtrl.spec      = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);
mBoxMtrl.specPower = 8.0f;

下面是渲染这个AABB:

// Draw the bounding box with alpha blending.
HR(gd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true));
HR(gd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA));
HR(gd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA));
HR(mFX->SetMatrix(mhWVP, &(mBoundingBoxOffset*mView*mProj)));
D3DXMatrixInverse(&worldInvTrans, 0, &mBoundingBoxOffset);
D3DXMatrixTranspose(&worldInvTrans, &worldInvTrans);
HR(mFX->SetMatrix(mhWorldInvTrans, &worldInvTrans));
HR(mFX->SetMatrix(mhWorld, &mBoundingBoxOffset));
HR(mFX->SetValue(mhMtrl, &mBoxMtrl, sizeof(Mtrl)));
HR(mFX->SetTexture(mhTex, mWhiteTex));
HR(mFX->CommitChanges());
HR(mBox->DrawSubset(0));
HR(gd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false));

总结:

物理碰撞是游戏的一个方向,其数学也非常复杂,但是这些简单的AABB也可以应付不少游戏任务了。