빛의 세기 = dot(-L, N)
cos(θ)와 동일하다.dot = 1 → 정면dot = 0 → 수직dot < 0 → 음영| 용어 | 설명 |
|---|---|
| Diffuse Light | 물체 표면에서 산란되는 조명. 일반적인 반사광 |
| Lambert 법칙 | 코사인 값으로 빛의 세기를 계산하는 조명 모델 |
| LightDir | 빛의 방향 벡터 (빛이 향하는 반대 방향으로 사용됨) |
| LightDiffuse | 광원 자체의 색상. 빨강/파랑/흰색 등 |
| MaterialDiffuse | 물체가 광원을 얼마나 받아들이는가를 나타냄 |
| DiffuseMap | 물체 표면에 적용되는 텍스처 이미지 |
| Normalize() | 벡터를 길이 1로 만드는 함수. 내적 계산 시 필수 |
| dot() | 두 벡터의 내적. 각도에 따른 밝기 표현에 사용됨 |
Lighting_Diffuse.fx#include "00. Global.fx"
float3 LightDir;
float4 LightDiffuse;
float4 MaterialDiffuse;
Texture2D DiffuseMap;
LightDir: 광원의 방향 (반대 방향으로 내적)LightDiffuse: 빛의 색상 (예: 흰색이면 전체 밝기)MaterialDiffuse: 재질이 빛을 얼마나 받아들이는가DiffuseMap: 물체 표면 텍스처 (기존 Texture0에서 이름 변경됨)참고: 이전 Ambient 조명에서는
LightAmbient,MaterialAmbient를 사용했지만,
Diffuse에서는 이를 제거하고 위 3종 세트만 사용
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;
}
float3x3 W로 변환float4 PS(VertexOutput input) : SV_TARGET
{
float4 color = DiffuseMap.Sample(LinearSampler, input.uv);
float value = dot(-LightDir, normalize(input.normal)); // Lambert 공식 적용
color = color * value * LightDiffuse * MaterialDiffuse;
return color;
}
value)이 조명의 세기(밝기)를 결정technique11 T0
{
PASS_VP(P0, VS, PS);
};
DiffuseDemo.cpp_shader = make_shared<Shader>(L"10. Lighting_Diffuse.fx");
_camera = make_shared<GameObject>();
_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -10.f });
_camera->AddComponent(make_shared<Camera>());
_camera->AddComponent(make_shared<CameraScript>());
_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"));
_shader->GetSRV("DiffuseMap")->SetResource(_texture->GetComPtr().Get());
Texture0 → DiffuseMap으로 이름 변경 필요RENDER->Init(_shader);
좋습니다. 이어서 "Diffuse 조명 강의 교재 2부"를 작성해드리겠습니다.
이번 파트는 실습 중심으로 Update 함수에서 조명 세팅, 다양한 테스트 케이스, 그리고 Ambient 조명과의 조합까지 모두 포함한 완전한 내용입니다.
Diffuse 조명은 빛의 방향, 광원의 색상, 재질이 받아들이는 퍼센티지에 따라 결과 색상이 달라진다.
따라서 DiffuseDemo의 Update() 함수에서는 HLSL 셰이더에 이 값들을 매 프레임 전달해줘야 한다.
void DiffuseDemo::Update()
{
_camera->Update();
RENDER->Update();
Vec4 lightDiffuse{ 1.f, 1.f, 1.f, 1.f };
_shader->GetVector("LightDiffuse")->SetFloatVector((float*)&lightDiffuse);
(1,1,1,1)은 흰색을 의미 (모든 색을 갖고 있음)Vec3 lightDir{ 1.f, -1.f, 1.f };
lightDir.Normalize();
_shader->GetVector("LightDir")->SetFloatVector((float*)&lightDir);
Normalize()로 반드시 단위 벡터로 정규화해야 dot() 내적 시 올바른 값 계산 가능Vec4 material(1.f); // 모든 색을 다 받음
_shader->GetVector("MaterialDiffuse")->SetFloatVector((float*)&material);
_obj->Update();
_obj2->Update();
Diffuse 조명은 실험을 통해 감각적으로 익히는 것이 중요하다.
아래는 다양한 조합으로 테스트한 결과를 기반으로 정리한 예시이다.
Vec3 lightDir{ 1.f, -1.f, 1.f };
Vec4 lightDiffuse{ 1.f, 1.f, 1.f, 1.f };
Vec4 material{ 1.f, 1.f, 1.f, 1.f };
🟢 결과:
Vec3 lightDir{ 1.f, 0.f, 0.f };
🟢 결과:
Vec4 lightDiffuse{ 1.f, 0.f, 0.f, 0.f };
🟢 결과:
Vec4 material{ 1.f, 0.f, 0.f, 0.f };
Vec4 lightDiffuse{ 1.f, 1.f, 1.f, 1.f };
🟢 결과:
Vec4 lightDiffuse{ 1.f, 0.f, 0.f, 0.f };
Vec4 material{ 1.f, 0.f, 0.f, 0.f };
🟢 결과:
현재는 Lambert 법칙만 적용되어 빛을 못 받는 면은 완전히 검정색이 된다.
하지만 실제 현실에서는 빛이 반사되어 간접광이 도달하므로, 어느 정도는 보인다.
이를 해결하기 위해 Ambient 조명을 함께 합쳐야 한다.
Ambient + Diffuse + Specular로 병합 가능float4 finalColor = ambientColor + diffuseColor + specularColor;
0.2 ~ 0.6로 조절| 항목 | 내용 |
|---|---|
| 광원 방향 설정 | LightDir을 Normalize()해서 내적 계산에 사용 |
| 광원 색상 설정 | LightDiffuse로 조명의 색 표현 |
| 재질 수용도 | MaterialDiffuse로 어떤 색을 얼마나 받아들이는지 조절 |
| 실제 색상 출력 | 텍스처 × 밝기 × 광원 색상 × 재질 색상 |
| 그림자 영역 처리 | Ambient 조명 병합 필요 (덧셈 방식) |
| 정점 회전 보정 | 노멀 벡터는 float3x3 W로 회전만 적용 |
dot(-LightDir, Normal) 공식은 반드시 숙지해야 하며, 이를 기반으로 각도에 따른 빛의 세기를 계산한다.