[DirectX11] 075~77 Lighting, HLSL
DirectX 11에서 Lighting을 구현하려면, HLSL(고수준 셰이더 언어)을 사용하여 셰이더 코드를 작성해야 한다. HLSL은 Direct3D에서 셰이더 프로그래밍을 위해 사용되는 언어로, 셰이더 코드를 작성하고 컴파일하여 GPU에서 실행할 수 있는 바이너리 코드로 변환한다.
목차
Lighting
Lighting을 구현하는 방법은 다양하지만, 일반적으로는 Phong 또는 Blinn-Phong Lighting 모델을 사용한다. 이 모델은 ambient, diffuse, specular lighting을 사용하여 표면의 색상을 계산한다. 각각의 조명 구성 요소는 다음과 같이 계산된다.
Ambient lighting
float4 ambientColor = float4(0.2, 0.2, 0.2, 1.0); // ambient lighting의 색상값 float4 ambientLight = ambientColor * materialColor; //ambient lighting과 material color를 곱해서 계산
Diffuse lighting
float3 lightDirection = normalize(float3(0.0, 1.0, 0.0)); // 조명의 방향 벡터 float4 diffuseColor = float4(1.0, 1.0, 1.0, 1.0); // diffuse lighting의 색상값 float4 diffuseLight = saturate(dot(surfaceNormal, lightDirection)) * diffuseColor * materialColor; //surface normal(표면 법선 벡터)와 조명의 방향 벡터의 내적, diffuse lighting과 material color를 곱해서 계산
Specular lighting
float3 eyeDirection = normalize(eyePosition - surfacePosition); // 눈의 방향 벡터 float3 reflectDirection = reflect(-lightDirection, surfaceNormal); // 조명의 반사 방향 벡터 float4 specularColor = float4(1.0, 1.0, 1.0, 1.0); // specular lighting의 색상값 float4 specularLight = pow(saturate(dot(reflectDirection, eyeDirection)), materialShininess) * specularColor * materialColor; // 반사 방향 벡터와 눈의 방향 벡터의 내적, shininess(광택 정도)의 제곱, specular lighting과 material color를 곱해서 계산
이러한 Lighting 요소들은 조명과 재질(물체의 속성)에 따라 다양하게 계산될 수 있다. 이러한 HLSL 코드는 셰이더에서 조명과 재질을 계산하는 데 사용된다.
Shader | |
Light.fx Lighting.fx |
|
UnitTest | |
Lighting | |
LightingDemo.h .cpp 생성 |
빛 연산
Diffuse Color x Texture x NdotL(Lambert)
Specular Color x Texture x (Phong)
Light(광원에서 쏘아진 빛), Reflection(반사), Eye(시야로 가는선) 이 있을때
- 내적 dot(Reflection, Eye)를 구한 뒤
- power(R dot E, 상수값) 를 구한다.
- pow(0.5, 10)이라면 0.5^10
- 상수값이 커질수록 원(=범위)은 커지지만 값을 흐려진다.
- 상수값이 작을수록 원(=범위)은 작아지지만 빛이 강해진다.
윤곽선 구하기
윤곽선(=외각선)을 구하는 방법
- 눈(Eye)의 방향
- Normal의 방향
- 두 개를 사용하여 구할 수 있다.
바라보는 시점의 외각 = Normal 벡터와의 내적
Global.fx
Global.fx
float3 ViewPosition() { return ViewInverse._41_42_43; //카메라 변환되었던 값을 역행렬로 카메라 원래 위치를 구함 }
카메라의 원본 위치를 구한다.
Light.fx
Light.fx
struct LightDesc { float4 Ambient; float4 Specular; float3 Direction; float Padding; float3 Position; }; cbuffer CB_Light { LightDesc GlobalLight; }; Texture2D DiffuseMap; Texture2D SpecularMap; Texture2D NormalMap; TextureCube SkyCubeMap; Texture2D ShadowMap; SamplerComparisonState ShadowSampler; struct MaterialDesc { float4 Ambient; float4 Diffuse; float4 Specular; float4 Emissive; }; cbuffer CB_Material { MaterialDesc Material; }; MaterialDesc MakeMaterial() { MaterialDesc output; output.Ambient = float4(0, 0, 0, 0); output.Diffuse = float4(0, 0, 0, 0); output.Specular = float4(0, 0, 0, 0); output.Emissive = float4(0, 0, 0, 0); return output; } float3 MaterialToColor(MaterialDesc result) { return (result.Ambient + result.Diffuse + result.Specular + result.Emissive).rgb; } void AddMaterial(inout MaterialDesc result, MaterialDesc val) { result.Ambient += val.Ambient; result.Diffuse += val.Diffuse; result.Specular += val.Specular; result.Emissive += val.Emissive; } void Texture(inout float4 color, Texture2D t, float2 uv, SamplerState samp) { float4 sampling = t.Sample(samp, uv); color = color * sampling; } void Texture(inout float4 color, Texture2D t, float2 uv) { Texture(color, t, uv, LinearSampler); } void ComputeLight(out MaterialDesc output, float3 normal, float3 wPosition) { output = MakeMaterial(); float3 direction = -GlobalLight.Direction; float NdotL = dot(direction, normalize(normal)); output.Ambient = GlobalLight.Ambient * Material.Ambient; float3 E = normalize(ViewPosition() - wPosition); [flatten] if(NdotL > 0.0f) { output.Diffuse = Material.Diffuse * NdotL; [flatten] if(Material.Specular.a > 0.0f) { float3 R = normalize(reflect(-direction, normal)); float RdotE = saturate(dot(R, E)); float specular = pow(RdotE, Material.Specular.a); output.Specular = Material.Specular * specular * GlobalLight.Specular; } } [flatten] if(Material.Emissive.a > 0.0f) { float NdotE = dot(E, normalize(normal)); float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE)); output.Emissive = Material.Emissive * emissive; } } #define MAX_POINT_LIGHTS 256 struct PointLight { float4 Ambient; float4 Diffuse; float4 Specular; float4 Emissive; float3 Position; float Range; float Intensity; float3 Padding; }; cbuffer CB_PointLights { uint PointLightCount; float3 CB_PointLights_Padding; PointLight PointLights[MAX_POINT_LIGHTS]; }; void ComputePointLight(inout MaterialDesc output, float3 normal, float3 wPosition) { output = MakeMaterial(); MaterialDesc result = MakeMaterial(); for (uint i = 0; i < PointLightCount; i++) //PointLight 개수만큼 for문을 돈다. { float3 light = PointLights[i].Position - wPosition; float dist = length(light); [flatten] if(dist > PointLights[i].Range) continue; light /= dist; //Normalize result.Ambient = PointLights[i].Ambient * Material.Ambient; float NdotL = dot(light, normalize(normal)); float3 E = normalize(ViewPosition() - wPosition); [flatten] if (NdotL > 0.0f) { result.Diffuse = Material.Diffuse * NdotL * PointLights[i].Diffuse; [flatten] if (Material.Specular.a > 0.0f) { float3 R = normalize(reflect(-light, normal)); float RdotE = saturate(dot(R, E)); float specular = pow(RdotE, Material.Specular.a); result.Specular = Material.Specular * specular * PointLights[i].Specular; } } [flatten] if (Material.Emissive.a > 0.0f) { float NdotE = dot(E, normalize(normal)); float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE)); result.Emissive = Material.Emissive * emissive * PointLights[i].Emissive; } float temp = 1.0f / saturate(dist / PointLights[i].Range); float att = temp * temp * (1.0f / max(1.0f - PointLights[i].Intensity, 1e-8f)); output.Ambient += result.Ambient * temp; output.Diffuse += result.Diffuse * att; output.Specular += result.Specular * att; output.Emissive += result.Emissive * att; } } #define MAX_SPOT_LIGHTS 256 struct SpotLight { float4 Ambient; float4 Diffuse; float4 Specular; float4 Emissive; float3 Position; float Range; float3 Direction; float Angle; float Intensity; float3 Padding; }; cbuffer CB_SpotLights { uint SpotLightCount; float3 CB_SpotLights_Padding; SpotLight SpotLights[MAX_SPOT_LIGHTS]; }; void ComputeSpotLight(inout MaterialDesc output, float3 normal, float3 wPosition) { output = MakeMaterial(); MaterialDesc result = MakeMaterial(); for (uint i = 0; i < SpotLightCount; i++) { float3 light = SpotLights[i].Position - wPosition; float dist = length(light); [flatten] if (dist > SpotLights[i].Range) continue; light /= dist; //Normalize result.Ambient = SpotLights[i].Ambient * Material.Ambient; float NdotL = dot(light, normalize(normal)); float3 E = normalize(ViewPosition() - wPosition); [flatten] if (NdotL > 0.0f) { result.Diffuse = Material.Diffuse * NdotL * SpotLights[i].Diffuse; [flatten] if (Material.Specular.a > 0.0f) { float3 R = normalize(reflect(-light, normal)); float RdotE = saturate(dot(R, E)); float specular = pow(RdotE, Material.Specular.a); result.Specular = Material.Specular * specular * SpotLights[i].Specular; } } [flatten] if (Material.Emissive.a > 0.0f) { float NdotE = dot(E, normalize(normal)); float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE)); result.Emissive = Material.Emissive * emissive * SpotLights[i].Emissive; } float temp = pow(saturate(dot(-light, SpotLights[i].Direction)), SpotLights[i].Angle); float att = temp * (1.0f / max(1.0f - SpotLights[i].Intensity, 1e-8f)); output.Ambient += result.Ambient * temp; output.Diffuse += result.Diffuse * att; output.Specular += result.Specular * att; output.Emissive += result.Emissive * att; } } void NormalMapping(float2 uv, float3 normal, float3 tangent, SamplerState samp) { float4 map = NormalMap.Sample(samp, uv); [flatten] if (any(map.rgb) == false) return; float3 coord = map.rgb * 2.0f - 1.0f; //-1 ~ +1 //탄젠트 공간 float3 N = normalize(normal); //Z float3 T = normalize(tangent - dot(tangent, N) * N); //X //float3 T = tangent; //float3 T = float3(1, 0, 0); float3 B = cross(N, T); //Y float3x3 TBN = float3x3(T, B, N); coord = mul(coord, TBN); Material.Diffuse *= saturate(dot(-GlobalLight.Direction, coord)); } void NormalMapping(float2 uv, float3 normal, float3 tangent) { NormalMapping(uv, normal, tangent, LinearSampler); } float4 PS_AllLight(MeshOutput input) { NormalMapping(input.Uv, input.Normal, input.Tangent); Texture(Material.Diffuse, DiffuseMap, input.Uv); Texture(Material.Specular, SpecularMap, input.Uv); MaterialDesc output = MakeMaterial(); MaterialDesc result = MakeMaterial(); ComputeLight(output, input.Normal, input.wPosition); AddMaterial(result, output); ComputePointLight(output, input.Normal, input.wPosition); AddMaterial(result, output); ComputeSpotLight(output, input.Normal, input.wPosition); AddMaterial(result, output); return float4(MaterialToColor(result).rgb, 1.0f); } Texture2D ProjectorMap; struct ProjectorDesc { matrix View; matrix Projection; float4 Color; }; cbuffer CB_Projector { ProjectorDesc Projector; }; void VS_Projector(inout float4 wvp, float4 oPosition) { wvp = WorldPosition(oPosition); wvp = mul(wvp, Projector.View); wvp = mul(wvp, Projector.Projection); } void PS_Projector(inout float4 color, float4 wvp) { float3 uvw = 0; uvw.x = wvp.x / wvp.w * 0.5f + 0.5f; uvw.y = -wvp.y / wvp.w * 0.5f + 0.5f; uvw.z = wvp.z / wvp.w; [flatten] if (saturate(uvw.x) == uvw.x && saturate(uvw.y) == uvw.y && saturate(uvw.z) == uvw.z) { float4 map = ProjectorMap.Sample(LinearSampler, uvw.xy); map.rgb *= Projector.Color.rgb; color = lerp(color, map, map.a); } } cbuffer CB_Shadow { matrix ShadowView; matrix ShadowProjection; float2 ShadowMapSize; float ShadowBias; uint ShadowQuality; };
ComputeLight 부분 - ComputePointLight, ComputeSpotLight도 같은 원리
void ComputeLight(out MaterialDesc output, float3 normal, float3 wPosition) { output = MakeMaterial(); float3 direction = -GlobalLight.Direction; float NdotL = dot(direction, normalize(normal)); output.Ambient = GlobalLight.Ambient * Material.Ambient; float3 E = normalize(ViewPosition() - wPosition); [flatten] if(NdotL > 0.0f) { output.Diffuse = Material.Diffuse * NdotL; [flatten] if(Material.Specular.a > 0.0f) { float3 R = normalize(reflect(-direction, normal)); float RdotE = saturate(dot(R, E)); float specular = pow(RdotE, Material.Specular.a); output.Specular = Material.Specular * specular * GlobalLight.Specular; } } [flatten] if(Material.Emissive.a > 0.0f) { float NdotE = dot(E, normalize(normal)); float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE)); output.Emissive = Material.Emissive * emissive; } }

