MOONSUN
[그래픽스] Toon Shading (툰 셰이딩) : Lambert 단계화 & Ramp Texture 방식 본문
0. Toon Shading (툰 셰이딩) 이란?
현실적인 조명 표현 대신,
단계적인 명암 구분을 사용하는 비사실적 셰이딩 (NPR, Non-Photorealistic Rendering) 기법
- 일반적인 Phong / Lambert 조명처럼 부드러운 명암을 사용하지 않고,
- 단계적인 색 구분(밝음–중간–어두움) 으로 음영을 표현한다.
- 만화, 애니메이션 같은 느낌을 내는 렌더링 기법
1. Toon Shading 구현 방식
Toon Shading은 2가지 방식이 있다.
1. Lambert 조명 단계화
2. Ramp Texture 방식
1-1. Lambert 조명 단계화 ( Lambert Quantization ) 방식
기본적인 Lambert 조명 모델은 다음과 같다.
Diffuse = (MaterialDiffuse ⊗ LightColor) * max(dot(N, L), 0)
여기서 dot(N, L)은 법선 벡터(N)와 광선 방향(L)의 내적 값으로, 빛의 세기를 의미한다.
이 값은 0~1 사이의 실수값을 가지며,
보통 이 값을 그대로 사용해서 부드러운 명암을 만들어 낸다.
1-1-1. Toon Shading을 위한 단계화 (Quantization)
Toon Shading에서는 이 값을 인위적으로 단계화(Quantize) 해서 사용한다.
dot(N, L) → floor(saturate(dot(N, L)) * levels) * scaleFactor
- levels : 밝기 단계 수 (4단계, 6단계 등)
- scaleFactor = 1.0 / levels
즉, dot(N, L)의 값을 몇 개의 구간으로 나누고, 각 구간마다 고정된 밝기를 부여한다.
int levels = 6; // 밝기 단계 수
float scaleFactor = 1.0 / (float)levels;
float DiffuseIntensity = floor(saturate(dot(N, L)) * levels) * scaleFactor;
float3 diffuseRGB = DiffuseIntensity * (MaterialDiffuseColor.xyz * LightColor.xyz);
이렇게 처리하면, 기존의 부드러운 음영 대신 뚜렷한 단계별 음영이 만들어진다.
1-2. Ramp Texture ( Texture Lookup 기반 ) 방식
Ramp Texture란?
Ramp는 "점진적 변화"를 의미한다.
Ramp Texture는 이런 변화를 이미지 형태로 표현한 텍스처로,
dot(N, L) 값을 Ramp Texture의 X 좌표(U) 로 삼아 색을 샘플링한다.
즉, dot(N, L) 값에 따라 Ramp Texture에서 색상을 읽어오는 방식
DiffuseIntensity 대신 RampColor 사용
- RampColor = tex2D(RampTexture, float2(NdotL, 0.5))
float NdotL = saturate(dot(N, L));
float2 rampUV = float2(NdotL, 0.5);
float4 RampColor = tex2D(RampTexture, rampUV);
float4 MaterialDiffuseColor = tex2D(BaseTexture, Input.TexCoord);
float3 diffuseRGB = RampColor.rgb * (MaterialDiffuseColor.rgb * LightColor.rgb);
Ramp Texture 방식은 다음과 같은 장점을 가진다.
- 수학적 계산(floor) 대신 이미지 기반 제어 가능
- Ramp Texture만 바꿔도 다양한 스타일 연출 가능
- 예: Classic / Warm / Cool / Soft 등
즉, 아티스트가 직접 톤 맵을 그려서 원하는 명암 스타일을 시각적으로 조정할 수 있다.
2. Toon Shading + 단계화된 Specular (Quantized Specular Highlight)
2-1. 일반적인 Specular 계산
기본 Phong 하이라이트는 다음과 같이 계산한다.
float3 R = normalize(2.0 * dot(N, L) * N - L);
float SpecularIntensity = pow(saturate(dot(R, V)), Shininess);
float3 specularRGB = SpecularIntensity * (MaterialSpecularColor * LightColor);
하지만 이 방식은 부드럽게 번지는 하이라이트를 만들어내며,
만화풍 스타일인 Toon Shading 에서는 딱딱 끊기는 반사광이 더 자주 사용된다.
2-2. Toon Specular (단계화 Floor 방식)
Specular 값에 floor() 를 적용해 단계화할 수 있다. (밝은 영역만 또렷하게 남도록)
// Toon Shading 단계 수 설정
int specularLevels = 3; // 하이라이트 단계 수
float speculatScaleFactor = 1.0 / (float)specularLevels;
// Toon Specular (단계화 floor 방식)
// Specular Reflection Vector R = 2(N·L)N - L
float3 R = normalize(2.0 * dot(N, L) * N - L);
float SpecularIntensity = pow(saturate(dot(R, V)), Shininess);
SpecularIntensity = floor(SpecularIntensity * specularLevels) * speculatScaleFactor;
float3 specularRGB = SpecularIntensity * (MaterialSpecularColor * LightColor);
이렇게 하면 반사광이 ‘계단식으로’ 표현되어, 만화적이고 선명한 빛 반사 효과를 구현할 수 있다.
3. Toon Shading + Specular Ramp Texture
단계화된 Specular을 Ramp Texture 기반으로 제어할 수도 있다.
float3 R = normalize(2.0 * dot(N, L) * N - L);
float SpecularIntensity = pow(saturate(dot(R, V)), Shininess);
// Specular RampTexture를 통해 하이라이트 단계 제어
float2 specRampUV = float2(SpecularIntensity, 0.5);
float4 specRampColor = tex2D(SpecularRampTexture, specRampUV);
float3 specularRGB = specRampColor.rgb * (MaterialSpecularColor.rgb * LightColor.rgb);
이 방식은 Diffuse와 동일한 원리로,
SpecularRampTexture 이미지를 통해 반사광의 단계나 톤을 자유롭게 조절할 수 있다.
4. 정리
| 구분 | 방식 | 특징 |
| Lambert 단계화 | dot(N,L) → floor() 방식 | 수학적 계산으로 계단화된 밝기 |
| Ramp Texture 방식 | dot(N,L) → RampTexture lookup | 이미지 기반 색상 단계 제어 |
| Quantized Specular | pow(dot(R,V)) → floor() 적용 | 하이라이트 단계화 (딱딱한 빛 표현) |
| Specular Ramp 방식 | RampTexture로 Specular 제어 | 반사광의 톤과 형태를 시각적으로 조절 가능 |
'D3D' 카테고리의 다른 글
| [그래픽스] PBR(Physically Based Rendering) : 빛의 물리 기반 렌더링의 이해 (0) | 2025.12.01 |
|---|---|
| [그래픽스] Toon Shading의 OutLine 표현 : 실루엣 Pass (0) | 2025.11.11 |
| [D3D] 알파값(투명도) 처리 방식 : Alpha Test, Alpha Sorting, Alpha Blending (0) | 2025.10.21 |
| [D3D] 노멀 매핑(Normal Mapping) 과 접선 공간 (Tangent Space) (0) | 2025.10.02 |
| [D3D11] 01. RenderingTriangle 삼각형 그리기 (0) | 2025.09.29 |