수업
✅ 주제
- DirectX11 기반 실시간 렌더링에서 Ambient Light(환경광)를 구현하고, Shader와 재질(Material)의 상호작용을 통해 물체에 일정한 조도를 부여하는 기초 조명 시스템을 구현하는 방법을 학습한다.
✅ 개념
- 우리가 물체를 볼 수 있는 이유는 빛이 물체에 닿고 반사되어 눈으로 들어오기 때문이다.
- 하지만 실제 조명은 수많은 난반사와 굴절이 얽혀 있으므로, 이걸 실시간으로 GPU에서 처리하는 건 불가능하다.
- 따라서 현실의 조명을 흉내 내는 단순화된 조명 모델을 사용한다.
- 이 중 Ambient Light는 물체에 광원이 직접 닿지 않아도 일정한 조도로 보이게 해주는 기본 조명이다.
- Ambient는 방향성이 없고, 항상 일정한 색상과 밝기를 가지며, 그림자나 하이라이트 없이 전반적인 밝기 효과만 부여한다.
- Diffuse, Specular, Emissive 조명과 함께 최종적으로 조명 시스템을 구성하게 된다.
✅ 용어정리
| 용어 | 의미 |
|---|
| Ambient Light | 환경광. 광원에서 직접 닿지 않은 빛들이 반사되어 퍼지는 간접광 |
| LightAmbient | 광원이 가지고 있는 Ambient 색상 (조명 색상) |
| MaterialAmbient | 물체가 Ambient 조명에 반응하는 비율 또는 재질 특성 |
| Shader | GPU에서 실행되는 프로그램. 조명, 변환, 색상 결정 등을 담당 |
| Vertex Shader (VS) | 정점 위치 계산 (월드 → VP 변환) |
| Pixel Shader (PS) | 픽셀별로 최종 색상 계산 (조명, 텍스처 연산) |
| Sampling | 텍스처 좌표 기반으로 픽셀 색상 추출 |
| Multiplicative Lighting | 조명 색 × 재질 색으로 조도 계산하는 방식 |
✅ 코드 분석
🔹 Shader 파일: 09. Lighting_Ambient.fx
#include "00. Global.fx"
float4 LightAmbient; // 광원의 환경광 색상
float4 MaterialAmbient; // 재질의 환경광 반응 정도
- 조명과 재질 각각의 색상이 float4로 선언되어 있고, PS에서 곱해진다.
VertexOutput VS(VertexTextureNormal input)
{
VertexOutput output;
output.position = mul(input.position, W); // 월드 변환
output.position = mul(output.position, VP); // 뷰-프로젝션 변환
output.uv = input.uv;
output.normal = mul(input.normal, (float3x3)W); // 회전 성분만 포함
return output;
}
- 정점 좌표를 월드-뷰-프로젝션으로 변환하여 화면상 위치로 설정
- 노멀은 회전만 반영하여 조명 방향 계산에 사용할 준비
Texture2D Texture0;
float4 PS(VertexOutput input) : SV_TARGET
{
float4 color = LightAmbient * MaterialAmbient;
return color;
}
- 픽셀 셰이더에서 조명 색과 재질 색을 곱하여 조도 계산
- 지금은 텍스처 적용 없이 순수 조명 색상만 화면에 출력
🔹 AmbientDemo.cpp
_shader = make_shared<Shader>(L"09. Lighting_Ambient.fx");
- Ambient 조명 전용 쉐이더 파일을 불러온다.
_camera = make_shared<GameObject>();
_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -10.f });
- 카메라를 뒤쪽으로 배치하여, 두 오브젝트를 관찰하기 쉽게 만든다.
Sphere, Cube 오브젝트 생성
_obj = make_shared<GameObject>();
_obj->AddComponent(make_shared<MeshRenderer>());
_obj->GetMeshRenderer()->SetShader(_shader);
_obj->GetMeshRenderer()->SetMesh(RESOURCES->Get<Mesh>(L"Sphere"));
_obj->GetMeshRenderer()->SetTexture(RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg"));
_obj2 = make_shared<GameObject>();
_obj2->GetOrAddTransform()->SetPosition(Vec3{ 0.5f, 0.f, 2.f });
_obj2->AddComponent(make_shared<MeshRenderer>());
_obj2->GetMeshRenderer()->SetShader(_shader);
_obj2->GetMeshRenderer()->SetMesh(RESOURCES->Get<Mesh>(L"Cube"));
_obj2->GetMeshRenderer()->SetTexture(RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg"));
- Sphere와 Cube 두 개의 오브젝트를 생성하고, 동일한 텍스처와 쉐이더를 사용
🔹 Update 함수: 조명/재질 값 전달
Vec4 lightAmbient{ 0.5f, 0.f, 0.f, 1.f };
_shader->GetVector("LightAmbient")->SetFloatVector((float*)&lightAmbient);
Vec4 materialAmbient(1.f);
_shader->GetVector("MaterialAmbient")->SetFloatVector((float*)&materialAmbient);
_obj->Update();
_obj2->Update();
- 붉은 Ambient 조명을 설정하고, 두 오브젝트가 모두 그 영향을 100% 받도록 materialAmbient를 (1,1,1,1)로 설정
- 결국 두 물체는 붉게 표현됨
🔹 샘플링과 혼합 테스트 (Shader 수정)
float4 color = LightAmbient * MaterialAmbient;
return Texture0.Sample(LinearSampler, input.uv) * color;
- 텍스처 색상과 Ambient 조명을 곱하여 더 현실적인 표현 가능
- 붉은 빛만 반영되면 텍스처의 붉은 부분은 밝고, 다른 색은 어둡게 표현됨
✅ 핵심
- Ambient 조명은 광원이 명확하지 않아도 은은한 조도를 제공하는 간접광의 개념이다.
- 픽셀 쉐이더에서 LightAmbient × MaterialAmbient만으로 간단하게 구현할 수 있다.
- 이후에 Diffuse, Specular, Emissive 조명을 순차적으로 결합해가며 완전한 조명 시스템을 구현할 수 있다.
- 텍스처 샘플링과 곱해줄 경우 실제 텍스처의 색상에 조명 효과가 반영되어 더 자연스러운 시각 효과를 만들 수 있다.
- 재질(material)마다 반응 정도를 다르게 하여, 광원에 따라 다른 물체는 다르게 보이도록 구현 가능하다.
- Shader 공식은 고정된 정답이 없고, 실험과 시각적 판단에 따라 구성될 수 있다.