https://en.wikipedia.org/wiki/Slab_method

참고블로그 : https://ingun37.wordpress.com/2013/06/20/선육면체-충돌처리-slabs-methodraybox-intersection/

Slab Method 라는 알고리즘 사용

초록선은 충돌이 생기는 케이스, 빨간선은 충돌이 생기지 않는 케이스

초록선은 충돌이 생기는 케이스, 빨간선은 충돌이 생기지 않는 케이스

Slab이란?

$t_{enter}$는 $t_{x_{min}}$ 이고,  $t_{exit}$는 $t_{y_{max}}$인 상황

$t_{enter}$는 $t_{x_{min}}$ 이고, $t_{exit}$는 $t_{y_{max}}$인 상황

    bool RaycastAABB(const Ray& ray, const AABBCollider* aabb, float maxDistance, float& outDistance) {
        // AABB의 좌상단 기준 좌표와 크기를 이용해 최소/최대 좌표 계산
        Vector2<float> aabbMin(static_cast<float>(aabb->GetX()), static_cast<float>(aabb->GetY()));
        Vector2<float> aabbMax(static_cast<float>(aabb->GetX() + aabb->GetWidth()),
            static_cast<float>(aabb->GetY() + aabb->GetHeight()));

        // 레이 원점이 AABB 내부에 있으면 즉시 충돌로 처리
        if (ray.origin.x >= aabbMin.x && ray.origin.x <= aabbMax.x &&
            ray.origin.y >= aabbMin.y && ray.origin.y <= aabbMax.y) {
            outDistance = 0.0f;
            return true;
        }

        /*
        Slab Test
        핵심 개념: 각 축(x, y)에서 박스의 최소/최대 평면과 광선이 교차하는 매개변수 t를 구하는 것
        직선의 매개변수 방정식 : P(t) = O + tD
         - P(t): 광선 위의 한 점
         - O: 광선의 시작점 (ray.origin)
         - D: 광선의 방향 벡터 (ray.direction)
         - t: 광선의 진행 매개변수 (ray가 진행하는 거리 비율)

        각 축의 AABB 평면과 교차하는 t값을 구하기   (위의 수식을 t에 대해 정리)
            tmin = (AABB_Min - ray.origin) / ray.direction
            tmax = (AABB_Max - ray.origin) / ray.direction
        tmin과 tmax는 교차 구간을 정의하며, 모든 축에서 공통 구간이 존재하면 충돌.

        변수명에서 t는 위 수식에서의 매개변수 t를 의미.

        */

        float txmin, txmax, tymin, tymax;

        // x 축 처리: Slab Test를 통해 광선과 AABB의 x축 평면 교차 여부를 계산 (Kay-Kajiya test)
        // 방향 벡터의 x 성분이 0인 경우, slab 교차 여부를 직접 계산할 수 없으므로 별도로 처리

        if (ray.direction.x != 0.0f) {
            // P(t) = O + tD 의  t값을 유도
            // t = ( P(t) - O ) / D    수식을 그대로 적은 것
            
            txmin = (aabbMin.x - ray.origin.x) / ray.direction.x;       // AABB Box의 x의 최솟값일 때의 t값 
            txmax = (aabbMax.x - ray.origin.x) / ray.direction.x;      // AABB Box의 y의 최솟값일 때의 t값
						
						// txmin > txmax인 경우는 Ray가 우측에서 오는 경우
            // swap에 의해 뒷 부분의 t_enter는 항상 왼쪽에서 시작하는 것으로 바꿔서 고려
            
            if (txmin > txmax)
            {
                std::swap(txmin, txmax);
            }
        }

        else {
            // x축 성분이 0인데 시작점의 x가 AABB 박스의 x범위를 벗어난다면 false
            // 왜냐하면 y축성분이 얼마든지 간에 평행하게 됨
            if (ray.origin.x < aabbMin.x || ray.origin.x > aabbMax.x)
                return false;

            // 값을 각각 음수 무한대, 양수 무한대로 두어 해당 값은 고려하지 않도록 함
            // 즉 해당 축에 대해서는 Slab 교차여부를 판정하지 않음
            txmin = -FLT_MAX;
            txmax = FLT_MAX;
        }

        // y 축 처리
        if (ray.direction.y != 0.0f) {

            // P(t) = O + tD 의  t값을 유도
            // t = ( P(t) - O ) / D    수식을 그대로 적은 것

            tymin = (aabbMin.y - ray.origin.y) / ray.direction.y;       // AABB Box의 y의 최솟값일 때의 t값 
            tymax = (aabbMax.y - ray.origin.y) / ray.direction.y;      // AABB Box의 y의 최댓값일 때의 t값 

						// tymin > tymax인 경우는 Ray가 위쪽에서 오는 경우
            // swap에 의해 뒷 부분의 t_enter는 항상 아래쪽에서 시작하는 것으로 바꿔서 고려
            
            if (tymin > tymax) {
                std::swap(tymin, tymax);
            }
        }
        else {

            // y축 성분이 0인데 시작점의 y가 AABB 박스 범위의 y를 벗어난다면 false
            // 왜냐하면 x축성분이 얼마든지 간에 평행하게 됨
            if (ray.origin.y < aabbMin.y || ray.origin.y > aabbMax.y)
                return false;

            // 값을 각각 음수 무한대, 양수 무한대로 두어 해당 값은 고려하지 않도록 함
            // 즉 해당 축에 대해서는 Slab 교차여부를 판정하지 않음
            tymin = -FLT_MAX;
            tymax = FLT_MAX;
        }

        // 두 축의 교차 구간이 겹치지 않으면 충돌 없음
        if ((txmin > tymax) || (tymin > txmax))
            return false;

        // 위에서 swap 처리를 하였기에 아래 로직이 항상 성립
        
        // tmin: AABB에 진입하는 순간의 매개변수 t값 (즉, AABB를 처음으로 만나는 지점)
        // tmax: AABB에서 이탈하는 순간의 매개변수 t값 (즉, AABB를 마지막으로 만나는 지점)

        float t_min, t_max;

        // 최종 tmin, tmax 결정
        t_min = std::max<float>(txmin, tymin);
        t_max = std::min<float>(txmax, tymax);

        // Ray가 AABB 뒤쪽에서 시작하는 경우
        if (t_max < 0)
            return false;

        // 충돌이 maxDistance 이내인지 확인
        if (t_min <= maxDistance) {
            outDistance = t_min;
            return true;
        }

        return false;
    }