-
[DirectX11] 기본 게임 도형(Sphere,AABB,OBB등)과 Raycast2024년 03월 14일
- 유니얼
-
작성자
-
2024.03.14.:03
728x90DirectX 11로 게임 엔진 아키텍처 만들기
DirectX11과 게임 엔진 아키텍처 학습 과정에서 3D 그래픽스의 기본 구성 요소와 중요한 개념 중 하나인 레이캐스팅(Raycasting)에 대해 알아보겠습니다. 레이캐스팅은 가상 환경 내에서 레이(광선)를 발사하여 해당 레이가 다른 객체와 충돌하는지를 판별하는 과정입니다. 이 방법은 그래픽스 렌더링, 물리 시뮬레이션, 인공 지능의 시야 판단 등 다양한 분야에서 활용됩니다.
참고강의 링크:
레이와 구(Sphere)의 충돌 검사
레이와 구의 충돌 검사는 레이의 시작점부터 구의 중심까지의 거리를 계산하고, 이 거리가 구의 반지름보다 짧은지 여부를 통해 충돌을 판단합니다. 코드에서는 e가 레이의 시작점에서 구의 중심까지의 벡터, rSq가 구의 반지름의 제곱, eSq는 e의 길이 제곱, a는 e와 레이의 방향 벡터의 내적, f는 실제 충돌 지점까지의 거리를 나타냅니다.
// 구와 레이의 충돌 검사 bool MathUtils::Raycast(const Sphere3D& sphere, const Ray3D& ray, OUT float& distance) { Vec3 e = sphere.position - ray.origin; // 레이의 시작점에서 구의 중심까지의 벡터 float rSq = sphere.radius * sphere.radius; // 구의 반지름의 제곱 float eSq = e.LengthSquared(); // e 벡터의 길이의 제곱 float a = e.Dot(ray.direction); // 레이 방향과 e 벡터의 내적 float bSq = eSq - (a * a); // b의 제곱(삼각형의 한 변의 제곱) float f = sqrt(rSq - bSq); // f는 삼각형의 다른 변(구의 반지름에서 b를 뺀 값) // 실제 충돌이 발생하지 않는 경우 if (rSq - (eSq - (a * a)) < 0.0f) return false; // 레이의 시작점이 구 내부에 있는 경우 if (eSq < rSq) { distance = a + f; // 구를 뚫고 나가는 지점까지의 거리 return true; } // 구 외부에서 시작하여 구에 닿지 않는 경우 distance = a - f; // 구에 가장 가까운 점까지의 거리 return false; }
레이와 AABB(Axis-Aligned Bounding Box)의 충돌 검사
AABB와 레이의 충돌 검사는 AABB를 구성하는 6개의 평면 각각에 대해 레이와의 교차점을 찾아내는 과정을 포함합니다. 이는 Cyrus-Beck 클리핑 알고리즘을 변형한 형태로 수행되며, tmin과 tmax를 통해 충돌이 발생하는지 판별합니다.
// AABB와 레이의 충돌 검사 (Cyrus-Beck clipping algorithm을 사용) bool MathUtils::Raycast(const AABB3D& aabb, const Ray3D& ray, OUT float& distance) { Vec3 min = AABB3D::GetMin(aabb); // AABB의 최소 좌표 Vec3 max = AABB3D::GetMax(aabb); // AABB의 최대 좌표 // 각 축에 대해 레이가 AABB의 두 평면(최소값, 최대값)과 만나는 t 값을 계산 float t1 = (min.x - ray.origin.x) / ray.direction.x; float t2 = (max.x - ray.origin.x) / ray.direction.x; float t3 = (min.y - ray.origin.y) / ray.direction.y; float t4 = (max.y - ray.origin.y) / ray.direction.y; float t5 = (min.z - ray.origin.z) / ray.direction.z; float t6 = (max.z - ray.origin.z) / ray.direction.z; // 가장 큰 최소 t 값(tmin)과 가장 작은 최대 t 값(tmax)을 계산 float tmin = fmaxf(fmaxf(fminf(t1, t2), fminf(t3, t4)), fminf(t5, t6)); float tmax = fminf(fminf(fmaxf(t1, t2), fmaxf(t3, t4)), fmaxf(t5, t6)); // tmax가 0보다 작으면 레이는 AABB의 뒤쪽을 향함 if (tmax < 0) return false; // tmin이 tmax보다 크면 레이는 AABB를 교차하지 않음 if (tmin > tmax) return false; // 실제 충돌 거리 계산 distance = (tmin < 0.0f) ? tmax : tmin; return true; }
레이와 평면(Plane)의 충돌 검사
레이와 평면의 충돌 검사는 레이가 평면에 평행한지, 그리고 평행하지 않다면 레이가 평면을 지나가는 지점을 찾아내는 과정을 포함합니다. 이는 레이의 방향 벡터와 평면의 법선 벡터의 내적, 그리고 레이의 시작점과 평면의 거리를 계산하여 수행됩니다.
// 평면과 레이의 충돌 검사 bool MathUtils::Raycast(const Plane3D& plane, const Ray3D& ray, OUT float& distance) { float nd = ray.direction.Dot(plane.normal); // 레이 방향과 평면의 법선의 내적 float pn = ray.origin.Dot(plane.normal); // 레이의 시작점과 평면의 법선의 내적 // nd가 0보다 크거나 같으면 레이와 평면은 평행하거나 레이가 평면에서 멀어짐 if (nd >= 0.0f) return false; // 실제 충돌 거리 계산 float t = (plane.distance - pn) / nd; if (t >= 0.0f) { distance = t; // 충돌 지점까지의 거리 return true; } return false; }
결론
레이캐스팅은 3D 그래픽스 및 게임 엔진에서 광범위하게 사용되는 기술로, 가상 환경 내의 객체 간 상호 작용을 결정하는 데 핵심적인 역할을 합니다. 본문에서 소개된 구체적인 레이캐스팅 방법들—레이와 구의 충돌, 레이와 AABB의 충돌, 그리고 레이와 평면의 충돌 검사—은 개발자가 보다 정확하고 효율적으로 3D 공간에서의 상호 작용을 구현할 수 있게 해줍니다.
반응형다음글이전글이전 글이 없습니다.댓글