수업
✅ 주제
- 본 강의의 주제는 Emissive Light(자가 발광 조명)을 활용하여
물체의 외곽선을 강조하는 림 라이트(Rim Light) 효과를 구현하는 것이다.
- 외부 광원(LightDir, LightColor) 없이, 카메라 방향과 Normal의 각도 차만을 이용하여 윤곽선 강조를 수행한다.
📘 개념
🔹 Emissive 조명이란?
- Emissive는 물체 자체가 빛을 내는 것처럼 보이도록 하는 조명 기법이다.
- 일반적으로 사용되는 조명(Diffuse, Specular)은 외부 광원의 방향과 색상에 따라 조명이 적용되지만,
Emissive는 광원 없이도 표현 가능하다.
- 이번 강의에서는 이 Emissive를 외곽선을 강조하는 Rim Light 방식으로 구현한다.
🔹 작동 원리
- Eye 벡터(E) = 픽셀 위치에서 카메라 위치를 향하는 벡터
- Normal 벡터(N) = 표면 법선 방향
dot(E, N)은 정면일수록 1, 수직일수록 0
- 외곽은 수직인 곳이므로,
1 - dot(E, N)을 사용해 외곽 정도를 계산
- 그 결과를
smoothstep()으로 보정하여 부드럽게 외곽 강조값 생성
- 해당 값을
MaterialEmissive에 곱해 최종 색상 출력
🧾 용어 정리
| 용어 | 설명 |
|---|
| Emissive Light | 자가 발광 조명. 물체가 자체적으로 발광하는 시각적 효과 |
| Rim Light | 림 라이트. 물체의 가장자리(윤곽선)에 강조를 주는 기법 |
| Eye 벡터 (E) | 픽셀 → 카메라로 향하는 방향 벡터 |
| dot() | 두 벡터의 방향 각도 계산. 1이면 일치, 0이면 수직 |
| saturate() | 0~1로 clamp. 음수나 1 초과 제거 |
| smoothstep(a,b,x) | a~b 범위의 x를 부드럽게 보간 |
| pow(x,n) | 값의 강조 혹은 경계 영역 조절 |
🧠 코드 분석
📄 HLSL 셰이더: Lighting_Emissive.fx
🔹 변수 선언
#include "00. Global.fx"
float4 MaterialEmissive;
- 외부 광원 관련 변수(
LightDir, LightDiffuse)는 사용하지 않으므로 제거
MaterialEmissive만 남김: 물체의 발광 색상을 결정
🔹 정점 셰이더 (VS)
MeshOutput VS(VertexTextureNormal input)
{
MeshOutput output;
output.position = mul(input.position, W); // 월드 좌표계로 변환
output.worldPosition = output.position; // 카메라 위치 연산용
output.position = mul(output.position, VP); // 클립 좌표로 변환
output.uv = input.uv;
output.normal = mul(input.normal, (float3x3)W); // 회전만 반영된 노멀
return output;
}
worldPosition: Eye 벡터 계산을 위해 필요
- 노멀은 월드 기준에서 회전만 적용
🔹 픽셀 셰이더 (PS)
float4 PS(MeshOutput input) : SV_TARGET
{
float3 cameraPosition = -V._41_42_43;
float3 E = normalize(cameraPosition - input.worldPosition); // 카메라 방향 벡터
float value = saturate(dot(E, input.normal)); // 정면일수록 1, 수직일수록 0
float emissive = 1.0f - value; // 수직일수록 강조
emissive = smoothstep(0.0f, 1.0f, emissive); // 부드러운 그라데이션 적용
emissive = pow(emissive, 2); // 강조 영역 좁힘
float4 color = MaterialEmissive * emissive; // 색상 = 강조값 × 재질색
return color;
}
dot(E, N) → 중심부일수록 1, 외곽일수록 0
1 - value → 외곽일수록 값이 커짐
smoothstep + pow 조합으로 자연스럽고 날카로운 외곽선 생성
- 광원 정보 없이 카메라 시점에 따라 강조
📄 technique 정의
technique11 T0
{
PASS_VP(P0, VS, PS)
};
- 단일 패스 구성: 정점 셰이더 → 픽셀 셰이더
💻 C++: EmissiveDemo.cpp
🔹 Init()
_shader = make_shared<Shader>(L"12. Lighting_Emissive.fx");
_obj->GetMeshRenderer()->SetShader(_shader);
_obj2->GetMeshRenderer()->SetShader(_shader);
🔹 Update()
Vec4 materialEmissive(1.f, 0.f, 0.f, 1.f);
_shader->GetVector("MaterialEmissive")->SetFloatVector((float*)&materialEmissive);
_obj->Update();
Vec4 materialEmissive2(0.f, 1.f, 0.f, 1.f);
_shader->GetVector("MaterialEmissive")->SetFloatVector((float*)&materialEmissive2);
_obj2->Update();
📊 실험
| 조건 | 결과 |
|---|
| Emissive 적용 | 외곽선 강조 발광 효과 확인 |
pow(emissive, 2) 적용 | 외곽선이 더 얇고 날카롭게 표현됨 |
| Sphere 오브젝트 | 곡면이 많아 외곽 강조 효과 뚜렷 |
| Cube 오브젝트 | 평면 노멀이 균일해 외곽선 표현이 덜 뚜렷 |
🔍 수식 분석
- 기존 조명:
dot(N, L) → 정면일수록 밝게
- Emissive 림라이트:
dot(N, E) → 정면일수록 어둡게, 수직일수록 밝게
float value = saturate(dot(E, N)); // 정면 = 1
float emissive = 1.0f - value; // 반전 → 외곽 강조
smoothstep는 외곽의 색상 변화 경계를 부드럽게
pow()는 강조 영역 축소 → 더 날카롭고 선명한 외곽선 표현 가능
✅ 핵심
- Emissive는 조명을 받지 않아도 물체가 자체 발광하는 효과를 만든다.
- 카메라 방향(Eye)과 Normal이 수직일수록 외곽선으로 간주하고 강조한다.
- 핵심 수식은
1 - dot(E, N)이며, 이를 smoothstep()과 pow()로 보정해 효과를 조절한다.
- 광원이 없는 상황에서도 림 라이트 기반 외곽선 강조 효과를 매우 간단하게 구현할 수 있다.
- 다양한 색상 적용, 강조 범위 조절, UI 연동 등으로 확장 가능하다.