v = (v가 v1와 얼마나 떨어졌는가) * (v0) + (v가 v0와 얼마나 떨어졌는가) * (v1)
으로 표현할 수 있다.
이를 그래프로 그려보면 다음과 같이 선형(linear) 그래프가 나오는 것을 알 수 있다.
다시 무게 중심 좌표계로 돌아와서...
이제 두 점이 아닌 세 개의 점 사이의 어떤 값에 대해 이야기 해 보자.
세 점 중간 어딘가에 있는 한 점의 위치는 다음 그림처럼 주어진 세 점을 이어 삼각형을 그리고 그 삼각형을 세개로 쪼개서 각 삼각형의 넓이를 이용해서 구할 수 있다.
예를들어 v0 와 얼마나 가까운가? v0와 가까울수록 v0에 가까운 값이 나올 것이다. 어떤 점 건너편에 있는 삼각형(a0)의 넓이에 비례해서 v0와 가까울 것이다. 즉, a0의 넓이가 클수록 v0에 가깝다.
각 삼각형의 넓이는 cross product 를 이용해 구한다.
팁이 있다면, 마지막 weight는 이미 구한 2개의 weight를 이용해서 구하면 계산량이 줄어든다.
v0, v1, v2의 색이 정해져 있고, 그 사이의 색깔도 같은 방식으로 구할 수 있다.
// 참고: https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/ray-triangle-intersection-geometric-solution
bool IntersectRayTriangle(const vec3 &orig, const vec3 &dir,
const vec3 &v0, const vec3 &v1,
const vec3 &v2, vec3 &point, vec3 &faceNormal,
float &t, float &w0, float &w1)
{
/*
* 기본 전략
* - 삼각형이 놓여있는 평면과 광선의 교점을 찾고,
* - 그 교점이 삼각형 안에 있는지 밖에 있는지를 판단한다.
*/
/* 1. 삼각형이 놓여 있는 평면의 수직 벡터 계산 */
faceNormal = glm::normalize(glm::cross(v1 - v0, v2 - v0));
//주의: 삼각형의 넓이가 0일 경우에는 계산할 수 없음
// 삼각형 뒷면을 그리고 싶지 않은 경우 (Backface culling)
if (dot(-dir, faceNormal) < 0.0f)
return false;
// 평면과 광선이 수평에 매우 가깝다면 충돌하지 못하는 것으로 판단
if (glm::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 계산
// 방향만 확인하면 되기 때문에 normalize() 생략 가능
const vec3 cross0 = glm::cross(point - v2, v1 - v2);
const vec3 cross1 = glm::cross(point - v0, v2 - v0);
const vec3 cross2 = glm::cross(v1 - v0, point - v0);
if (dot(cross0, faceNormal) < 0.0f)
return false;
if (dot(cross1, faceNormal) < 0.0f)
return false;
if (dot(cross2, faceNormal) < 0.0f)
return false;
// Barycentric coordinates 계산
// 텍스춰링(texturing)에서 사용
// 아래에서 cross product의 절대값으로 작은 삼각형들의 넓이 계산
// 삼각형의 넓이 구하는데 *0.5는 생략가능. 어차피 비율만 따질거라서
const float area0 = glm::length(cross0);
const float area1 = glm::length(cross1);
const float area2 = glm::length(cross2);
const float areaSum = area0 + area1 + area2;
// const float areaSum = glm::length(glm::cross(v1 - v0, v2 - v0)) * 0.5f; // 이렇게도 구할 수 있음
// 기호에 alpha, beta, gamma 또는 u, v, w 등을 사용하기도 함
w0 = area0 / areaSum;
w1 = area1 / areaSum;
// w2 = area2 / areaSum 또는 점이 삼각형 안에 있는것이 확실하다면 w2 = 1.0f - w0 - w1 도 가능
return true;
}