[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