[DirectX11] 113-117 Shadow 그림자, PCF (Percentage Closer Filtering)
그림자 구현방법에는 원형 그림자, 투영 그림자, 그리고 깊이 버퍼 그림자 방식이 있다. 이 중 가장 정교한 묘사가 가능한 깊이 버퍼 그림자(Depth Buffer Shadow)를 구현하여 그림자를 표현하였다. Two Pass Rendering 원리를 이용하여 깊이 버퍼 그림자를 계산하고 이를 활용한다.
목차
그림자 구현하기
그림자 구현방법
- 원형 그림자: 고전 방식
- 투영 그림자: 그 다음 등장한 방식. 장면 그대로 렌더링
- 깊이 버퍼 그림자 (Depth Buffer Shadow): 최근 대부분의 게임에서 사용하는 방식.
- 깊이 버퍼를 이용하여 그림자를 구현한다.
깊이 버퍼 그림자 (Depth Buffer Shadow)
- Two Pass Rendering
- 1 Pass: 깊이(DSV) - 조명의 방향을 가지고 Projection 렌더링 진행한다.
- Light 공간(WVP)
- 2 Pass: 깊이를 비교해가면서 실제 렌더링을 수행한다.
- Main Camera 공간(WVP)
- Light 공간(WVP)에서 깊이를 비교한다.
- depth >= z 이면 그림자가 생길 부분이 아니다.
- depth < z 이면 그림자가 지어질 영역이다.
- 여기서 말하는 하나의 패스(Pass)는 IA → VS → RS → PS → OM을 지난다는 것을 의미한다.
- 1 Pass: 깊이(DSV) - 조명의 방향을 가지고 Projection 렌더링 진행한다.
Shaders | |
Render.fx Light.fx Shadow.fx 생성 |
|
Framework | |
Objects | |
Shadow.h .cpp 생성 | |
UnitTest | |
Objects | |
ShadowDemo.h .cpp 생성 |
Render.fx
Render.fx
더보기
struct DepthOutput { float4 Position : SV_Position; float4 sPosition : Position1; }; float4 PS_Depth(DepthOutput input) : SV_Target { float depth = input.Position.z / input.Position.w; return float4(depth, depth, depth, 1.0f); } #define VS_DEPTH_GENERATE \ output.Position = WorldPosition(input.Position); \ output.Position = mul(output.Position, ShadowView); \ output.Position = mul(output.Position, ShadowProjection); \ \ output.sPosition = output.Position; DepthOutput VS_Depth_Mesh(VertexMesh input) { DepthOutput output; SetMeshWorld(World, input); VS_DEPTH_GENERATE return output; } DepthOutput VS_Depth_Model(VertexModel input) { DepthOutput output; SetModelWorld(World, input); VS_DEPTH_GENERATE return output; } DepthOutput VS_Depth_Animation(VertexModel input) { DepthOutput output; if (BlendFrames[input.InstanceID].Mode == 0) SetTweenWorld(World, input); else SetBlendWorld(World, input); VS_DEPTH_GENERATE return output; }
Light.fx
Light.fx
더보기
cbuffer CB_Shadow { matrix ShadowView; matrix ShadowProjection; float2 ShadowMapSize; float ShadowBias; uint ShadowQuality; };
Shadow.fx
Shadow.fx
#include "00_Global.fx" #include "00_Light.fx" #include "00_Render.fx" float4 PS(MeshOutput input) : SV_Target { //float4 color = float4(1, 1, 1, 1); float4 color = PS_AllLight(input); float4 position = input.sPosition; position.xyz /= position.w; [flatten] if (position.x < -1.0f || position.x > +1.0f || position.y < -1.0f || position.y > +1.0f || position.z < +0.0f || position.z > +1.0f) { return color; } position.x = position.x * 0.5f + 0.5f; position.y = -position.y * 0.5f + 0.5f; float depth = 0; float z = position.z - ShadowBias; float factor = 0; if(ShadowQuality == 0) { depth = ShadowMap.Sample(LinearSampler, position.xy).r; factor = (float) (depth >= z); } else if (ShadowQuality == 1) //PCF { depth = position.z; factor = ShadowMap.SampleCmpLevelZero(ShadowSampler, position.xy, depth).r; } else if (ShadowQuality == 2) //PCF + Blur { depth = position.z; float2 size = 1.0f / ShadowMapSize; float2 offsets[] = { float2(-size.x, -size.y), float2(0.0f, -size.y), float2(+size.x, -size.y), float2(-size.x, 0.0f), float2(0.0f, 0.0f), float2(+size.x, 0.0f), float2(-size.x, +size.y), float2(0.0f, +size.y), float2(+size.x, +size.y), }; float2 uv = 0; float sum = 0; [unroll(9)] for (int i = 0; i < 9; i++) { uv = position.xy + offsets[i]; sum += ShadowMap.SampleCmpLevelZero(ShadowSampler, uv, depth).r; } factor = sum / 9.0f; } factor = saturate(factor + depth); return color * factor; } technique11 T0 { //1Pass - Depth Rendering //P_VP(P0, VS_Depth_Mesh, PS_Depth) //P_VP(P1, VS_Depth_Model, PS_Depth) //P_VP(P2, VS_Depth_Animation, PS_Depth) //1Pass - Depth Rendering //z-fighting 문제가 발생하지 않도록 뒤집어(P_RS_VP)준다. P_RS_VP(P0, FrontCounterClockwise_True, VS_Depth_Mesh, PS_Depth) P_RS_VP(P1, FrontCounterClockwise_True, VS_Depth_Model, PS_Depth) P_RS_VP(P2, FrontCounterClockwise_True, VS_Depth_Animation, PS_Depth) P_VP(P3, VS_Mesh, PS) P_VP(P4, VS_Model, PS) P_VP(P5, VS_Animation, PS) }
z-fighting 문제가 발생하지 않도록 뒤집어(P_RS_VP, FrontCounterClockwise)준다.
- technique11 T0 {
//1Pass - Depth Rendering
P_RS_VP(P0, FrontCounterClockwise_True, VS_Depth_Mesh, PS_Depth)
P_RS_VP(P1, FrontCounterClockwise_True, VS_Depth_Model, PS_Depth)
P_RS_VP(P2, FrontCounterClockwise_True, VS_Depth_Animation, PS_Depth) }
Shadow
Shadow.h
더보기
#pragma once class Shadow { public: Shadow(Shader* shader, Vector3& position, float radius, UINT width = 1024, UINT height = 1024); ~Shadow(); void PreRender(); ID3D11ShaderResourceView* SRV() { return renderTarget->SRV(); } private: void CalcViewProjection(); private: struct Desc { Matrix View;//그림자를 그리기 위한 빛 방향에서의 View Projection Matrix Projection; Vector2 MapSize; float Bias = -0.0006f; UINT Quality = 0; } desc; private: Shader* shader; UINT width, height;//그림자 맵의 해상도를 결정한 너비와 높이 Vector3 position;//조명이 빛을 비출 위치 float radius;//조명 구역. 그림자가 들어갈 구역. RenderTarget* renderTarget; DepthStencil* depthStencil; Viewport* viewport; ConstantBuffer* buffer; ID3DX11EffectConstantBuffer* sBuffer;//그림자 정보 넘길 변수 ID3DX11EffectShaderResourceVariable* sShadowMap;//깊이를 줄 변수 ID3D11SamplerState* pcfSampler; ID3DX11EffectSamplerVariable* sPcfSampler; };
Shadow.cpp
더보기
#include "Framework.h" #include "Shadow.h" Shadow::Shadow(Shader * shader, Vector3 & position, float radius, UINT width, UINT height) : shader(shader), position(position), radius(radius), width(width), height(height) { renderTarget = new RenderTarget(width, height); depthStencil = new DepthStencil(width, height); viewport = new Viewport((float)width, (float)height); desc.MapSize = Vector2((float)width, (float)height); buffer = new ConstantBuffer(&desc, sizeof(Desc)); sBuffer = shader->AsConstantBuffer("CB_Shadow"); sShadowMap = shader->AsSRV("ShadowMap"); //Create Sampler State { D3D11_SAMPLER_DESC desc; ZeroMemory(&desc, sizeof(D3D11_SAMPLER_DESC)); desc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR; desc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; desc.MaxAnisotropy = 1; desc.MaxLOD = FLT_MAX; Check(D3D::GetDevice()->CreateSamplerState(&desc, &pcfSampler)); sPcfSampler = shader->AsSampler("ShadowSampler"); } } Shadow::~Shadow() { SafeDelete(renderTarget); SafeDelete(depthStencil); SafeDelete(viewport); SafeDelete(buffer); } void Shadow::PreRender() { ImGui::InputInt("Quality", (int *)&desc.Quality); desc.Quality %= 3; //1Pass ImGui::SliderFloat3("Light Direction", Context::Get()->Direction(), -1, +1); ImGui::SliderFloat("Bias", &desc.Bias, -0.0001f, +0.01f, "%.4f"); renderTarget->PreRender(depthStencil); viewport->RSSetViewport(); CalcViewProjection(); buffer->Render(); sBuffer->SetConstantBuffer(buffer->Buffer()); sShadowMap->SetResource(depthStencil->SRV()); sPcfSampler->SetSampler(0, pcfSampler); } void Shadow::CalcViewProjection() { Vector3 up = Vector3(0, 1, 0); Vector3 direction = Context::Get()->Direction();//주 빛의 방향(=조명 방향) Vector3 position = direction * radius * -2.0f;// D3DXMatrixLookAtLH(&desc.View, &position, &this->position, &up); Vector3 origin; D3DXVec3TransformCoord(&origin, &this->position, &desc.View);//원점을 View공간 만큼 움직인다. float l = origin.x - radius; float b = origin.y - radius; float n = origin.z - radius; float r = origin.x + radius; float t = origin.y + radius; float f = origin.z + radius; D3DXMatrixOrthoLH(&desc.Projection, r - l, t - b, n, f); }
ShadowDemo
ShadowDemo.h
더보기
#pragma once #include "Systems/IExecute.h" class ShadowDemo : 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 PointLighting(); void SpotLighting(); void Pass(UINT mesh, UINT model, UINT anim); private: Shader* shader; Shadow* shadow; Render2D* render2D; 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; };
ShadowDemo.cpp
더보기
#include "stdafx.h" #include "ShadowDemo.h" void ShadowDemo::Initialize() { Context::Get()->GetCamera()->RotationDegree(20, 0, 0); Context::Get()->GetCamera()->Position(1, 36, -85); ((Freedom *)Context::Get()->GetCamera())->Speed(50, 2); shader = new Shader(L"113_Shadow.fxo"); UINT size = 1024;// * 8; * 16; shadow = new Shadow(shader, Vector3(0, 0, 0), 65, size, size); render2D = new Render2D(); //render2D->GetTransform()->Position(D3D::Width() * 0.5f, D3D::Height() * 0.5f, 0); //render2D->GetTransform()->Scale(D3D::Width(), D3D::Height(), 1); render2D->GetTransform()->Position(150, D3D::Height() - 150, 0); render2D->GetTransform()->Scale(300, 300, 1); render2D->SRV(shadow->SRV()); sky = new CubeSky(L"Environment/GrassCube1024.dds"); Mesh(); Airplane(); Kachujin(); KachujinCollider(); KachujinWeapon(); PointLighting(); SpotLighting(); } void ShadowDemo::Update() { 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); weapon->GetTransform(i)->World(weaponInitTransform->World() * worlds[40]); } weapon->UpdateTransforms(); weapon->Update(); render2D->Update(); } void ShadowDemo::PreRender() { shadow->PreRender(); 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(); } void ShadowDemo::Render() { sky->Pass(0); sky->Render(); Pass(3, 4, 5); wall->Render(); sphere->Render(); brick->Render(); cylinder->Render(); stone->Render(); cube->Render(); floor->Render(); grid->Render(); airplane->Render(); kachujin->Render(); weapon->Render(); } void ShadowDemo::PostRender() { render2D->Render(); } void ShadowDemo::Mesh() { //Create Material { floor = new Material(shader); floor->DiffuseMap("Floor.png"); 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->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->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->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 ShadowDemo::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 ShadowDemo::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 ShadowDemo::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 ShadowDemo::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 ShadowDemo::PointLighting() { PointLight light; light = { Color(0.0f, 0.0f, 0.0f, 1.0f), //Ambient Color(0.0f, 0.0f, 1.0f, 1.0f), //Diffuse Color(0.0f, 0.0f, 0.7f, 1.0f), //Specular Color(0.0f, 0.0f, 0.7f, 1.0f), //Emissive Vector3(-30, 10, -30), 5.0f, 0.9f }; Lighting::Get()->AddPointLight(light); light = { Color(0.0f, 0.0f, 0.0f, 1.0f), Color(1.0f, 0.0f, 0.0f, 1.0f), Color(0.6f, 0.2f, 0.0f, 1.0f), Color(0.6f, 0.3f, 0.0f, 1.0f), Vector3(15, 10, -30), 10.0f, 0.3f }; Lighting::Get()->AddPointLight(light); light = { Color(0.0f, 0.0f, 0.0f, 1.0f), //Ambient Color(0.0f, 1.0f, 0.0f, 1.0f), //Diffuse Color(0.0f, 0.7f, 0.0f, 1.0f), //Specular Color(0.0f, 0.7f, 0.0f, 1.0f), //Emissive Vector3(-5, 1, -17.5f), 5.0f, 0.9f }; Lighting::Get()->AddPointLight(light); light = { Color(0.0f, 0.0f, 0.0f, 1.0f), Color(0.0f, 0.0f, 1.0f, 1.0f), Color(0.0f, 0.0f, 0.7f, 1.0f), Color(0.0f, 0.0f, 0.7f, 1.0f), Vector3(-10, 1, -17.5f), 5.0f, 0.9f }; Lighting::Get()->AddPointLight(light); } void ShadowDemo::SpotLighting() { SpotLight light; light = { Color(0.3f, 1.0f, 0.0f, 1.0f), Color(0.7f, 1.0f, 0.0f, 1.0f), Color(0.3f, 1.0f, 0.0f, 1.0f), Color(0.3f, 1.0f, 0.0f, 1.0f), Vector3(-15, 20, -30), 25.0f, Vector3(0, -1, 0), 30.0f, 0.4f }; Lighting::Get()->AddSpotLight(light); light = { Color(1.0f, 0.2f, 0.9f, 1.0f), Color(1.0f, 0.2f, 0.9f, 1.0f), Color(1.0f, 0.2f, 0.9f, 1.0f), Color(1.0f, 0.2f, 0.9f, 1.0f), Vector3(0, 20, -30), 30.0f, Vector3(0, -1, 0), 40.0f, 0.55f }; Lighting::Get()->AddSpotLight(light); } void ShadowDemo::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); }
실행화면

'⭐ DirectX > DirectX11 3D' 카테고리의 다른 글
[DirectX11] 111-112 Projector (0) | 2023.05.10 |
---|---|
[DirectX11] 107-110 Dynamic Cube Map (0) | 2023.05.05 |
[DirectX11] 106 Bloom (0) | 2023.05.03 |
[DirectX11] 104-105 Blur, Gaussian Blur (0) | 2023.04.28 |
[DirectX11] 103 MRT(Multiple Render Targets) (0) | 2023.04.27 |
댓글
이 글 공유하기
다른 글
-
[DirectX11] 111-112 Projector
[DirectX11] 111-112 Projector
2023.05.10 -
[DirectX11] 107-110 Dynamic Cube Map
[DirectX11] 107-110 Dynamic Cube Map
2023.05.05 -
[DirectX11] 106 Bloom
[DirectX11] 106 Bloom
2023.05.03 -
[DirectX11] 104-105 Blur, Gaussian Blur
[DirectX11] 104-105 Blur, Gaussian Blur
2023.04.28
댓글을 사용할 수 없습니다.