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(00); // 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(01&_vertexBufferView); // Slot: (0~15)
    CMD_LIST->DrawInstanced(_vertexCount, 100);
}
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, 00, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
        { "COLOR"0, DXGI_FORMAT_R32G32B32A32_FLOAT, 012, 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 정점 세가지를 이용해서 삼각형이 만들어졌다.

세 정점 사이의 색이 채워진 이유는 위의 내용과 렌더링파이프라인의 부분에서 설명했듯 레스터라이저에 의해서 정점과의 거리, 색상을 통해서 자동으로 보간이 되어 채워진 것이다.

+ Recent posts