- 광선(ray)은 시작점 O와 방향 벡터 D를 가진 선으로 정의됩니다. 이는 수식으로 다음과 같이 표현됩니다:P(t)=O+tDP(t) = O + tD여기서 t는 파라미터로, 광선의 시작점에서부터 어떤 위치까지를 결정하는 변수입니다. P(t)는 광선의 특정 지점입니다.
- 삼각형(triangle)은 세 개의 점으로 정의됩니다. 예를 들어, 삼각형의 세 꼭짓점을 V0, V1, V2라고 할 때, 이 삼각형은 이 세 점을 이용해 형성된 면입니다.
다음 그림과 코드를 보며 이해해봅시다.
orig는 시점, dir은 ray의 방향, v0,v1,v2는 삼각형의 세 꼭짓점, faceNormal은 삼각형 평면에서의 수직벡터입니다.
bool IntersectRayTriangle(const vec3 &orig, const vec3 &dir,
const vec3 &v0, const vec3 &v1,
const vec3 &v2, vec3 &point, vec3 &faceNormal,
float &t, float &u, float &v)
{
/*
* 기본 전략
* - 삼각형이 놓여있는 평면과 광선의 교점을 찾고,
* - 그 교점이 삼각형 안에 있는지 밖에 있는지를 판단한다.
*/
/* 1. 삼각형이 놓여 있는 평면의 수직 벡터 계산 */
faceNormal = normalize(glm::cross(v1 - v0, v2 - v0));
//주의: 삼각형의 넓이가 0일 경우에는 계산할 수 없음
// 삼각형 뒷면을 그리고 싶지 않은 경우 (Backface culling)
if (dot(-dir,faceNormal)< 0.0f) return false;
// 평면과 광선이 수평에 매우 가깝다면 충돌하지 못하는 것으로 판단
if (std::abs(dot(dir,faceNormal))< 1e-2f) return false; // t 계산시 0으로 나누기 방지
/* 2. 광선과 평면의 충돌 위치 계산 */
t = (dot(v0, faceNormal) - dot(orig, faceNormal)) / dot(dir, faceNormal);
// 광선의 시작점 이전에 충돌한다면 렌더링할 필요 없음
if (t<0.0f) return false;
point = orig + t * dir; // 충돌점
/* 3. 그 충돌 위치가 삼각형 안에 들어 있는 지 확인 */
// 작은 삼각형들 3개의 normal 계산
const vec3 normal0 = normalize(cross(point - v1, v0 - v1));
const vec3 normal1 = normalize(cross(point - v0, v2 - v0));
const vec3 normal2 = normalize(cross(point - v2, v1 - v2));
// 방향만 확인하면 되기 때문에 normalize() 생략 가능
// 아래에서 cross product의 절대값으로 작은 삼각형들의 넓이 계산
if (dot(normal0, faceNormal) < 0.0f) return false;
if (dot(normal1, faceNormal) < 0.0f) return false;
if (dot(normal2, faceNormal) < 0.0f) return false;
return true;
}
그림과 코드를 같이 보며 이해해봅시다. 여기서 헷갈릴 부분은 p가 삼각형 안에 있는지 확인할 때 인데, 왼손 법칙을 잘 이용하고, faceNormal이 정해지면 v0,v1,v2의 평면과 방향이 확실히 정해졌다는 뜻을 유의하고 생각해 봅시다.
'그래픽스 기술' 카테고리의 다른 글
[Ray Tracing] 월드좌표 도형에 텍스춰링 하는 방법 (4) | 2024.10.20 |
---|---|
[Ray Tracing] Barycentric coordinates(무게 중심 좌표계)를 통한 텍스쳐 좌표 보간 (2) | 2024.10.17 |
[그래픽스 기본 렌더링] 정투영을 원근투영으로 바꾸는 법 (3) | 2024.10.16 |
[그래픽스 기본 개념] 퐁 모델 (1) | 2024.10.15 |
[Ray Tracing] ray를 통해 구의 입체감 표현하기 (0) | 2024.10.11 |