Programming/컴퓨터그래픽스 (DX 11)

[Shader] 툰 쉐이딩(Toon Shading)과 램프텍스처(RampTexture)

양양줘 2025. 11. 10. 16:18

 
아래 코드는 모두 DirectX 11 기준입니다.
 
 

툰 쉐이딩 (Toon Shading)


툰 쉐이딩(Toon Shading)은 일반적인 부드러운 명암(Phong) 대신 밝음-중간-어두움 같이 명암에 단계를 둬 3D 그래픽을 2D 애니메이션처럼 보이게 만드는 비사실적 렌더링(NPR, Non-Photorealistic REndering) 기법이다. 툰 쉐이딩은 기본적으로 램버트 조명 모델(Diffuse)을 기반으로 단순화한다.

 

// Diffuse (부드러운 음영, 0~1)
intensity = dot(N, L)

// ⭐ Diffuse Toon (단계적 음영, 0, 1, 2, 3, 4, 5)
int   levels = 6;                          // 밝기 단계 수
float scaleFactor = 1.0 / (float) levels;  // 0~1 로 정규화
intensity = floor(dot(N, L) * levels) * scaleFactor

 
툰 쉐이딩을 위해서는 일반적인 램버트 조명 (dot(N,L)) 값을 부드럽게 쓰지 않고, 이를 몇 개의 구간으로 나누어 고정된 밝기값을 정의한다. 밝기를 몇단계로 나눌지 levels로 정의하고 램버트 조명 모델의 밝기에 곱한다. 이렇게 나온 일반적인 intensity를 floor연산을 통해 내림 처리하면 각 intensity의 단계가 정해진다. 이렇게 나온 level을 scaleFator를 통해 0~1 사이 값으로 정규화하여 툰쉐이딩의 intensity를 도출할 수 있다.

dot(N,L)dot(N,L)*levelsfloor(...) 단계intensity 결과
0.120.7200단계0.0
0.352.122단계0.333
0.834.9844단계0.666

 
 
 
 

Ramp Texture 툰 쉐이딩(Toon Shading)


위에서 설명한 툰 쉐이딩 기법은 단순히 밝기를 단계적으로 나누었다. 이 방식은 원래 계산되어야 할 색상의 밝기값을 밝음-중간-어두움과 같이 단순하게 나눈 것이기 때문에 밝기의 색상을 우리가 제어할 수는 없다. 이때 램프 텍스처를 사용하면 조금 더 고급 쉐이딩이 가능하다.

 
램프 택스처(Ramp Texture)밝기값(dot(N,L))을 UV좌표의 x축으로 사용해서 색상을 샘플링하는 1D 텍스처이다. 즉 수식을 통해 계산한 intensity 를 밝기에 적용하지 않고, intensity에 따라 렌더텍스처의 색상을 매핑할 수 있다. 램프텍스처를 사용하면 텍스처만 변경해도 다양한 스타일을 연출할 수 있다.(Classic, Warm, Cool, Soft 등)

// register binding
Texture2D rampTex : register(t1);
SamplerState rampSampler : register(s1);

// ⭐ ramp texture toon shading
float NdotL = saturate(dot(normal, lightDir));
float3 toonColor = rampTex.Sample(RampSampler, float2(NdotL, 0.0)).rgb;

 
 
 

정반사 툰쉐이딩 (Specular Toon Shading)


정반사(Specualr)에도 툰쉐이딩을 적용할 수 있다. 정반사에 툰쉐이딩을 적용하면 반사가 자연스럽게 번지지 않고 뚝 뚝 끊긴 하이라이트 띠가 생겨서 애니메이션 풍이 강해진다. 램프 텍스처를 사용하면 정반사 색상까지 제어할 수 있어 하이라이트 모양이나 색조(보라빛 반사 등)까지 이미지로 디자인 할 수 있게 된다.

 
아래 코드는 반사 벡터를 구하지 않고 하프벡터를 사용하는 블린퐁 공식의 정반사 개념을 사용한다.

// 블린퐁 Specualr
float NdotH = saturate(dot(N, H));
float intensity = pow(NdotH, shininess);

// ⭐ 블린퐁 Specualr Toon - 단계적 툰
float NdotH = saturate(dot(N, H));
float intensity = pow(NdotH, shininess);
int specLevels = 3;
float scaleFactor = 1.0 / specLevels;
intensity = floor(spec * specLevels) * scaleFactor;

// ⭐ 블린퐁 Specualr Toon - Ramp Texture
float NdotH = saturate(dot(N, H));
NdotH = pow(NdotH, shininess);    // 생략 가능
float3 intensity= specularRampTex.Sample(rampSampler, float2(NdotH, 0)).rgb;

 
 
10_CartoonRendering 프로젝트에서 자세한 코드를 확인할 수 있다. (쉐이더는 WinBase 라이브러리 폴더에 있다.)

GitHub - wooj22/DirectX3D11_Study: DirectX3D11 공부용 레포지토리입니다.

DirectX3D11 공부용 레포지토리입니다. Contribute to wooj22/DirectX3D11_Study development by creating an account on GitHub.

github.com