폴리곤 수를 늘리지 않고 법선 벡터를 조작해 표면 디테일을 표현하는 기법

 

핵심 개념 : 빛 반응을 속이는 원리

 

폴리곤이 적은 메시 (Low Poly)

실제 지오메트리가 평평함 -> 빛이 균열하게 반사 -> 밋밋해 보이는 효과 발생


노멀 맵 적용

지오메트리는 그대로 (폴리곤 추가 없음) -> 픽셀마다 법선 벡터(Normal Vector)를 다르게 조작 -> 빛이 마치 울퉁불퉁한 표면에서 반사된 것처럼 계산 -> 디테일이 있어보이는 효과

 

즉. 실제로 튀어나온 것은 없이 빛 계산만 속인다. 폴리고 비용 없이 디테일 표현이 가능하다

 

법선 벡터

- 표면에 수직인 방향 벡터

- 평평한 표면 : 모든 법선이 동일하여 빛 반응이 균일하고 평평해 보인다.

- 울툴불퉁한 표면 : 법선이 각각 다르기에 빛 반응이 다양하고 굴곡이 있어 보인다.

 

조명 계산

color = albedo * dot(Normal, LightDir)

dot(Normal, LightDir)의 값이 달리지면 빛 방향이 달라진다. 노멀 맵은 이 Normal을 픽셀마다 바꾸는 것이다.

 

노멀 맵이 파란색인 이유

- 노멀 맵 = 법선 벡터를 RGB 텍스처로 저장한 것

- 법선 벡터 범위 : -1 ~ +1

- 텍스처 색상 범위 : 0 ~ 1

 

변환 공식

색상 = (법선 + 1) / 2

법선 = 색상 * 2  - 1

 

평평한 표면의 법선 = (0, 0, 1)

- R = (0 + 1) / 2 = 0.5 -> 128 -> 회색

- G = (0 + 1) / 2 = 0.5 -> 128 -> 회색

- B = (1 + 1) / 2 = 1.0 -> 255 -> 파란색

-> 완전히 평평한 곳은 RGB(128, 128, 255) = 파란색. 그렇기에 노멀 맵이 파란빛이다.

 

Z축이 파란색인 이유

- 노멀 맵은 탄젠트 공간 기준

- 탄젠트 공간에서 표면 정면 = Z축 = (0, 0, 1)

- Z가 가장 큰 값(1.0 -> 파란색 255)이 기본값

 

좌표 공간

노멀 맵이 저장하는 법선의 좌표 공간

 

월드 공간 노멀 맵 (World Space)

- 글로벌 XYZ 기준으로 거장

- 오브젝트가 회전하면 잘못된 방향 가리킴 -> 움직이는 오브젝트에 부적합하다

 

탄젠트 공간 노멀 맵 (Tangent Space) <- 일반적으로 이것으 가리킨다

- 메시 표면 기준 좌표계로 저장

- T(탄젠트) + B(바이탄젠트) + N(노멀)로 구성

- 오브젝트가 어떻게 회전해도 정상 동작 -> 캐릭터, 애니메이션 메시에 적합

 

픽셀 셰이더에서 변환

- 텍스처에서 읽은 법선 ( 탄젠트 공간 )

- TBN 행렬로 월드/뷰 공간으로 변환

- 조명 계산에 사용

 

TBN 행렬 (셰이더 구현)

// 노멀 맵 픽셀 셰이더(HLSL)

Texture2D NoramlMap : register(t1);
SamplerState Sampler : register(s0);

cbuffer LightBuffer : register(b1)
{
     float3 lightDir;
     float3 lightColor;
}

float4 PS(PixelInput input) : SV_TARGET
{
     // 노멀 맵에서 법선 일기
     float3 normalSample = NormalMap.Sample(Sampler, input.texCoord).rgb;
     
     // 0 ~ 1 범위를 -1 ~ 1 범위로 변환
     float3 tangentNormal = normalSample  * 2.0 - 1.0;
     
     // TBN 행렬 구성. T, B, N은 버텍스 셰이더에서 전달받은 값
     float3 T = normalize(input.tangent);
     float3 B = normalize(input.bitangent);
     float3 N = normalize(input.normal);
     float3x3 TBN = float3x3(T, B, N);
     
     // 탄젠트 공간 -> 월드 공간으로 변환
     float3 worldNormal = normalize(mul(tangentNormal, TBN));	
     
     // 변환된 법선으로 조명 계산
     float NdotL = max(dot(worldNormal, normalize(-lightDir)), 0.0);
     float4 albedo = AlbedoMap.Sample(Sampler, input.texCoord);
     
     return float4(albedo.rgb * lightColor * NdotL, albedo.a);
}

 

노멀 맵 종류

 

노멀 맵 (Normal Map)

- 법선 방향만 저장

- 빛 반응 조작

- 가장 기본적이고 가장 많이 쓰인다.

- 파일 : _N, _Normal 접미사

 

범프 맵 (Bump Map)

