
Triplanar Mapping으로 텍스처 스트레칭 없는 복잡한 지형 렌더링
가파른 절벽과 불규칙한 지형에서 발생하는 UV 텍스처 스트레칭을 Triplanar Mapping으로 해결하는 원리, HLSL 구현, 성능 최적화와 블렌딩 팁을 정리합니다.
복잡한 지형의 텍스처 스트레칭은 단일 UV 투영이 표면의 기울기를 따라가지 못해서 발생한다. Triplanar Mapping은 월드 좌표를 X·Y·Z 세 평면에 각각 투영하고 표면 노멀 방향에 따른 가중치로 세 결과를 섞어 이 문제를 줄인다. 절벽·바위·동굴처럼 UV 언랩 비용이 높거나 지형 변화가 큰 환경에 특히 적합하다.
Triplanar Mapping이란 무엇인가?
Triplanar Mapping은 메시 UV 대신 월드 공간 또는 오브젝트 공간 위치를 텍스처 좌표로 사용한다. 텍스처를 세 축 방향에서 투영한 뒤 픽셀 노멀의 각 축 성분을 이용해 가장 자연스러운 투영 결과가 나타나도록 블렌딩한다.
| 항목 | 일반 UV Mapping | Triplanar Mapping |
|---|---|---|
| 좌표 기준 | 메시의 UV 채널 | 월드 또는 오브젝트 좌표 |
| 절벽 텍스처 | 쉽게 늘어남 | 축별 투영으로 안정적 |
| UV 언랩 | 필요 | 대부분 생략 가능 |
| 비용 | 비교적 낮음 | 텍스처 샘플 수 증가 |
| 적합한 대상 | 캐릭터, 소품 | 지형, 바위, 절벽, 볼륨형 구조물 |
지형 텍스처는 왜 가파른 면에서 늘어나는가?
지형에 흔히 사용하는 평면 UV는 수평면을 기준으로 만들어진다. 이 UV를 수직에 가까운 절벽에 그대로 적용하면 좁은 UV 영역이 긴 실제 표면을 덮게 된다. 그 결과 흙, 암석, 이끼 무늬가 한 방향으로 길게 당겨진다.
Triplanar Mapping은 이 문제를 단일 투영으로 해결하려 하지 않는다. 예를 들어 위쪽을 향한 표면은 XZ 평면 투영을 주로 사용하고 동쪽을 향한 표면은 YZ 평면 투영을 주로 사용한다. 각 방향이 이어지는 경계에서는 두세 투영 결과를 부드럽게 섞는다.

