템플릿에 대한 이해가 더 필요하다고 생각해 콘솔 프로젝트로 템플릿을 테스트해 보았다.
2020/01/21 - [Programming] - Template 연구 - Enum과 Template 임의 다중 상속
2020/01/22 - [Programming] - Enum Template 응용 :: 강제 형 변환하여 char 데이터 배열을 원하는 타입으로 출력
이제 이렇게 실행이 잘 된다는 것을 확인했으니 내 엔진에 적용해보자.
Vertex 클래스는 다음과 같이 제작했다. VertexLayout을 정보로 가지고 온다.
class Vertex
{
friend class VertexBuffer;
public:
Vertex(VertexLayout& layout);
template<ElementType E>
auto Get()-> typename VertexLayout::Map<E>::SysType&
{
using DestPtr = typename VertexLayout::Map<E>::SysType;
auto element = layout.Get(E);
return *reinterpret_cast<DestPtr*>(data.data() + element.offset);
}
private:
std::vector<char> data;
VertexLayout& layout;
};
이렇게 Vertex클래스를 제작함으로써 얻는 이득은 아래와 같다.
auto vl = VertexLayout({ Position3D, ColorRGBA });
VertexBuffer vb(vl);
Vertex v = vb.Inst();
v.Get<Position3D>() = { -0.5f, 0.0f, 0.0f };
v.Get<ColorRGBA>() = { 1.0f, 0.0f, 0.0f, 1.0f };
vb.Add(v);
v.Get<Position3D>() = { 0.0f, 1.0f, 0.0f };
v.Get<ColorRGBA>() = { 0.0f, 0.0f, 1.0f, 1.0f };
vb.Add(v);
v.Get<Position3D>() = { 0.5f, 0.0f, 0.0f };
v.Get<ColorRGBA>() = { 0.0f, 1.0f, 0.0f, 1.0f };
vb.Add(v);
VertexLayout을 생성하면, Vertex 클래스의 데이터를 동적으로 조정할 수 있다. 여기 VertexBuffer가 나오는데, 이는 Vertex를 그냥 std::vector<Vertex>로 저장할 경우 생기는 문제점이 많기 때문이다. 첫 번째로 Vertex에는 Vertexlayout& 변수로 인해 sizeof(Vertex)를 하면 원래 크기보다 더 큰 값이 나온다. 그리고 위에 나온 것 처럼 그냥 버퍼 클래스를 만들어서 거기서 필요한 vertex 클래스를 Inst() 함수로 받아오는 게 더 안전하다고 생각한다. 실수로 VertexLayout과 다른 Vertex를 만들 수도 있으니까. VertexBuffer 클래스에는 실제 ID3D11Buffer 구조체는 갖고 있지 않다. 단지 vertex를 저장하기 위한 std::vector<char> 가 있을 뿐이다. Vertex의 데이터를 그게 무엇인지 확인하는 기능 없이 그냥 복사해서 저장하는 버퍼의 역할만 하고 있다. 자세한 건 아래 코드 첨부를 보자.
//Vertex Container
class VertexBuffer
{
public:
VertexBuffer(VertexLayout layout);
void Add(Vertex& v);
size_t Size() { return vertices.size(); }
void* SysMem() { return vertices.data(); }
Vertex Inst() { return Vertex(layout); }
private:
std::vector<char> vertices;
VertexLayout layout;
DEFINEPROPERTY:
READONLY_PROPERTY(UINT, byteStride);
GET(byteStride)
{
return layout.GetDataSize();
}
};
이 VertexBuffer를 가지고 Mesh를 생성한다.
Mesh::Mesh(Graphics& gfx, VertexBuffer& v)
:
Bindable(gfx),
stride(v.byteStride)
{
HRESULT hr;
D3D11_BUFFER_DESC bd = {};
D3D11_SUBRESOURCE_DATA sd = {};
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.Usage = D3D11_USAGE_DEFAULT;
bd.CPUAccessFlags = 0u;
bd.MiscFlags = 0u;
bd.ByteWidth = (UINT)v.Size();
bd.StructureByteStride = 0;
sd.pSysMem = v.SysMem();
GFX_THROW_HR(gfx.device.CreateBuffer(&bd, &sd, vertices.GetAddressOf()));
}
Mesh 클래스에서 필요한 정보를 모두 제공하도록 만들었다.
다음은 InputLayout 클래스이다.
class InputLayout : Bindable
{
public:
InputLayout(
Graphics& gfx,
ID3DBlob * pByteCode,
VertexLayout layout);
void Bind();
private:
wrl::ComPtr <ID3D11InputLayout> inputLayout;
VertexLayout layout;
DEFINEPROPERTY:
READONLY_PROPERTY(VertexLayout&, vertexLayout);
GET(vertexLayout) { return layout; }
};
심플하게 VertexLayout의 레퍼런스와 Shader 소스 코드 버퍼를 가지고 레이아웃을 초기화한다.
그리고 실수한 게 있었는데.... 후.. 이것 때문에 허무하게 5시간을 날렸다.
IASetInputLayout함수를 자동완성 기능으로 대충 눌러서 IAGetInputLayout 으로 잘못 써놓고 있었다. 아직 이런 바인딩 함수에 대한 디버깅은 할 수 없어서 계속 검은 화면만 출력이 되고 있었다. 후... 하고픈 말이 많지만 대체 누구에게 한단 말인가. 이 억울함은 모두 나에게서 비롯된 것인데.
그러면 이제 렌더링 파이프라인은 대략 이렇게 된다.
버텍스 버퍼 바인딩 + 삼각형리스트로 세팅 -> 상수버퍼 바인딩 -> 인풋레이아웃 바인딩 -> 버텍스 셰이더 바인딩 -> 픽셀 셰이더 바인딩 -> Draw 함수 호출! -> Present 함수로 화면에 띄우기
'진행과정 기록 > GameEngine' 카테고리의 다른 글
20200129 Input 클래스 수정 (0) | 2020.01.29 |
---|---|
20200128 Camera, imgui (0) | 2020.01.28 |
20200121 enum value를 template parameter로 사용하려면...? (0) | 2020.01.21 |
20200117 (0) | 2020.01.17 |
20200115 (0) | 2020.01.15 |