법선맵(Normal Map)이란 압축된 x, y, z 좌표성분들을 각각 R, G, B 채널에 담겨져 표현되는 하나의 텍스처이다.
법선맵을 사용하면 표면의 미세한 디테일 표현이 가능해지고, 실제로 정점을 추가하지 않더라도 조명에 대한 굴곡 표현을 정밀하게 조작할 수 있게 하는 라이팅 트릭 기법에 사용된다.
이때, Normal Map은 Tangent Space에서 정의되어 있고, 실제 조명 계산은 World Space 또는 View Space에서 수행되므로 Tangent Space → World/View Space 변환이 필요하다.
법선(Normal) : 어떤 표면의 수직 방향을 나타내는 벡터
Tangent Space : 물체의 메시 표면 기준 좌표계
여기서 좌표축(T = x, N = y, B = z)를 제외한 화살표는 전부 법선을 표현한 것으로, 방향에 따라 R, G, B 색으로 Normal Map에서 표현된다.
T, N, B는 Tangent Space에서 사용되는 좌표계이다.
왼쪽이 일반 .png 사진이고, 오른쪽이 NomalMap으로 표현된 사진이다.
셰이더에서 노멀맵 샘플링
float3 normalMapSample = texNormalMap.Sample(sampler, texCoord).rgb;
RGB → 벡터 변환
float3 normalTangent = normalize(normalMapSample * 2.0f - 1.0f); // [0,1] → [-1,1]
Tangent Space → World Space 변환
float3x3 TBN = float3x3(tangent, bitangent, normal); // tangent basis
float3 normalWorld = mul(normalTangent, TBN);
조명 연산에 사용
float lightIntensity = max(dot(normalWorld, lightDir), 0);
노멀맵은 Tangent Space에 정의되어 있으므로 월드/뷰 공간으로 변환해야 조명 계산에 쓸 수 있다.
이 과정에서 [0, 1] 범위를 가지고 있는 노멀맵의 RGB는 실제 벡터의 [-1, 1] 범위로 변환이 되어야 한다.
float3 normalSample = texNormalMap.Sample(sampler, input.TexCoord).rgb;
float3 normalTS = normalize(normalSample * 2.0f - 1.0f);
RGB(0.5, 0.5, 1.0) → (0, 0, 1) (표면 밖 방향)2.0f * RGB - 1.0f을 통해 [0,1] → [-1,1]로 변환된다.float3 T = normalize(input.Tangent);
float3 N = normalize(input.Normal);
float3 B = normalize(cross(N, T)); // Bitangent는 보통 cross로 생성
float3x3 TBN = float3x3(T, B, N); // 각 벡터는 열 벡터
float3 normalWS = mul(normalTS, TBN); // normalTS는 tangent space에서의 벡터
mul()은 행렬-벡터 곱이다.