[OpenGL] - TBN

gest·6일 전

OpenGL

목록 보기
6/11

랜덤으로 지형을 만들다 보니까 지형(terrain)이 가까이서 보면 너무 평평해 보인다.

단순히 텍스처만 입혀놓으니 입체감이 전혀 안 느껴져서, TBN 행렬을 이용해 제대로 된 노멀 매핑(Normal Mapping)을 적용해 보기로 했다.


Tangent Space, TBN 행렬

멀 맵(Normal Map) 이미지를 그대로 지형에 갖다 바른다고 해서 우리가 원하는 입체감이 튀어나오지는 않는다. 왜냐하면 노멀 맵 안에 저장된 법선(Normal) 벡터들은 표면이 평평하다는 가정하에 만들어진 접선 공간(Tangent Space) 기준이기 때문이다.

빛 반사를 제대로 계산하려면 이 접선 공간에 있는 노멀 벡터들을 우리가 실제로 계산을 수행하는 월드 공간(World Space)으로 변환해 줘야 한다. 이때 사용하는 변환 행렬이 바로 TBN 행렬이다.

T (Tangent): 텍스처의 U(가로) 좌표 방향과 일치하는, 표면에 접하는 벡터

B (Bitangent): 텍스처의 V(세로) 좌표 방향과 일치하는, T와 직교하며 표면에 접하는 벡터

N (Normal): 표면과 수직을 이루는 법선 벡터

이 세 개의 벡터를 각각 열(Column)로 삼아서 만든 3x3 행렬이 TBN 행렬이다.

TBN = [ T | B | N ] (3x3 행렬)
결과적으로 텍스처에서 읽어온 접선 공간의 노멀 값에 이 TBN 행렬을 곱해주면, 월드 공간 기준의 알맞은 노멀 벡터로 변환된다!

디버깅 일화

적용하는 과정에서 빛을 정면으로 받는 부분이 밝아야 하는데 오히려 어둡게 나오는 기이한 버그가 있었다. 알고 보니 외적(Cross Product) 순서가 꼬여서 Normal 벡터가 위쪽이 아닌 아래쪽을 향해버린 것이 원인이었다.

glm::vec3 n1 = glm::normalize(glm::cross(
    positions[i01] - positions[i00],
    positions[i10] - positions[i00]));

//아래 벡터를 나타냄
//positions[i10] - positions[i00],
//positions[i01] - positions[i00]
    
glm::vec3 n2 = glm::normalize(glm::cross(
    positions[i01] - positions[i10],
    positions[i11] - positions[i10]));
    
//아래 벡터를 나타냄
//positions[i11] - positions[i10],
//positions[i01] - positions[i10]

결과


눈 특유의 재질이 느껴진다.

0개의 댓글