[DX] 법선맵 (Normal Map)

김진우·2025년 7월 27일

DirectX

목록 보기
29/32
post-thumbnail

정의

법선맵(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으로 표현된 사진이다.

DirectX에서 법선맵 적용

  1. 셰이더에서 노멀맵 샘플링

    float3 normalMapSample = texNormalMap.Sample(sampler, texCoord).rgb;
  2. RGB → 벡터 변환

    float3 normalTangent = normalize(normalMapSample * 2.0f - 1.0f); // [0,1] → [-1,1]
  3. Tangent Space → World Space 변환

    float3x3 TBN = float3x3(tangent, bitangent, normal); // tangent basis
    float3 normalWorld = mul(normalTangent, TBN);
  4. 조명 연산에 사용

    float lightIntensity = max(dot(normalWorld, lightDir), 0);

Lighting 적용을 위한 공간 변환

노멀맵은 Tangent Space에 정의되어 있으므로 월드/뷰 공간으로 변환해야 조명 계산에 쓸 수 있다.

이 과정에서 [0, 1] 범위를 가지고 있는 노멀맵의 RGB는 실제 벡터의 [-1, 1] 범위로 변환이 되어야 한다.

1. 노멀맵 샘플링

float3 normalSample = texNormalMap.Sample(sampler, input.TexCoord).rgb;

2. 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]로 변환된다.

3. TBN 행렬 구성

float3 T = normalize(input.Tangent);
float3 N = normalize(input.Normal);
float3 B = normalize(cross(N, T)); // Bitangent는 보통 cross로 생성

float3x3 TBN = float3x3(T, B, N);  // 각 벡터는 열 벡터
  • 이 행렬은 Tangent Space를 World Space로 변환하는 역할을 한다.
  • 경우에 따라, Tangent/Bitangent/Normal은 월드 공간에서 주어지기도 한다.

4. 벡터 변환: Tangent → World

float3 normalWS = mul(normalTS, TBN);  // normalTS는 tangent space에서의 벡터
  • mul()은 행렬-벡터 곱이다.
  • 이 결과가 World Space에서의 실제 법선 방향이 된다.

0개의 댓글