Triplanar Mapping 블렌딩 가중치는 어떻게 계산할까?
정규화된 월드 노멀을 라고 하자. 기본 가중치는 각 성분의 절댓값이다.
세 가중치의 합이 1이 되도록 정규화한다.
블렌딩 경계를 더 선명하게 만들고 싶다면 BlendSharpness 거듭제곱을 적용한다.
여기서 는 BlendSharpness다. 값이 낮으면 축 전환이 넓고 부드러워지며 값이 높으면 각 투영이 더 뚜렷해진다. 실무에서는 2~8 범위에서 시작해 머티리얼과 텍스처의 대비에 맞춰 조정하는 편이 안전하다.
HLSL로 Triplanar Mapping을 구현하는 방법
다음 함수는 알베도 텍스처를 세 번 샘플링해 Triplanar 결과를 반환한다. positionWS와 normalWS는 각각 월드 공간 위치와 정규화된 월드 공간 노멀이다.
float3 SampleTriplanarAlbedo(
Texture2D albedoMap,
SamplerState linearRepeatSampler,
float3 positionWS,
float3 normalWS,
float textureScale,
float blendSharpness)
{
float3 blend = pow(abs(normalize(normalWS)), blendSharpness);
blend /= max(blend.x + blend.y + blend.z, 1e-5);
float2 uvX = positionWS.zy * textureScale;
float2 uvY = positionWS.xz * textureScale;
float2 uvZ = positionWS.xy * textureScale;
float3 sampleX = albedoMap.Sample(linearRepeatSampler, uvX).rgb;
float3 sampleY = albedoMap.Sample(linearRepeatSampler, uvY).rgb;
float3 sampleZ = albedoMap.Sample(linearRepeatSampler, uvZ).rgb;
return sampleX * blend.x + sampleY * blend.y + sampleZ * blend.z;
}
Step 1. 투영 좌표계를 결정한다
고정된 월드 지형에는 월드 공간 좌표를 권장한다. 인접한 여러 메시가 같은 좌표계를 공유하므로 경계가 자연스럽게 이어진다. 움직이거나 회전하는 소품에는 오브젝트 공간 좌표가 더 적합하다. 월드 공간만 사용하면 오브젝트가 움직일 때 텍스처가 공간에 고정된 것처럼 보인다.
Step 2. X·Y·Z 축 투영을 샘플링한다
축에 수직인 두 좌표를 UV로 사용한다.
| 투영 방향 | 사용할 좌표 | 주로 보이는 표면 |
|---|---|---|
| X축 | positionWS.zy | 좌우를 향한 면 |
| Y축 | positionWS.xz | 위아래를 향한 면 |
| Z축 | positionWS.xy | 앞뒤를 향한 면 |
좌표에 textureScale을 곱하면 월드 단위 기준 타일링 밀도를 조절할 수 있다. 지형 크기가 커질수록 이 값을 에셋별 값이 아니라 월드 단위 밀도로 관리해야 텍스처 크기가 일관된다.
Step 3. 노멀 기반 가중치로 결과를 합성한다
각 축의 텍스처 색에 해당 축의 가중치를 곱해 더한다. 반드시 가중치를 정규화해야 밝기가 불필요하게 변하지 않는다. 또한 월드 공간 노멀을 쓰는 경우 노멀 보정 이후의 노멀이 아니라 투영 방향을 판단할 기준 노멀을 명확히 선택해야 한다.

