SkyBox
- 게임 내의 하늘을 구현하는 방법이다.
- 카메라의 가시영역의 끝이 1이라고 가정할 때 거대한 큐브를 만들어 그 안에 카메라를 넣는 방식으로 구현한다.
- 카메라를 skybox 안에 넣고 별 다른 수정이 없다면 다음과 같은 문제가 발생한다.
- 플레이어가 움직여 Skybox에 접근하게 될 경우 Skybox를 뚫고 지나가게 되며, Skybox에 가까워질 수록 sky에 해당하는 텍스처가 더 가까이 보이게된다.
- 만약 Skybox보다 먼 곳에 존재하는 물체가 존재한다면 Depth-Stencil 검사에 의해 Skybox보다 먼 곳에 위치한 물체는 그려지지 않는다.
- Culling에 의해서 반 시계방향으로 이루어진 정점을 그리지 않는다. 카메라는 skybox 큐브 내부에 존재하므로 카메라의 위치에서 보면 skybox는 반 시계방향으로 이루어진 정점들이다. 따라서 그려지지 않는다.
=> 카메라를 이동시켜 큐브 물체를 뚫고 들어가보면 뒷면이 보이지 않는다. 뒷면은 거꾸로 되어 정점이 반 시계방향으로 이루어져있기 때문이다.
=> 따라서 Culling을 하는 이유는 정점의 순서가 반시계 방향이라는 것은 보이지 않는 뒷면이라는 뜻이 되고, 래스터라이저는 이를 포착하여 정점 연산을 스킵하게 되며 이를 통해 연산량을 줄이게 된다.
- 위와 같은 경우는 실제 하늘과 차이가 발생하므로 우리는 몇가지 코드상의 수정을 통해 실제 하늘과 유사하게 구현한다.
- player가 이동해도 관계가 없게끔 Skybox를 View Space의 정점에 위치하게끔 고정시킨다.
- Skybox를 카메라의 가시범위 내에서 가장 먼 위치로 그린다. 카메라가 움질일 경우에도 Skybox는 항상 가시범위내 먼 위치로 유지한다.
=> 위치행렬을 xyww로 설정하여 깊이 값이 무조건 1로 설정되도록 만든다. 즉, SkyBox는 항상 카메라의 가시범위 끝부분에 존재하게 된다. - Culling 규칙을 조정하여 반시계방향의 정점 또한 그려지게 만든다.
=> RASTERIZER_TYPE::CULL_NONE, DEPTH_STENCIL_TYPE::LESS_EQUAL 을 통해 SkyBox는 컬링을 하지 않으며, 뎁스 스텐실 검사는 끝거리까지 포함하여 전부 그려주게 된다.
<Shader.h>
더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
#pragma once
#include "Object.h"
enum class RASTERIZER_TYPE
{
CULL_NONE,
CULL_FRONT,
CULL_BACK,
WIREFRAME,
};
enum class DEPTH_STENCIL_TYPE
{
LESS,
LESS_EQUAL,
GREATER,
GREATER_EQUAL,
};
struct ShaderInfo
{
RASTERIZER_TYPE rasterizerType = RASTERIZER_TYPE::CULL_BACK;
DEPTH_STENCIL_TYPE depthStencilType = DEPTH_STENCIL_TYPE::LESS;
};
class Shader : public Object
{
public:
Shader();
virtual ~Shader();
void Init(const wstring& path, ShaderInfo info = ShaderInfo());
void Update();
private:
void CreateShader(const wstring& path, const string& name, const string& version, ComPtr<ID3DBlob>& blob, D3D12_SHADER_BYTECODE& shaderByteCode);
void CreateVertexShader(const wstring& path, const string& name, const string& version);
void CreatePixelShader(const wstring& path, const string& name, const string& version);
private:
ComPtr<ID3DBlob> _vsBlob;
ComPtr<ID3DBlob> _psBlob;
ComPtr<ID3DBlob> _errBlob;
ComPtr<ID3D12PipelineState> _pipelineState;
D3D12_GRAPHICS_PIPELINE_STATE_DESC _pipelineDesc = {};
};
|
cs |
- 컬링 설정을 위한 RASTERIZER_TYPE, 뎁스스텐실 설정을 위한 DEPTH_STENCIL_TYPE, 두 타입을 저장하기 위한 구조체 ShaderInfo가 추가되었다.
- Init()의 매개변수에 ShaderInfo가 존재하는데, Skybox를 위한 Shader를 생성할 때 RASTERIZER_TYPE과 DEPTH_STENCIL_TYPE를 인자로 넘겨줌으로써 타입을 변경하여 생성함으로써 옵션을 변경하는 방식이다.
<Shader.cpp>
더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
#include "pch.h"
#include "Shader.h"
#include "Engine.h"
Shader::Shader() : Object(OBJECT_TYPE::SHADER)
{
}
Shader::~Shader()
{
}
void Shader::Init(const wstring& path, ShaderInfo info)
{
CreateVertexShader(path, "VS_Main", "vs_5_0");
CreatePixelShader(path, "PS_Main", "ps_5_0");
D3D12_INPUT_ELEMENT_DESC desc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
_pipelineDesc.InputLayout = { desc, _countof(desc) };
_pipelineDesc.pRootSignature = ROOT_SIGNATURE.Get();
_pipelineDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
_pipelineDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
_pipelineDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
_pipelineDesc.SampleMask = UINT_MAX;
_pipelineDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
_pipelineDesc.NumRenderTargets = 1;
_pipelineDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
_pipelineDesc.SampleDesc.Count = 1;
_pipelineDesc.DSVFormat = GEngine->GetDepthStencilBuffer()->GetDSVFormat();
switch (info.rasterizerType)
{
case RASTERIZER_TYPE::CULL_BACK:
_pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
_pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK;
break;
case RASTERIZER_TYPE::CULL_FRONT:
_pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
_pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_FRONT;
break;
case RASTERIZER_TYPE::CULL_NONE:
_pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
_pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
break;
case RASTERIZER_TYPE::WIREFRAME:
_pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
_pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
break;
}
switch (info.depthStencilType)
{
case DEPTH_STENCIL_TYPE::LESS:
_pipelineDesc.DepthStencilState.DepthEnable = TRUE;
_pipelineDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
break;
case DEPTH_STENCIL_TYPE::LESS_EQUAL:
_pipelineDesc.DepthStencilState.DepthEnable = TRUE;
_pipelineDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
break;
case DEPTH_STENCIL_TYPE::GREATER:
_pipelineDesc.DepthStencilState.DepthEnable = TRUE;
_pipelineDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER;
break;
case DEPTH_STENCIL_TYPE::GREATER_EQUAL:
_pipelineDesc.DepthStencilState.DepthEnable = TRUE;
_pipelineDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER_EQUAL;
break;
}
DEVICE->CreateGraphicsPipelineState(&_pipelineDesc, IID_PPV_ARGS(&_pipelineState));
}
void Shader::Update()
{
CMD_LIST->SetPipelineState(_pipelineState.Get());
}
void Shader::CreateShader(const wstring& path, const string& name, const string& version, ComPtr<ID3DBlob>& blob, D3D12_SHADER_BYTECODE& shaderByteCode)
{
uint32 compileFlag = 0;
#ifdef _DEBUG
compileFlag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#endif
if (FAILED(::D3DCompileFromFile(path.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE
, name.c_str(), version.c_str(), compileFlag, 0, &blob, &_errBlob)))
{
::MessageBoxA(nullptr, "Shader Create Failed !", nullptr, MB_OK);
}
shaderByteCode = { blob->GetBufferPointer(), blob->GetBufferSize() };
}
void Shader::CreateVertexShader(const wstring& path, const string& name, const string& version)
{
CreateShader(path, name, version, _vsBlob, _pipelineDesc.VS);
}
void Shader::CreatePixelShader(const wstring& path, const string& name, const string& version)
{
CreateShader(path, name, version, _psBlob, _pipelineDesc.PS);
}
|
cs |
- Init()을 할 때 rasterizerType과 depthStencilType을 switch-case 문으로 검사함으로써 만약 타입이 따로 설정되어 있다면 타입에 맞게 RasterizerState과 DepthStencilState의 옵션을 변경해주어 생성하게 된다.
<Skybox.hlsli>
더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#ifndef _SKYBOX_HLSLI_
#define _SKYBOX_HLSLI_
#include "params.hlsli"
struct VS_IN
{
float3 localPos : POSITION;
float2 uv : TEXCOORD;
};
struct VS_OUT
{
float4 pos : SV_Position;
float2 uv : TEXCOORD;
};
VS_OUT VS_Main(VS_IN input)
{
VS_OUT output = (VS_OUT)0;
// Translation은 하지 않고 Rotation만 적용한다
float4 viewPos = mul(float4(input.localPos, 0), g_matView);
float4 clipSpacePos = mul(viewPos, g_matProjection);
// w/w=1이기 때문에 항상 깊이가 1로 유지된다
output.pos = clipSpacePos.xyww;
output.uv = input.uv;
return output;
}
float4 PS_Main(VS_OUT input) : SV_Target
{
float4 color = g_tex_0.Sample(g_sam_0, input.uv);
return color;
}
#endif
|
cs |
- VS_Main() 부분에선 viewPos를 설정할 때 (input.localPos, 0)라는 설정을 통해 Translation을 제외한 Rotation만 수행하게 된다.
이후 View 좌표에서 Projection 좌표계를 곱해줌으로써 clipSpace의 Pos를 구하게 되며
output의 position을 clipSpace의 xyww값으로 설정하게 해준다. => z값은 z/w가 아닌 w/w가 행해지므로 항상 1이 된다. - PS_Main()은 그저 색상 texture를 불러와 입혀주는 역할을 수행한다.
<SceneManager.cpp>
더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
#include "pch.h"
#include "SceneManager.h"
#include "Scene.h"
#include "Engine.h"
#include "Material.h"
#include "GameObject.h"
#include "MeshRenderer.h"
#include "Transform.h"
#include "Camera.h"
#include "Light.h"
#include "TestCameraScript.h"
#include "Resources.h"
void SceneManager::Update()
{
if (_activeScene == nullptr)
return;
_activeScene->Update();
_activeScene->LateUpdate();
_activeScene->FinalUpdate();
}
// TEMP
void SceneManager::Render()
{
if (_activeScene)
_activeScene->Render();
}
void SceneManager::LoadScene(wstring sceneName)
{
// TODO : 기존 Scene 정리
// TODO : 파일에서 Scene 정보 로드
_activeScene = LoadTestScene();
_activeScene->Awake();
_activeScene->Start();
}
shared_ptr<Scene> SceneManager::LoadTestScene()
{
shared_ptr<Scene> scene = make_shared<Scene>();
#pragma region Camera
shared_ptr<GameObject> camera = make_shared<GameObject>();
camera->AddComponent(make_shared<Transform>());
camera->AddComponent(make_shared<Camera>()); // Near=1, Far=1000, FOV=45도
camera->AddComponent(make_shared<TestCameraScript>());
camera->GetTransform()->SetLocalPosition(Vec3(0.f, 0.f, 0.f));
scene->AddGameObject(camera);
#pragma endregion
#pragma region SkyBox
{
shared_ptr<GameObject> skybox = make_shared<GameObject>();
skybox->AddComponent(make_shared<Transform>());
shared_ptr<MeshRenderer> meshRenderer = make_shared<MeshRenderer>();
{
shared_ptr<Mesh> sphereMesh = GET_SINGLE(Resources)->LoadSphereMesh();
meshRenderer->SetMesh(sphereMesh);
}
{
shared_ptr<Shader> shader = make_shared<Shader>();
shared_ptr<Texture> texture = make_shared<Texture>();
shader->Init(L"..\\Resources\\Shader\\skybox.hlsli",
{ RASTERIZER_TYPE::CULL_NONE, DEPTH_STENCIL_TYPE::LESS_EQUAL });
texture->Init(L"..\\Resources\\Texture\\Sky01.jpg");
shared_ptr<Material> material = make_shared<Material>();
material->SetShader(shader);
material->SetTexture(0, texture);
meshRenderer->SetMaterial(material);
}
skybox->AddComponent(meshRenderer);
scene->AddGameObject(skybox);
}
#pragma endregion
#pragma region Cube
{
shared_ptr<GameObject> sphere = make_shared<GameObject>();
sphere->AddComponent(make_shared<Transform>());
sphere->GetTransform()->SetLocalScale(Vec3(100.f, 100.f, 100.f));
sphere->GetTransform()->SetLocalPosition(Vec3(0.f, 0.f, 150.f));
shared_ptr<MeshRenderer> meshRenderer = make_shared<MeshRenderer>();
{
shared_ptr<Mesh> sphereMesh = GET_SINGLE(Resources)->LoadCubeMesh();
meshRenderer->SetMesh(sphereMesh);
}
{
shared_ptr<Shader> shader = make_shared<Shader>();
shared_ptr<Texture> texture = make_shared<Texture>();
shared_ptr<Texture> texture2 = make_shared<Texture>();
shader->Init(L"..\\Resources\\Shader\\default.hlsli");
texture->Init(L"..\\Resources\\Texture\\Metal.jpg");
texture2->Init(L"..\\Resources\\Texture\\Metal_Normal.jpg");
shared_ptr<Material> material = make_shared<Material>();
material->SetShader(shader);
material->SetTexture(0, texture);
material->SetTexture(1, texture2);
meshRenderer->SetMaterial(material);
}
sphere->AddComponent(meshRenderer);
scene->AddGameObject(sphere);
}
#pragma endregion
#pragma region Green Directional Light
{
shared_ptr<GameObject> light = make_shared<GameObject>();
light->AddComponent(make_shared<Transform>());
//light->GetTransform()->SetLocalPosition(Vec3(0.f, 150.f, 150.f));
light->AddComponent(make_shared<Light>());
light->GetLight()->SetLightDirection(Vec3(1.f, 0.f, 1.f));
light->GetLight()->SetLightType(LIGHT_TYPE::DIRECTIONAL_LIGHT);
light->GetLight()->SetDiffuse(Vec3(0.5f, 0.5f, 0.5f));
light->GetLight()->SetAmbient(Vec3(0.1f, 0.1f, 0.1f));
light->GetLight()->SetSpecular(Vec3(0.3f, 0.3f, 0.3f));
scene->AddGameObject(light);
}
#pragma endregion
return scene;
}
|
cs |
- SkyBox를 생성하는 부분이 추가되었다.
- Sphere (구체)로 SkyBox를 생성하였고 shader를 Init 할 때
RASTERIZER_TYPE::CULL_NONE, DEPTH_STENCIL_TYPE::LESS_EQUAL를 인수로 넘겨줌으로써 래스터라이저와 뎁스스텐실 타입을 변경하였다.
결과
- 여태 배경화면이 그저 검은색이었던 것과 달리 물체의 뒤가 밤하늘과 같은 배경이 되었음을 볼 수 있다.
'Grahpics' 카테고리의 다른 글
[Graphics] DirectX12 - 직교 투영 (Orthographic) (0) | 2022.02.11 |
---|---|
[Graphics] DirectX12 - Frustum Culling (0) | 2022.02.09 |
[Graphics] DirectX12 - Normal Mapping (0) | 2022.02.08 |
[Graphics] DirectX12 - Lighting (0) | 2022.02.07 |
[Graphics] DirectX12 - Lighting 개요 (0) | 2022.02.06 |