분리축 이론 : 다면체 A, B에 대해서 어떤 축이 존재해서 그 축으로의 다면체들의 투영된 구간이 "하나라도" 서로 겹치지 않는다면 A, B는 서로 분리되어 있다는 이론
bool CCollision::CollisionBox3DToBox3D(CollisionResult& SrcResult, CollisionResult& DestResult, const Box3DInfo& boundingBox, const Box3DInfo& targetBox)
{
double c[3][3];
double absC[3][3];
double d[3];
double r0, r1, r;
int i;
const double cutoff = 0.999999;
bool existsParallelPair = false;
Vector3 diff = boundingBox.Center - targetBox.Center;
// 분리축 : boundingBox의 x축
for (i = 0; i < 3; ++i)
{
//targetBox의 3축에 대해서 boundingBox의 x축으로 정사영 내려서 boundingBox의 x축으로의 거리를 추출
c[0][i] = boundingBox.Axis[0].Dot(targetBox.Axis[i]);
absC[0][i] = abs(c[0][i]);
// 평행 여부를 검사하는 이유는 만약에 boundingBox와 targetBox가 회전한 축의 종류가 같아서 평행할 때
// (ex. boundingBox와 targetBox 모두 Y축 기준으로만 회전한 상태라면 두 Box의 Y축끼리는 평행할 것임)
// 꼭지점만 맞닿아서 충돌하는 경우라면 외적해서 나온 9개의 새로운 축을 고려하지 않고도 충돌인 것을 확인할 수 있으므로
// existParallelPair가 한번이라도 true이고, bounding/targetBox의 각각의 x,y,z축(총 6개)만 고려해서
// 충돌 여부를 판단할 수 있기 때문이다
if (absC[0][i] > cutoff)
existsParallelPair = true;
}
// 두 Box의 중심사이의 거리가 boundingBox의 x축 성분에 정사영내리면
// 두 Box의 중심사이의 거리중 boundingBox의 x축 성분에 대한 길이(스칼라)를 얻을 수 있다
d[0] = diff.Dot(boundingBox.Axis[0]);
r = abs(d[0]);
r0 = boundingBox.AxisLen[0];
r1 = targetBox.AxisLen[0] * absC[0][0] + targetBox.AxisLen[1] * absC[0][1] + targetBox.AxisLen[2] * absC[0][2];
// r(= boundingBox의 x축에 대한 두 Box의 중심 거리)와
// r0(= boundingBox의 x축에 대한 길이)
// r1(= targetBox의 3축에 대해서 boundingBox의 x축으로 정사영 내려서 boundingBox의 x축으로의 거리를 추출)
if (r > r0 + r1)
{
return false;
}
// 분리축 : Src의 y축
for (i = 0; i < 3; ++i)
{
c[1][i] = boundingBox.Axis[1].Dot(targetBox.Axis[i]);
absC[1][i] = abs(c[1][i]);
if (absC[1][i] > cutoff)
existsParallelPair = true;
}
//d[1] = diff.Dot(targetBox.Axis[1]);
d[1] = diff.Dot(boundingBox.Axis[1]);
r = abs(d[1]);
r0 = boundingBox.AxisLen[1];
r1 = targetBox.AxisLen[0] * absC[1][0] + targetBox.AxisLen[1] * absC[1][1] + targetBox.AxisLen[2] * absC[1][2];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Src의 z축
for (i = 0; i < 3; ++i)
{
c[2][i] = boundingBox.Axis[2].Dot(targetBox.Axis[i]);
absC[2][i] = abs(c[2][i]);
if (absC[2][i] > cutoff)
existsParallelPair = true;
}
d[2] = diff.Dot(boundingBox.Axis[2]);
r = abs(d[2]);
r0 = boundingBox.AxisLen[2];
r1 = targetBox.AxisLen[0] * absC[2][0] + targetBox.AxisLen[1] * absC[2][1] + targetBox.AxisLen[2] * absC[2][2];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Dest의 x축
r = abs(diff.Dot(targetBox.Axis[0]));
r0 = boundingBox.AxisLen[0] * absC[0][0] + boundingBox.AxisLen[1] * absC[1][0] + boundingBox.AxisLen[2] * absC[2][0];
r1 = targetBox.AxisLen[0];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Dest의 y축
r = abs(diff.Dot(targetBox.Axis[1]));
r0 = boundingBox.AxisLen[0] * absC[0][1] + boundingBox.AxisLen[1] * absC[1][1] + boundingBox.AxisLen[2] * absC[2][1];
r1 = targetBox.AxisLen[1];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Dest의 z축
r = abs(diff.Dot(targetBox.Axis[2]));
r0 = boundingBox.AxisLen[0] * absC[0][2] + boundingBox.AxisLen[1] * absC[1][2] + boundingBox.AxisLen[2] * absC[2][2];
r1 = targetBox.AxisLen[2];
if (r > r0 + r1)
{
return false;
}
if (existsParallelPair == true)
{
return true;
}
// 분리축 : Cross(Src x축, Dest x축)
r = abs(d[2] * c[1][0] - d[1] * c[2][0]);
r0 = boundingBox.AxisLen[1] * absC[2][0] + boundingBox.AxisLen[2] * absC[1][0];
r1 = targetBox.AxisLen[1] * absC[0][2] + targetBox.AxisLen[2] * absC[0][1];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Cross(Src x축, Dest y축)
r = abs(d[2] * c[1][1] - d[1] * c[2][1]);
r0 = boundingBox.AxisLen[1] * absC[2][1] + boundingBox.AxisLen[2] * absC[1][1];
r1 = targetBox.AxisLen[0] * absC[0][2] + targetBox.AxisLen[2] * absC[0][0];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Cross(Src x축, Dest z축)
r = abs(d[2] * c[1][2] - d[1] * c[2][2]);
r0 = boundingBox.AxisLen[1] * absC[2][2] + boundingBox.AxisLen[2] * absC[1][2];
r1 = targetBox.AxisLen[0] * absC[0][1] + targetBox.AxisLen[1] * absC[0][0];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Cross(Src y축 , Dest x축)
r = abs(d[0] * c[2][0] - d[2] * c[0][0]);
r0 = boundingBox.AxisLen[0] * absC[2][0] + boundingBox.AxisLen[2] * absC[0][0];
r1 = targetBox.AxisLen[1] * absC[1][2] + targetBox.AxisLen[2] * absC[1][1];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Cross(Src y축 , Dest y축)
r = abs(d[0] * c[2][1] - d[2] * c[0][1]);
r0 = boundingBox.AxisLen[0] * absC[2][1] + boundingBox.AxisLen[2] * absC[0][1];
r1 = targetBox.AxisLen[0] * absC[1][2] + targetBox.AxisLen[2] * absC[1][0];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Cross(Src y축 , Dest z축)
r = abs(d[0] * c[2][2] - d[2] * c[0][2]);
r0 = boundingBox.AxisLen[0] * absC[2][2] + boundingBox.AxisLen[2] * absC[0][2];
r1 = targetBox.AxisLen[0] * absC[1][1] + targetBox.AxisLen[1] * absC[1][0];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Cross(Src z축 , Dest x축)
r = abs(d[1] * c[0][0] - d[0] * c[1][0]);
r0 = boundingBox.AxisLen[0] * absC[1][0] + boundingBox.AxisLen[1] * absC[0][0];
r1 = targetBox.AxisLen[1] * absC[2][2] + targetBox.AxisLen[2] * absC[2][1];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Cross(Src z축 , Dest y축)
r = abs(d[1] * c[0][1] - d[0] * c[1][1]);
r0 = boundingBox.AxisLen[0] * absC[1][1] + boundingBox.AxisLen[1] * absC[0][1];
r1 = targetBox.AxisLen[0] * absC[2][2] + targetBox.AxisLen[2] * absC[2][0];
if (r > r0 + r1)
{
return false;
}
// 분리축 : Cross(Src z축 , Dest z축)
r = abs(d[1] * c[0][2] - d[0] * c[1][2]);
r0 = boundingBox.AxisLen[0] * absC[1][2] + boundingBox.AxisLen[1] * absC[0][2];
r1 = targetBox.AxisLen[0] * absC[2][1] + targetBox.AxisLen[1] * absC[2][0];
if (r > r0 + r1)
{
return false;
}
return true;
}
왜 OBB는 외적해서 나온 9개의 축에 대해 추가적으로 검사해줘야 하는가에 대해 생각해보자
분리축 이론에 의하면 두 Box를 분할할 수 있는 축이 있다면 이 축에 대해서는 반드시 (위 코드처럼) 축에 대해 정사영을 내려서 거리 체크를 해줘야 한다. 만약에 Box와 Box가 충돌하는데 모서리(Edge를 의미한다)끼리 만나서 충돌하는 경우를 생각해보자.
그러면 이렇게 충돌하기 직전 상황을 생각해보면, 위 그림에서 충돌하는 2개의 모서리(왼쪽 Box의 빨간색 모서리와 오른쪽 Box의 파란색 모서리)벡터와 자신의 Normal벡터가 수직인 평면(= 두 모서리 벡터를 포함하는 평면)으로 공간을 분할할 수 있다. 이 평면의 Normal 벡터는 BoundingBox/TargetBox의 x,y,z축과는 또 다른 분리축이다.
따라서 BoundingBox/TargetBox의 x,y,z축끼리 외적해서 나온 9개의 축에 대해서도 추가적으로 고려를 해줘야 하는 것이다.
단, AABB에서는 두 축끼리 외적을 해서 나온 축도 기존 축에 평행하니까 boundingBox의 3개의 축, targetBox의 3개의 축만 검사를 하는것이다.
Reference
https://www.gpgstudy.com/forum/viewtopic.php?t=1464
'공부 > Graphics, DirectX, 포트폴리오 구조' 카테고리의 다른 글
계산 셰이더(Compute Shader) (0) | 2022.09.27 |
---|---|
상수 버퍼, 구조화 버퍼, D3D11_BUFFER_DESC (0) | 2022.09.27 |
그림자 매핑(Shadow Mapping) (0) | 2022.09.17 |
Lighting (0) | 2022.09.13 |
ID3D11DeviceContext::Map 상황별 사용 (0) | 2022.05.29 |