Material를 Lighting.fx에 넘겨준다.
Lighting
Lighting.fx
#include "00_Global.fx" #include "00_Light.fx" #include "00_Render.fx" float4 PS(MeshOutput input) : SV_Target { Texture(Material.Diffuse, DiffuseMap, input.Uv); Texture(Material.Specular, SpecularMap, input.Uv); MaterialDesc output; ComputeLight(output, input.Normal, input.wPosition); return float4(MaterialToColor(output), 1.0f); } technique11 T0 { P_VP(P0, VS_Mesh, PS) P_VP(P1, VS_Model, PS) P_VP(P2, VS_Animation, PS) }
LightingDemo
LightingDemo.h
#pragma once #include "Systems/IExecute.h" class LightingDemo : public IExecute { public: virtual void Initialize() override; virtual void Ready() override {} virtual void Destroy() override {} virtual void Update() override; virtual void PreRender() override {} virtual void Render() override; virtual void PostRender() override {} virtual void ResizeScreen() override {} private: void Mesh(); void Airplane(); void Kachujin(); void KachujinCollider(); void KachujinWeapon(); void Pass(UINT mesh, UINT model, UINT anim); private: Shader* shader; CubeSky* sky; Material* floor; Material* stone; Material* brick; Material* wall; MeshRender* cube; MeshRender* cylinder; MeshRender* sphere; MeshRender* grid; ModelRender* airplane = NULL; ModelAnimator* kachujin = NULL; Transform* colliderInitTransforms; ColliderObject** colliders; ModelRender* weapon = NULL; Transform* weaponInitTransform; vector<MeshRender *> meshes; vector<ModelRender *> models; vector<ModelAnimator *> animators; };
LightingDemo.cpp
#include "stdafx.h" #include "LightingDemo.h" void LightingDemo::Initialize() { Context::Get()->GetCamera()->RotationDegree(20, 0, 0); Context::Get()->GetCamera()->Position(1, 36, -85); //Performance perfomence; //perfomence.Start(); //{ shader = new Shader(L"75_Lighting.fxo"); //} //float t = perfomence.End(); //MessageBox(D3D::GetDesc().Handle, to_wstring(t).c_str(), L"", MB_OK); sky = new CubeSky(L"Environment/GrassCube1024.dds"); Mesh(); Airplane(); Kachujin(); KachujinCollider(); KachujinWeapon(); } void LightingDemo::Update() { ImGui::ColorEdit3("Ambient", Context::Get()->Ambient()); ImGui::ColorEdit3("Specular", Context::Get()->Specular()); sky->Update(); cube->Update(); grid->Update(); cylinder->Update(); sphere->Update(); airplane->Update(); kachujin->Update(); Matrix worlds[MAX_MODEL_TRANSFORMS]; for (UINT i = 0; i < kachujin->GetTransformCount(); i++) { kachujin->GetAttachTransform(i, worlds); //colliders[i]->Collider->GetTransform()->World(worlds[40]); //colliders[i]->Collider->Update(); weapon->GetTransform(i)->World(weaponInitTransform->World() * worlds[40]); } weapon->UpdateTransforms(); weapon->Update(); } void LightingDemo::Render() { sky->Render(); Pass(0, 1, 2); wall->Render(); sphere->Render(); brick->Render(); cylinder->Render(); stone->Render(); cube->Render(); floor->Render(); grid->Render(); airplane->Render(); kachujin->Render(); weapon->Render(); //for (UINT i = 0; i < kachujin->GetTransformCount(); i++) //colliders[i]->Collider->Render(); } void LightingDemo::Mesh() { //Create Material { floor = new Material(shader); floor->DiffuseMap("Floor.png"); floor->Ambient(0.2f, 0.2f, 0.2f, 20); floor->Specular(1, 1, 1, 20); floor->SpecularMap("Floor_Specular.png"); //floor->NormalMap("Floor_Normal.png"); stone = new Material(shader); stone->DiffuseMap("Stones.png"); stone->Ambient(0.2f, 0.2f, 0.2f, 20); stone->Specular(1, 1, 1, 20); stone->SpecularMap("Stones_Specular.png"); stone->Emissive(0.15f, 0.15f, 0.15f, 0.3f); //stone->NormalMap("Stones_Normal.png"); brick = new Material(shader); brick->DiffuseMap("Bricks.png"); brick->Ambient(0.2f, 0.2f, 0.2f, 20); brick->Specular(1, 0.3f, 0.3f, 20); brick->SpecularMap("Bricks_Specular.png"); brick->Emissive(0.15f, 0.15f, 0.15f, 0.3f); //brick->NormalMap("Bricks_Normal.png"); wall = new Material(shader); wall->DiffuseMap("Wall.png"); wall->Ambient(0.2f, 0.2f, 0.2f, 20); wall->Specular(1, 1, 1, 20); wall->SpecularMap("Wall_Specular.png"); wall->Emissive(0.15f, 0.15f, 0.15f, 0.3f); //wall->NormalMap("Wall_Normal.png"); } //Create Mesh { Transform* transform = NULL; cube = new MeshRender(shader, new MeshCube()); transform = cube->AddTransform(); transform->Position(0, 5, 0); transform->Scale(20, 10, 20); grid = new MeshRender(shader, new MeshGrid(5, 5)); transform = grid->AddTransform(); transform->Position(0, 0, 0); transform->Scale(12, 1, 12); cylinder = new MeshRender(shader, new MeshCylinder(0.5f, 3.0f, 20, 20)); sphere = new MeshRender(shader, new MeshSphere(0.5f, 20, 20)); for (UINT i = 0; i < 5; i++) { transform = cylinder->AddTransform(); transform->Position(-30, 6, -15.0f + (float)i * 15.0f); transform->Scale(5, 5, 5); transform = cylinder->AddTransform(); transform->Position(30, 6, -15.0f + (float)i * 15.0f); transform->Scale(5, 5, 5); transform = sphere->AddTransform(); transform->Position(-30, 15.5f, -15.0f + (float)i * 15.0f); transform->Scale(5, 5, 5); transform = sphere->AddTransform(); transform->Position(30, 15.5f, -15.0f + (float)i * 15.0f); transform->Scale(5, 5, 5); } } sphere->UpdateTransforms(); cylinder->UpdateTransforms(); cube->UpdateTransforms(); grid->UpdateTransforms(); meshes.push_back(sphere); meshes.push_back(cylinder); meshes.push_back(cube); meshes.push_back(grid); } void LightingDemo::Airplane() { airplane = new ModelRender(shader); airplane->ReadMesh(L"B787/Airplane"); airplane->ReadMaterial(L"B787/Airplane"); Transform* transform = airplane->AddTransform(); transform->Position(2.0f, 9.91f, 2.0f); transform->Scale(0.004f, 0.004f, 0.004f); airplane->UpdateTransforms(); models.push_back(airplane); } void LightingDemo::Kachujin() { kachujin = new ModelAnimator(shader); kachujin->ReadMesh(L"Kachujin/Mesh"); kachujin->ReadMaterial(L"Kachujin/Mesh"); kachujin->ReadClip(L"Kachujin/Sword And Shield Idle"); kachujin->ReadClip(L"Kachujin/Sword And Shield Walk"); kachujin->ReadClip(L"Kachujin/Sword And Shield Run"); kachujin->ReadClip(L"Kachujin/Sword And Shield Slash"); kachujin->ReadClip(L"Kachujin/Salsa Dancing"); Transform* transform = NULL; transform = kachujin->AddTransform(); transform->Position(0, 0, -30); transform->Scale(0.075f, 0.075f, 0.075f); kachujin->PlayTweenMode(0, 0, 1.0f); transform = kachujin->AddTransform(); transform->Position(-15, 0, -30); transform->Scale(0.075f, 0.075f, 0.075f); kachujin->PlayTweenMode(1, 1, 1.0f); transform = kachujin->AddTransform(); transform->Position(-30, 0, -30); transform->Scale(0.075f, 0.075f, 0.075f); kachujin->PlayTweenMode(2, 2, 0.75f); transform = kachujin->AddTransform(); transform->Position(15, 0, -30); transform->Scale(0.075f, 0.075f, 0.075f); kachujin->PlayBlendMode(3, 0, 1, 2); kachujin->SetBlendAlpha(3, 1.5f); transform = kachujin->AddTransform(); transform->Position(30, 0, -32.5f); transform->Scale(0.075f, 0.075f, 0.075f); kachujin->PlayTweenMode(4, 4, 0.75f); kachujin->UpdateTransforms(); animators.push_back(kachujin); } void LightingDemo::KachujinCollider() { UINT count = kachujin->GetTransformCount(); colliders = new ColliderObject*[count]; colliderInitTransforms = new Transform(); colliderInitTransforms->Position(-2.9f, 1.45f, -50.0f); colliderInitTransforms->Scale(5, 5, 75); for (UINT i = 0; i < count; i++) { colliders[i] = new ColliderObject(); //colliders[i]->Init = new Transform(); //colliders[i]->Init->Position(0, 0, 0); //colliders[i]->Init->Scale(10, 30, 10); colliders[i]->Transform = new Transform(); //colliders[i]->Collider = new Collider(colliders[i]->Transform, colliders[i]->Init); colliders[i]->Collider = new Collider(colliders[i]->Transform, colliderInitTransforms); } } void LightingDemo::KachujinWeapon() { weapon = new ModelRender(shader); weapon->ReadMesh(L"Weapon/Sword"); weapon->ReadMaterial(L"Weapon/Sword"); UINT count = kachujin->GetTransformCount(); for (UINT i = 0; i < count; i++) weapon->AddTransform(); weapon->UpdateTransforms(); models.push_back(weapon); weaponInitTransform = new Transform(); weaponInitTransform->Position(-2.9f, 1.45f, -6.45f); weaponInitTransform->Scale(0.5f, 0.5f, 0.75f); weaponInitTransform->Rotation(0, 0, 1); } void LightingDemo::Pass(UINT mesh, UINT model, UINT anim) { for (MeshRender* temp : meshes) temp->Pass(mesh); for (ModelRender* temp : models) temp->Pass(model); for (ModelAnimator* temp : animators) temp->Pass(anim); }
실행화면

Specular를 조정한 결과

윤곽선 효과를 적용한 결과
'⭐ DirectX > DirectX11 3D' 카테고리의 다른 글
[DirectX11] 080 Spot Lighting (0) | 2023.03.26 |
---|---|
[DirectX11] 078~79 Point Lighting, HLSL [flatten] [branch] (0) | 2023.03.21 |
[DirectX11] 074 Instancing (0) | 2023.03.18 |
[DirectX11] 071~73 Get MultiBones (0) | 2023.03.15 |
[DirectX11] 070 OBB(Oriented Bounding Box) collision (4) | 2023.03.14 |
댓글
이 글 공유하기
다른 글
-
[DirectX11] 080 Spot Lighting
[DirectX11] 080 Spot Lighting
2023.03.26 -
[DirectX11] 078~79 Point Lighting, HLSL [flatten] [branch]
[DirectX11] 078~79 Point Lighting, HLSL [flatten] [branch]
2023.03.21 -
[DirectX11] 074 Instancing
[DirectX11] 074 Instancing
2023.03.18 -
[DirectX11] 071~73 Get MultiBones
[DirectX11] 071~73 Get MultiBones
2023.03.15
댓글을 사용할 수 없습니다.