대표적인 OpenGL의 예제들을 살펴보면 아래와 같은 형태의 데이터를 Vertex Shader에 전달해준다.
std::vector<glm::vec2> texCoord = {
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f
}
단순하게 생각해보면 Texture를 가져온 데이터를 좌표값을 나타냈을 때, 아래처럼 생겼기에 화면에 맞추는 작업이구나 하고 넘어갈 수 있다.
left top(0.0f, 1.0f)
right top(1.0f, 1.0f)
left bottom(0.0f, 0.0f)
right bottom(1.0f, 0.0f)
하지만, 이렇게 맞춰서 변환하는 과정은 생각보다 길고, 복잡할 수 있다.
Tangent Plane
구로 예시를 들어서 말하는 것으로 하겠습니다.
구에서 임의의 한 점과 맞닿는 평면, 이 평면이 Tangent Plane이다.
또한, 이런 평면을 좌표계 기준으로 잡는 공간을 Tangent Space라고 한다.
OpenGL에서 Tangent Plane이 이 어떻게 쓰일까?
OpenGL에서는 3개의 Vertex로 이루어진 삼각형을 하나의 Tangent Space에 존재한다고 생각한다.
그래서 주어진 Vertices와 Indices를 기준으로 Tangent Space를 계산한다.
주어진 Vertices와 Indices에 따라 2개의 벡터를 생성[$E_{1}, E_{2}$]하고,
주어진 Vertices에 대한 TexCoord에 따라 u와 v를 2개씩 생성[$\begin {bmatrix} u_1 & v_1 \\ u_2 & v_2 \end {bmatrix}$]한다.
여기서 우리는 Tangent, Bitangent Vector를 구할 수 있다.
$\begin {bmatrix} E_{1x} & E_{1y} & E_{1z} \\ E_{2x} & E_{2y} & E_{2z} \end {bmatrix} = \begin {bmatrix} u_1 & v_1 \\ u_2 & v_2 \end {bmatrix} \begin {bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end {bmatrix}$
사실 이렇게 보면 잘 모르겠어서 여러 가지로 생각해봤다.
- 이 변환 과정은 한 번에 진행되는 것이 아니다.
- 여러 가지 좌표계의 변환을 통해 생긴 것.
- 단순히 변환 행렬을 구한 것이지만 여러 개의 혼합된 버전일 것.
- 해당 좌표계로 이동, 좌표계 방향에 맞춰 회전, 해당 크기로 축소/확대의 혼합
- 3차원에서 2차원으로 변환하는 것이기에 이렇게 구하는 것.
- 한 번에 변환하는 행렬을 구한 것일 듯?
결국 구하고 싶은 행렬을 우린 TB Matrix라고 생각했을 때, 생성된 uv Matrix의 역행렬과 2개의 Vector로 이뤄진 Matrix를 곱하면 구할 수 있다.
$\begin {bmatrix} T_x & T_y & T_z \\ B_x & B_y & B_z \end {bmatrix} = \begin {bmatrix} u_1 & v_1 \\ u_2 & v_2 \end {bmatrix} ^{-1} \begin {bmatrix} E_{1x} & E_{1y} & E_{1z} \\ E_{2x} & E_{2y} & E_{2z} \end {bmatrix}$
하지만 이와 같은 과정에서 TBN의 Space에서 서로가 직교하지 않을 수 있다.
그렇기 때문에 그람-슈미트 직교화를 통해 각각의 벡터가 직교하도록 만든다.
위의 방법처럼 한 번에 구하는 것이 아니라, Tangent만 구해서 Shader로 넘겨준다.
이후 Shader에서 Normal과 Tangent를 Cross Product 하면 Bitangent의 값이 나온다.