- 높이값(흑백)으로 법선을 유추

- 노멀 맵보다 구형 기술

- GPU가 높이 -> 법선 변환 필요가 필요하다

- 파일 : _B, _Bump 접미사

 

하이트 맵 (Height Map)

- 높이 정보를 흑백으로 저장

- 노멀 맵 생성에 쓰거나 테셀레이션으로 실제 지오메트리 변위에 사용

 

디스플레이스먼트 맵 (Displacement Map)

- 하이트 맵 기반으로 버텍스를 실제로 이동

- 테셀레이션이 필요하다

- 실제 실루엣이 변하기에 노멀 맵보다 비용이 크다

- UE5에서 World Displacement 노드가 있다.

 

노멀 맵 vs 디스플레이스먼트 맵 

 

노멀 맵

- 폴리곤 수 변화가 없다

- 실루엣에도 변화가 없다. 여전히 가장자리는 평평하다

- 텍스처 샘플링만 쓰기에 비용이 낮다

- 가장자리에서 깨진다(silhouette problem)는 한계점이 존재하다

 

디스플레이스먼트 맵

- 폴리곤 수가 테셀레이션으로 인해 증가한다

- 실루엣이 실제로 변형된다

- 지오메트리 생성으로 인해 비용이 높다

- 실루엣까지 디테일하게 표현되는 장점이 있다.

 

평평한 바닥, 벽 -> 노멀 맵으로 충분하다

바위, 지형 실루엣 -> 디스플레이스먼트 맵

캐릭터 피부 -> 노멀 맵 + 약간의 테셀레이션

 

UE5에서 노멀 맵 작업 흐름 (제작 파이프라인)

 

하이폴리 메시(High Poly) : Blender / ZBrush에서 수백만 폴리곤으로 디테일 조각한다

 

↓ 베이킹 (Baking)

 

로우폴리 메시 + 노멀맵 : 게임에서 실제로 쓰는 메시 + 하이폴리의 디테일을 구운 노멀 맵

 

UE5 임포트

- 텍스처 설정 : Normal Map 타입 선택

- sRGB 체크 해제 ( 노멀 맵은 색상 데이터가 아니다 )

- Compression : BC5 / DXT5nm (채널 최적화)

 

머티리얼 연결

- TextureSample -> Normal 핀

- Flat Normal 노드 : (0, 0, 1) 기본값

- Normal Intensity 조절 : Lerp로 강도 제어

 

노멀 맵 최적화

 

BC5  압축 (두 채널 압축)

 

노멀 맵은 XY만 저장해도 된다. Z = sqrt(1 - X^2 - Y^2)으로 재계산 가능

-> BC5로 RG만 저장한다 -> 메모리 절약 + 품질 유지

 

셰이더에서 Z 재계산

float3 normal;

normal.xy = normalSample.rg * 2.0 - 1.0;

normal.z = sqrt(1.0 - saturate(dot(normal.xy, normal.xy)));

normal = normalize(normal);

 

sRGB 설정 주의

- 노멀 맵은 색상이 아닌 벡터 데이터

- sRGB 감마 보정을 적용하면 잘못된 법선이다

- UE5에서 반드지 sRGB 체크를 해제해야한다

 

밉맵과 노멀 맵

- 일반 텍스처 : 밉맵 = 평균값으로 다운샘플

- 노멀 맵 : 평균 -> 법선이 정규화되지 않는다. -> 각 밉맵 레벨에서 법선 재정규화가 필요하다. 근데 UE5에서는 자동으로 처리.

 

연결되는 단어들

 

패럴렉스 매핑 (Parallax Mapping) : 노멀 맵의 실루엣 한계를 보완으로 하이트 맵으로 UV 좌표를 시선 방향에 따라 이동시켜 깊이감을 포현하며 자기 그림자까지 구현 가능하다

 

앰비언트 오클루전 맵 (AO Map) : 노멀 맵과 함께 쓰이는 텍스처. 구석진 곳에 그림자를 추가해 입체감을 강화한다. 노멀 맵이 빛 방향 반응을 담당하면 AO는 전역 그림자를 담당한다.

 

베이킹 (Baking) : 하이폴리 메시의 디테일을 로우폴리 메시의 노멀 맵으로 굽는 과정. Marmoset Toolbag, Blender, Substance Painter에서 수행한다.

 

탄젠트 (Tangent) : 노멀과 수직이면서 UV의 U 방향을 따르는 벡터. 노멀 맵의 TBN 행렬 구성에 필수이다. 버텍스 데이터에

포함되어 GPU로 전달한다.

 

'Word' 카테고리의 다른 글

오버드로우 (Overdraw)  (0) 2026.07.27
UV 매핑 (UV Mapping)  (0) 2026.07.22
컴퓨트 셰이더 ( Compute Shader )  (0) 2026.07.20
HLSL/GLSL  (0) 2026.07.16
WebGL / WebGPU  (0) 2026.07.14

+ Recent posts