游戏碰撞检测 AABB包围体 sphere 圆形包围体计算
程序员文章站
2022-03-13 13:47:46
...
包围体Bounding Volumes
有时候我们想要计算一个mesh模型的Bounding Volume。一般使用圆形和AABB。如下图,左边AABB,右边圆形:
圆形是由它的圆心和半径确定其位置,AABB是有其最小和最大顶点确定位置。
如果希望更加准确的碰撞,那么就可以测试到包围体重合之后,就测试其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));
mBox是 ID3DXMesh 对象,存储一个由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也可以应付不少游戏任务了。
上一篇: 一道时间复杂度算法题
下一篇: 数据结构-散列表-图