-
[DirectX11] Lighting의 이해와 활용 Ambient, Diffuse, Specular, Emissive2024년 03월 06일
- 유니얼
-
작성자
-
2024.03.06.:45
728x90DirectX 11로 게임 엔진 아키텍처 만들기
조명은 3D 그래픽에서 장면의 실감나는 표현을 위해 필수적인 요소입니다. DirectX11에서는 Ambient, Diffuse, Specular, Emissive 등 다양한 조명 기법을 지원하여 더욱 풍부한 비주얼 효과를 제공합니다. 본 글에서는 DirectX11을 사용하여 개발 중인 게임 엔진 아키텍처 내에서 Lighting의 개념을 이해하고, 이를 적용한 예제 코드를 통해 그 활용 방법을 살펴보고자 합니다.
참고강의 링크:
Ambient Lighting (주변광) 정의:
Ambient Lighting은 객체를 둘러싼 일반적인 빛의 양을 나타냅니다. 이 빛은 모든 방향에서 동일하게 오며, 특정 방향성이 없습니다. 주변광은 장면 전체에 대한 기본 조명 수준을 설정하는 데 사용되며, 모든 객체에 균일하게 적용됩니다. 용도: 특정 빛의 방향이 없는 경우, 즉 일광이나 조명이 고르게 분포된 실내 공간에서의 장면을 모델링할 때 유용합니다. 또한, 그림자가 생성되지 않아 처리 비용이 낮은 편입니다.
// Ambient 처리 { float4 color = GlobalLight.ambient * Material.ambient; ambientColor = DiffuseMap.Sample(LinearSampler, uv) * color; }
Diffuse Lighting (확산광) 정의:
빛이 객체의 표면에 닿아 여러 방향으로 확산되어 반사되는 현상을 나타냅니다. 이는 표면의 노멀 벡터와 빛의 방향에 따라 영향을 받습니다. 표면이 빛을 향해 있을수록 더 밝게 보입니다. 특징: 확산광은 표면의 기본 색상을 결정하며, 질감과 재질의 느낌을 부여하는 데 중요한 역할을 합니다. 또한, 물체의 형태와 입체감을 나타내는 데 필수적입니다.
// Diffuse 처리 { float4 color = DiffuseMap.Sample(LinearSampler, uv); float value = dot(-GlobalLight.direction, normalize(normal)); diffuseColor = color * value * GlobalLight.diffuse * Material.diffuse; }
Specular Lighting (경사광) 정의:
빛이 객체의 표면에서 특정 각도로 반사되어 생성되는 밝은 하이라이트를 나타냅니다. 이는 관찰자의 시점, 빛의 방향, 그리고 표면의 재질 특성(반짝임)에 따라 변화합니다. 특징: 경사광은 표면의 반짝이는 특성을 표현하는 데 사용됩니다. 높은 광택의 표면에서는 강한 경사광이 나타나며, 이는 재질의 질감을 구분하는 데 중요한 단서를 제공합니다.
// Specular 처리 { // 반사 벡터 계산 float3 R = GlobalLight.direction - (2 * normal * dot(GlobalLight.direction, normal)); R = normalize(R); // 카메라 위치에서 세계 좌표까지의 방향 벡터 float3 cameraPosition = CameraPosition(); float3 E = normalize(cameraPosition - worldPosition); float value = saturate(dot(R, E)); // 0~1 사이의 값으로 제한 float specular = pow(value, 10); specularColor = GlobalLight.specular * Material.specular * specular; }
Emissive Lighting (발광) 정의:
물체 자체에서 빛을 내는 현상을 표현합니다. 이 조명은 외부 빛의 영향을 받지 않고, 오로지 물체 자체의 발광 특성에 의해 결정됩니다. 특징: 발광은 화면에서 물체가 스스로 빛나는 듯한 효과를 주며, 전자 장치의 화면, 불빛, 혹은 마법 효과 등 다양한 장면에서 사용됩니다. 이는 장면에 생동감을 부여하며 시각적 포커스 역할을 합니다.
// Emissive 처리 { float3 cameraPosition = CameraPosition(); float3 E = normalize(cameraPosition - worldPosition); float value = saturate(dot(E, normal)); float emissive = 1.0f - value; emissive = smoothstep(0.0f, 1.0f, emissive); emissive = pow(emissive, 2); emissiveColor = GlobalLight.emissive * Material.emissive * emissive; }
Light Shader
#ifndef _LIGHT_FX_ #define _LIGHT_FX_ #include "00. Global.fx" // 전역 쉐이더 변수와 함수를 포함하는 파일을 포함 ////////// //Struct// ////////// // 광원에 대한 설명을 포함하는 구조체 struct LightDesc { float4 ambient; // 주변 광 float4 diffuse; // 확산 광 float4 specular; // 반사 광 float4 emissive; // 자체 발광 float3 direction; // 광원의 방향 float padding; // 패딩 (16바이트 경계를 맞추기 위함) }; // 재질에 대한 설명을 포함하는 구조체 struct MaterialDesc { float4 ambient; // 재질의 주변 광 속성 float4 diffuse; // 재질의 확산 광 속성 float4 specular; // 재질의 반사 광 속성 float4 emissive; // 재질의 자체 발광 속성 }; /////////////// //ConstBuffer// /////////////// // 광원에 대한 상수 버퍼 cbuffer LightBuffer { LightDesc GlobalLight; }; // 재질에 대한 상수 버퍼 cbuffer MaterialBuffer { MaterialDesc Material; }; /////// //SRV// /////// // 텍스처 샘플링을 위한 쉐이더 리소스 뷰(SRV) Texture2D DiffuseMap; // 확산 맵 Texture2D SpecularMap; // 반사 맵 Texture2D NormalMap; // 정규 맵 //////////// //Function// //////////// // 광원 계산 함수 float4 ComputeLight(float3 normal, float2 uv, float3 worldPosition) { // 각 광원 요소에 대한 색상을 초기화 float4 ambientColor = 0; float4 diffuseColor = 0; float4 specularColor = 0; float4 emissiveColor = 0; // Ambient 처리 { float4 color = GlobalLight.ambient * Material.ambient; ambientColor = DiffuseMap.Sample(LinearSampler, uv) * color; } // Diffuse 처리 { float4 color = DiffuseMap.Sample(LinearSampler, uv); float value = dot(-GlobalLight.direction, normalize(normal)); diffuseColor = color * value * GlobalLight.diffuse * Material.diffuse; } // Specular 처리 { // 반사 벡터 계산 float3 R = GlobalLight.direction - (2 * normal * dot(GlobalLight.direction, normal)); R = normalize(R); // 카메라 위치에서 세계 좌표까지의 방향 벡터 float3 cameraPosition = CameraPosition(); float3 E = normalize(cameraPosition - worldPosition); float value = saturate(dot(R, E)); // 0~1 사이의 값으로 제한 float specular = pow(value, 10); specularColor = GlobalLight.specular * Material.specular * specular; } // Emissive 처리 { float3 cameraPosition = CameraPosition(); float3 E = normalize(cameraPosition - worldPosition); float value = saturate(dot(E, normal)); float emissive = 1.0f - value; emissive = smoothstep(0.0f, 1.0f, emissive); emissive = pow(emissive, 2); emissiveColor = GlobalLight.emissive * Material.emissive * emissive; } return ambientColor + diffuseColor + specularColor + emissiveColor; } // 정규 매핑 계산 함수 void ComputeNormalMapping(inout float3 normal, float3 tangent, float2 uv) { // 텍스처에서 정규 벡터를 샘플링 float4 map = NormalMap.Sample(LinearSampler, uv); if (any(map.rgb) == false) return; float3 N = normalize(normal); // Z 방향 float3 T = normalize(tangent); // X 방향 float3 B = normalize(cross(N, T)); // Y 방향, 외적을 통해 계산 float3x3 TBN = float3x3(T, B, N); // 탄젠트 공간의 기저를 형성 // 탄젠트 공간 정규 벡터를 [-1,1] 범위로 재조정 float3 tangentSpaceNormal = (map.rgb * 2.0f - 1.0f); float3 worldNormal = mul(tangentSpaceNormal, TBN); normal = worldNormal; } #endif
프로젝트 호출
LightingDemo.h
#pragma once #include "IExecute.h" #include "Geometry.h" #include "GameObject.h" class LightingDemo : public IExecute { public: void Init() override; void Update() override; void Render() override; shared_ptr<Shader> _shader; //Object shared_ptr<GameObject> _obj; shared_ptr<GameObject> _obj2; shared_ptr<GameObject> _camera; };
LightingDemo.cpp
#include "pch.h" #include "16. LightingDemo.h" #include "GeometryHelper.h" #include "Camera.h" #include "CameraScript.h" #include "Transform.h" #include "Texture.h" #include "ResourceBase.h" #include "MeshRenderer.h" #include "GameObject.h" //#include "RenderManager.h" void MaterialDemo::Init() { RESOURCES->Init(); _shader = make_shared<Shader>(L"13. Lighting.fx"); // Material { shared_ptr<Material> material = make_shared<Material>(); { material->SetShader(_shader); } { auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg"); material->SetDiffuseMap(texture); } MaterialDesc& desc = material->GetMaterialDesc(); desc.ambient = Vec4(1.f); desc.diffuse = Vec4(1.f); desc.specular = Vec4(1.f); RESOURCES->Add(L"Veigar", material); } // Camera _camera = make_shared<GameObject>(); _camera->GetOrAddTransform()->SetPosition(Vec3{0.f, 0.f, -10.f}); _camera->AddComponent(make_shared<Camera>()); _camera->AddComponent(make_shared<CameraScript>()); // Object _obj = make_shared<GameObject>(); _obj->GetOrAddTransform(); _obj->AddComponent(make_shared<MeshRenderer>()); { auto mesh = RESOURCES->Get<Mesh>(L"Sphere"); _obj->GetMeshRenderer()->SetMesh(mesh); } { auto material = RESOURCES->Get<Material>(L"Veigar"); _obj->GetMeshRenderer()->SetMaterial(material); } // Object2 _obj2 = make_shared<GameObject>(); _obj2->GetOrAddTransform()->SetPosition(Vec3{0.5f, 0.f, 2.f}); _obj2->AddComponent(make_shared<MeshRenderer>()); { auto mesh = RESOURCES->Get<Mesh>(L"Cube"); _obj2->GetMeshRenderer()->SetMesh(mesh); } { auto material = RESOURCES->Get<Material>(L"Veigar")->Clone(); MaterialDesc& desc = material->GetMaterialDesc(); //desc.ambient = Vec4(0.f); //desc.diffuse = Vec4(0.f); _obj2->GetMeshRenderer()->SetMaterial(material); } RENDER->Init(_shader); } void MaterialDemo::Update() { _camera->Update(); RENDER->Update(); { LightDesc lightDesc; lightDesc.ambient = Vec4(0.5f); lightDesc.diffuse = Vec4(1.f); lightDesc.specular = Vec4(1.f, 1.f, 1.f, 1.f); lightDesc.direction = Vec3(0.f, -1.f, 0.f); RENDER->PushLightData(lightDesc); } { _obj->Update(); } { _obj2->Update(); } } void LightingDemo::Render() { }
결론
3D 그래픽에서 조명은 장면의 분위기와 재질의 느낌을 결정하는 중요한 요소입니다. Ambient, Diffuse, Specular, Emissive 조명 기법을 적절히 활용함으로써, 더욱 실감나고 다양한 표현이 가능한 3D 모델과 씬을 생성할 수 있습니다.
반응형다음글이전글이전 글이 없습니다.댓글