Mesh
- 유니티 엔진에서의 캐릭터와 같이 정점으로 이루어진 물체.
- Vec3 pos와 Vec3 color 값을 지닌 정점들의 목록(Vertex 구조체)을 매개변수로 받는다.
- 각 정점마다 위치값과 색상값을 가지고 있으며, 중간에 있는 픽셀들은 위치에 따라 색상이 보간되어 적절한 색상으로 가지게 된다. (레스터라이저)
<Mesh.h>
더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#pragma once
// [유니티짱]과 같이 정점으로 이루어진 물체
class Mesh
{
public:
void Init(vector<Vertex>& vec);
void Render();
private:
ComPtr<ID3D12Resource> _vertexBuffer;
D3D12_VERTEX_BUFFER_VIEW _vertexBufferView = {};
uint32 _vertexCount = 0;
};
|
cs |
<Mesh.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
|
#include "pch.h"
#include "Mesh.h"
#include "Engine.h"
void Mesh::Init(vector<Vertex>& vec)
{
_vertexCount = static_cast<uint32>(vec.size());
uint32 bufferSize = _vertexCount * sizeof(Vertex);
D3D12_HEAP_PROPERTIES heapProperty = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize);
DEVICE->CreateCommittedResource(
&heapProperty,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&_vertexBuffer));
// Copy the triangle data to the vertex buffer.
void* vertexDataBuffer = nullptr;
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
_vertexBuffer->Map(0, &readRange, &vertexDataBuffer);
::memcpy(vertexDataBuffer, &vec[0], bufferSize);
_vertexBuffer->Unmap(0, nullptr);
// Initialize the vertex buffer view.
_vertexBufferView.BufferLocation = _vertexBuffer->GetGPUVirtualAddress();
_vertexBufferView.StrideInBytes = sizeof(Vertex); // 정점 1개 크기
_vertexBufferView.SizeInBytes = bufferSize; // 버퍼의 크기
}
void Mesh::Render()
{
CMD_LIST->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
CMD_LIST->IASetVertexBuffers(0, 1, &_vertexBufferView); // Slot: (0~15)
CMD_LIST->DrawInstanced(_vertexCount, 1, 0, 0);
}
|
cs |
<Cpp 리딩>
Init
- 정점들을 GPU쪽 메모리에 복사를 해야한다.
- 따라서 우선 CreateCommittedResource를 이용하여 bufferSize만큼의 공간을 할당받고, heapProperty라는 것을 통하여 어떤 용도의 공간인지를 설정하여 생성한 뒤 vertexBuffer를 통해 공간을 참조한다.
- vertexBuffer는 GPU 내에 존재하는 공간을 참조하는 것으로, GPU 내의 공간에 데이터를 복사하는 것은 직접 복사하지 못하여 또다른 절차가 필요하다.
- 따라서 데이터를 복사할 수 있게끔 Map이라는 함수를 이용하여 vertexBuffer와 vertexDataBuffer를 연결해준다.
- 이후 memcpy를 통해 vertexDataBuffer에 데이터를 밀어넣어준 뒤 Unmap을 통해 닫아주면 vertexDataBuffer에 복사된 정보가 GPU쪽 메모리에 복사가 된다.
- 마지막으로 Unmap을 하였기 때문에 View를 통해 vertexBuffer의 주소와 정점 1개의 크기, 버퍼의 크기 등을 초기화한다.
Render
- Render 부분은 CommandList에 명령어가 들어가는 시점에서 실행이 된다. (RenderBegin과 RenderEnd 사이)
- IASetPrimitiveTopology = 정점의 형태 설정 (기본적으로 삼각형)
- IASetVertexBuffers = Slot 설정. 0번에 vertexBufferView 활용
- DrawInstanced = vertexCount만큼의 정점 draw
Shader
- 일종의 일감 기술서. 외주 인력들이 뭘 해야할지 기술
- 셰이더 파일을 로드해 같이 건네줌.
- 파일의 경로에 따라 Vertex Shader와 Pixel Shader를 생성
- shader.cpp의 init 부분에선 셰이더의 상태를 정의함과 더불어 파일을 읽으며 piplelineState를 초기화 한다.
<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
|
#include "pch.h"
#include "Shader.h"
#include "Engine.h"
void Shader::Init(const wstring& path)
{
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 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, 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.DepthEnable = FALSE;
_pipelineDesc.DepthStencilState.StencilEnable = FALSE;
_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;
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 |
<Game.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
|
#include "pch.h"
#include "Game.h"
#include "Engine.h"
shared_ptr<Mesh> mesh = make_shared<Mesh>();
shared_ptr<Shader> shader = make_shared<Shader>();
void Game::Init(const WindowInfo& info)
{
GEngine->Init(info);
vector<Vertex> vec(3);
vec[0].pos = Vec3(0.f, 0.5f, 0.5f);
vec[0].color = Vec4(1.f, 0.f, 0.f, 1.f);
vec[1].pos = Vec3(0.5f, -0.5f, 0.5f);
vec[1].color = Vec4(0.f, 1.0f, 0.f, 1.f);
vec[2].pos = Vec3(-0.5f, -0.5f, 0.5f);
vec[2].color = Vec4(0.f, 0.f, 1.f, 1.f);
mesh->Init(vec);
shader->Init(L"..\\Resources\\Shader\\default.hlsli");
GEngine->GetCmdQueue()->WaitSync();
}
void Game::Update()
{
GEngine->RenderBegin();
shader->Update();
mesh->Render();
GEngine->RenderEnd();
}
|
cs |
<default.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
|
struct VS_IN
{
float3 pos : POSITION;
float4 color : COLOR;
};
struct VS_OUT
{
float4 pos : SV_Position;
float4 color : COLOR;
};
VS_OUT VS_Main(VS_IN input)
{
VS_OUT output = (VS_OUT)0;
output.pos = float4(input.pos, 1.f);
output.color = input.color;
return output;
}
float4 PS_Main(VS_OUT input) : SV_Target
{
return input.color;
}
|
cs |
우선 현재는 Game.cpp에서 Red, Green, Blue 세개의 정점을 생성한 뒤 Mesh에 정점을 전달하고 이후 default.hlsli를 init한다.
default.hlsli 셰이더는 그저 정점에 대한 좌표와 컬러를 얻은 후, 그대로 출력을 해주는 기본적인 셰이더로 세개의 정점을 입력했기 때문에 세가지 색상의 정점이 찍히고, 그 것들이 이어져 RGB가 섞인 삼각형을 이룬다.
결과물은 위에서 설명한 것과 같이 12시에 Red, 7시에 Blue, 5시에 Green 정점 세가지를 이용해서 삼각형이 만들어졌다.
세 정점 사이의 색이 채워진 이유는 위의 내용과 렌더링파이프라인의 부분에서 설명했듯 레스터라이저에 의해서 정점과의 거리, 색상을 통해서 자동으로 보간이 되어 채워진 것이다.
'Grahpics' 카테고리의 다른 글
[Graphics] DirectX12 - Root Signature (루트서명) (0) | 2022.01.12 |
---|---|
[Graphics] DirectX12 - Constant Buffer (상수버퍼) (0) | 2022.01.12 |
[Graphics] DirectX12 장치 초기화 (5) - DesriptorHeap (0) | 2022.01.11 |
[Graphics] DirectX12 장치 초기화 (4) - SwapChain (0) | 2022.01.10 |
[Graphics] DirectX12 장치 초기화 (3) - CommnadQueue (0) | 2022.01.09 |