노멀 맵까지 Triplanar Mapping으로 처리하려면?
알베도처럼 색만 섞는 방식으로는 정상적인 노멀 맵 결과를 얻기 어렵다. 각 축에서 읽은 탄젠트 공간 노멀은 서로 다른 투영 기준을 가진다. 따라서 샘플별 노멀을 해당 축의 월드 공간 방향으로 재구성한 뒤 블렌딩해야 한다.
구현 난도가 부담스럽다면 다음 우선순위를 권장한다.
- 먼저 알베도, 마스크, 러프니스 또는 스무스니스에 Triplanar Mapping을 적용한다.
- 노멀 맵이 반드시 필요한 가까운 표면만 축별 노멀 재구성을 적용한다.
- 원거리 LOD에서는 노멀 샘플을 줄이거나 기존 지형 노멀을 유지한다.
특히 Unity URP/HDRP나 Unreal Engine Material Editor에서는 엔진이 제공하는 월드 공간 노멀, 파생 함수, 머티리얼 함수의 좌표 규약을 확인해야 한다. 노멀 맵이 반전되거나 이음새가 보이면 축별 UV의 부호와 각 축의 탄젠트·비탄젠트 방향부터 점검한다.
성능 비용은 얼마나 되며 어떻게 최적화할까?
기본 Triplanar Mapping은 텍스처 하나당 최대 3회 샘플링한다. 알베도·노멀·마스크·높이 맵을 모두 독립적으로 읽으면 텍스처 대역폭이 빠르게 증가한다. 화면을 크게 차지하는 지형 머티리얼에서는 샘플 수와 오버드로우를 함께 측정해야 한다.
| 최적화 방법 | 기대 효과 | 주의점 |
|---|---|---|
| ORM 패킹 | AO·Roughness·Metallic 샘플 통합 | 채널 규약을 팀에서 고정해야 함 |
| 거리 기반 품질 단계 | 원거리 샘플·노멀 비용 감소 | 전환 구간의 팝핑 방지 필요 |
| 절벽 전용 마스크 | 필요한 영역에만 고비용 처리 | 마스크 제작·관리 비용 발생 |
| 텍스처 배열 또는 아틀라스 | 머티리얼 변형 수 감소 | 플랫폼별 지원과 밉맵 확인 |
| 블렌드 샤프니스 제한 | 과도한 경계 노이즈 완화 | 너무 낮으면 축 경계가 넓어짐 |
가장 단순한 최적화는 모든 채널을 무조건 Triplanar로 바꾸지 않는 것이다. 예를 들어 원거리 지형의 마스크는 기존 UV를 유지하고 시각적으로 가장 눈에 띄는 알베도와 노멀만 Triplanar 처리하는 방식이 유효하다.
Triplanar Mapping과 Slope Mask를 함께 쓰는 이유
Triplanar Mapping은 텍스처가 늘어나는 문제를 해결하지만 어떤 재질을 어느 경사에 배치할지는 결정하지 않는다. 이 역할은 노멀의 Y 성분을 활용하는 Slope Mask가 맡는다.
a와 b는 경사 전환 구간이다. slope가 1에 가까운 수평면에는 흙·잔디를 0에 가까운 수직면에는 바위를 배치할 수 있다. 두 재질 모두 Triplanar 좌표로 샘플링하면 경사 변화가 큰 지형에서도 자연스럽게 이어진다.
float slope = saturate((normalWS.y - 0.25) / (0.75 - 0.25));
float3 ground = SampleTriplanarAlbedo(
groundMap, samplerGroundMap, positionWS, normalWS, 0.08, 4.0);
float3 rock = SampleTriplanarAlbedo(
rockMap, samplerRockMap, positionWS, normalWS, 0.12, 6.0);
float3 finalAlbedo = lerp(rock, ground, slope);
높이 맵 기반 블렌딩이나 버텍스 페인트를 추가하면 경사만으로 생기는 반복 패턴을 줄일 수 있다. 다만 이 단계부터는 머티리얼 복잡도가 빠르게 커지므로 Shader Complexity, GPU Frame Time, 텍스처 메모리를 실제 타깃 플랫폼에서 확인해야 한다.
자주 묻는 질문 (FAQ)
Triplanar Mapping은 모든 메시의 UV를 없애도 되는가?
아니다. 캐릭터, 데칼, 정밀한 페인팅이 필요한 소품, 방향성이 중요한 텍스처에는 일반 UV가 더 적합하다. Triplanar Mapping은 불규칙한 자연물과 지형에서 강점이 크다.
월드 공간과 오브젝트 공간 중 무엇을 써야 하는가?
고정 지형과 월드에 붙은 구조물은 월드 공간을 사용한다. 움직이거나 회전하는 메시는 오브젝트 공간을 사용해야 텍스처가 메시와 함께 움직인다.
축 경계가 눈에 띄면 어떤 값을 조절해야 하는가?
먼저 BlendSharpness를 낮춰 블렌딩 영역을 넓힌다. 그래도 반복이나 이음새가 보이면 텍스처 자체의 대비, 축별 UV 부호, 노멀 맵 축 변환을 확인한다.
마무리
Triplanar Mapping은 복잡한 지형의 UV 스트레칭을 줄이고 절벽과 바위에 일관된 텍스처 밀도를 유지하는 실용적인 셰이더 기법이다. 핵심은 세 축 투영보다도 정확한 노멀 가중치, 좌표계 선택 그리고 샘플링 비용 관리에 있다. 먼저 알베도와 마스크에 적용해 시각적 효과를 확인한 뒤 필요한 범위에서만 노멀·높이 블렌딩으로 확장하는 것이 안정적이다.


