01
12

https://dlemrcnd.tistory.com/91

 

DirectX11 3D - 반직선, 마우스 피킹, AABB 오브젝트 충돌처리 (Ray, Mouse Picking)

3D 정점은 로컬 -> 월드 -> 뷰 -> 투영 -> 화면 좌표로 최종 변환이 된다. 마우스 피킹에서는 화면 좌표 -> 투영 - > 뷰 -> 월드 역순으로 돌아와야 한다. 화면 좌표계에서 있는 마우스 클릭, 그 위치에

dlemrcnd.tistory.com

이전 DX 프로젝트에서 AABB 충돌 구현을 했었다. 최근에 다시 작업할 일이 있어서 리마인드 겸 정리함

AABB는 (Axis-Aligned Bounding Box)

월드 좌표축 XYZ가 정렬된 박스로 회전이 없고 계산이 빠르다. 브로드 페이즈에 쓰이곤한다고 한다.
Broad Phase -> 충돌했을 가능성이 낮은 pair를 골라내는 과정을 말한다.

struct AABB
{
    DD_Vector3 minPoint; // (x-, y-, z-)
    DD_Vector3 maxPoint; // (x+, y+, z+)
};

AABB vs AABB 충돌 판정은 아래와 같다.

bool IntersectAABB(const AABB& a, const AABB& b)
{
    if (a.maxPoint.x < b.minPoint.x || a.minPoint.x > b.maxPoint.x) return false;
    if (a.maxPoint.y < b.minPoint.y || a.minPoint.y > b.maxPoint.y) return false;
    if (a.maxPoint.z < b.minPoint.z || a.minPoint.z > b.maxPoint.z) return false;
    return true;
}

각 min max에 교차할 수 있는지 교차한다면 충돌로 판정한다. 비교적 간단하다. 


OBB (Oriented Bounding Box)

자기 자신의 로컬 축을 가지는 박스로 회전이 있다.
SAT (Separating Axos Theorem) 기반 판정을 한다.

출처 : https://kwonvector.tistory.com/59


두 개의 볼록 도형(Convex Shape)이 충돌하지 않는다면
반드시 두 도형을 완전히 분리할 수 있는 축(Separating Axis)이 하나 이상 존재한다.

즉, 어떤 축 하나라도 두 도형을 그 축에 투영했을 때 투영된 구간이 겹치지 않으면
→ 두 도형은 충돌하지 않는다

반대로 말하면, 모든 후보 축에서 투영 결과가 겹친다면
→ 충돌 상태라고 판단할 수 있다.

OBB vs OBB 충돌에서는 다음 3가지 종류의 축을 모두 검사해야 한다.

1. 첫 번째 OBB의 로컬 축 (3개)
Right, Up, Forward

2. 두 번째 OBB의 로컬 축 (3개)
Right, Up, Forward

3. 두 OBB의 로컬 축 간 외적(Cross Product) 축 (9개)
이 부분이 SAT에서 가장 중요한 핵심이다.
두 박스가 서로 회전된 상태일 때,
각자의 면(face) 기준 축으로는 겹쳐 보이지만.. 
실제로는 엣지(edge)끼리 엇갈려 충돌하지 않는 경우가 존재한다.

이 경우 분리축은
A 박스의 축 × B 박스의 축
즉, 엣지와 엣지 사이에 수직인 방향이 된다.
첫 번째 OBB 축이 3개, 두 번째 OBB 축도 3개이므로
외적 조합은 다음과 같이 총 9개가 된다.

3 × 3 = 9

총합 15의 축이 필요하게 됨

이 15개의 축 중
단 하나라도 투영 결과가 겹치지 않으면 → 충돌이 아니다.
모든 축에서 겹친다면 → 충돌 상태로 판단한다.

struct OBB
{
    DD_Vector3 centerPoint; // 중심점
    DD_Vector3 axis[3];    // 정규화된 로컬 축 (Right, Up, Forward)
    DD_Vector3 halfSize;   // 각 축 방향 반길이
};

 

// ========================================
// OBB vs OBB Collision (SAT - Single Function)
// ========================================
bool IntersectOBB(const OBB& a, const OBB& b)
{
    // 두 OBB 중심점 차이 벡터 - 중심 거리 > (A 투영 길이 + B 투영 길이)
    Vec3 centerDiff = b.center - a.center;

    // SAT에서 검사할 최대 15개의 분리축
    Vec3 axes[15];
    int axisCount = 0;

    // ----------------------------------------
    // 1️. a 로컬 축 (3개)
    // ----------------------------------------
    for (int i = 0; i < 3; ++i)
        axes[axisCount++] = a.axis[i];

    // ----------------------------------------
    // 2. b 로컬 축 (3개)
    // ----------------------------------------
    for (int i = 0; i < 3; ++i)
        axes[axisCount++] = b.axis[i];

    // ----------------------------------------
    // 3️. 두 OBB 로컬 축의 외적 (최대 9개)
    // ----------------------------------------
    for (int i = 0; i < 3; ++i)
    {
        for (int j = 0; j < 3; ++j)
        {
            Vec3 cross = Cross(a.axis[i], b.axis[j]);

            // 축이 거의 0이면 (서로 평행) 의미 없음
            if (cross.LengthSq() > DD_SMALL_NUMBER)
                axes[axisCount++] = Normalize(cross);
        }
    }

    // ----------------------------------------
    // 모든 축에 대해 SAT 검사
    // ----------------------------------------
    for (int i = 0; i < axisCount; ++i)
    {
        const Vec3& axis = axes[i];

        // 중심점 거리 (두 OBB 중심을 축에 투영)
        float distance = fabs(Dot(centerDiff, axis));

        // OBB A를 축에 투영한 반 길이
        float projectionA =
            fabs(Dot(a.axis[0], axis)) * a.halfSize.x +
            fabs(Dot(a.axis[1], axis)) * a.halfSize.y +
            fabs(Dot(a.axis[2], axis)) * a.halfSize.z;

        // OBB B를 축에 투영한 반 길이
        float projectionB =
            fabs(Dot(b.axis[0], axis)) * b.halfSize.x +
            fabs(Dot(b.axis[1], axis)) * b.halfSize.y +
            fabs(Dot(b.axis[2], axis)) * b.halfSize.z;

        if (distance > (projectionA + projectionB))
        {
            // 하나라도 분리되는 축이 있으면 충돌 아님
            return false;
        }
    }

    // 모든 축에서 겹침 → 충돌
    return true;
}

충돌 처리된 오브젝트들은 처리는 아래와 같이 작업했다.

 

MTV(Minimum Translation Vector) 연산 및 도형 이동

두 도형사이의 관계가 서로 겹쳐진 관계라면 이제 겹쳐진 길이만큼 도형을 이동시켜야 한다. 
도형을 이동시키기 위해 MTV를 구해서 이를 도형 이동에 적용해야 한다. 
여기서 MTV는 겹쳐진 도형을 겹쳐지지 않은 상태가 되기 위한 최소한의 벡터를 말한다. 
OBB에서는 SAT에서 계산된 각 분리축의 투영 겹침 길이 중 가장 작은 값을 가지는 축이 MTV를 결정한다.

MTV의 방향 결정은 노말 즉, 부딪힌 오브젝트의 반대방향을 반환해서 이동시킨다.

COMMENT