아래 코드는 모두 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)*levels | floor(...) | 단계 | intensity 결과 |
| 0.12 | 0.72 | 0 | 0단계 | 0.0 |
| 0.35 | 2.1 | 2 | 2단계 | 0.333 |
| 0.83 | 4.98 | 4 | 4단계 | 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
'Programming > 컴퓨터그래픽스 (DX 11)' 카테고리의 다른 글
| [DirectX11] 그림자 매핑 (Shadow Mapping) (0) | 2025.11.11 |
|---|---|
| [Shader] 외곽선(OutLine) 패스 (0) | 2025.11.10 |
| [DirectX 11] 3D 모델 로드하기 - Rigid Skeletal Mesh (0) | 2025.10.29 |
| [DirectX 11] 3D 모델 로드하기 - Static Mesh(정적 메시) (0) | 2025.10.29 |
| [DirectX 11] 노말맵(Normal Map)과 기저 공간(Tangent Space) (0) | 2025.10.11 |