렌더링 파이프라인을 설정하기 전에, 렌더러의 간단한 구조를 먼저 잡고 가는게 좋을 것 같아서 렌더러를 일단 구성해보았다.
#pragma once
#include <memory>
class RenderBase
{
public:
RenderBase();
~RenderBase();
RenderBase(const RenderBase& _Other) = delete;
RenderBase(RenderBase&& _Other) noexcept = delete;
RenderBase& operator=(const RenderBase& _Other) = delete;
RenderBase& operator=(RenderBase&& _Other) noexcept = delete;
public:
virtual void Init() = 0;
virtual void Render() = 0;
protected:
std::vector<Vertex> Vertices;
std::vector<uint16_t> Indices;
Microsoft::WRL::ComPtr<ID3D11Buffer> VertexBuffer;
Microsoft::WRL::ComPtr<ID3D11Buffer> IndexBuffer;
Microsoft::WRL::ComPtr<ID3D11Buffer> ConstantBuffer;
private:
};
일단, 모든 렌더러들은 RenderBase를 상속받도록 설정하였다.
EngineBase에서는 RenderBase으로 렌더러들을 저장한 뒤, 렌더링 할 때 Render함수를 실행할 것이기 때문에, Render함수를 순수가상함수로 만들어 하위 클래스에서 반드시 구현하도록 설정하였다.
Init도 마찬가지로, 렌더러가 처음 생성될 때 설정해야 하는 것들을 반드시 정의하도록 순수가상함수로 만들어두었다.
그 외에도, 렌더링에 사용될 버텍스 관련 변수들을 선언해주었다.
public:
template <typename T>
static std::shared_ptr<T> CreateRenderer()
{
std::shared_ptr<RenderBase> NewRenderer = std::make_shared<T>();
NewRenderer->Init();
Renderers.insert(NewRenderer);
return std::dynamic_pointer_cast<T>(NewRenderer);
}
렌더러의 생성은 EngineBase 클래스에 static함수로 선언해두었다.
템플릿 함수로 선언하여, RenderBase를 상속받은 클래스라면 어떤 클래스든 생성할 수 있도록 구성하였고
EngineBase가 소유하고 있는 RenderBase를 담는 자료구조인 Renderers에 생성된 Renderer를 insert해주었다.
private:
std::list<std::shared_ptr<RenderBase>> Renderers;
렌더러가 몇 개가 될 지 예측이 불가능하기 때문에, 수시로 Reserve를 해주어야 하는 vector대신 list를 사용하였다.
이후, 렌더링 기능이 추가되면 위의 list의 원소를 순회하면서 Render함수를 호출해줄 것이다.
이제, 버텍스 버퍼와 인덱스 버퍼 등 렌더링에 필요한 것들을 추가해보자.
테스트용으로 박스 렌더러 하나를 추가해볼것이다.
#pragma once
#include "RenderBase.h"
class BoxRenderer : public RenderBase
{
public:
BoxRenderer();
~BoxRenderer();
BoxRenderer(const BoxRenderer& _Other) = delete;
BoxRenderer(BoxRenderer&& _Other) noexcept = delete;
BoxRenderer& operator=(const BoxRenderer& _Other) = delete;
BoxRenderer& operator=(BoxRenderer&& _Other) noexcept = delete;
public:
virtual void Render() override{}
virtual void Init() override{}
protected:
private:
void CreateVertexAndIndex();
};
이렇게 RenderBase 클래스를 상속받았으며, Render함수와 Init함수를 오버라이딩 해주었다.
멤버함수로 Vertex와 Index를 만드는 함수도 만들어주었다.
함수 내부는 대강 아래와 같은데, 그냥 노가다이다.
void BoxRenderer::CreateVertexAndIndex()
{
std::vector<DirectX::SimpleMath::Vector3> Positions;
Positions.reserve(24);
std::vector<DirectX::SimpleMath::Vector3> Colors;
Colors.reserve(24);
std::vector<DirectX::SimpleMath::Vector3> Normals;
Normals.reserve(24);
//윗면
Positions.push_back({ -1.0f, 1.0f, -1.0f });
Positions.push_back({ -1.0f, 1.0f, 1.0f });
Positions.push_back({ 1.0f, 1.0f, 1.0f });
Positions.push_back({ 1.0f, 1.0f, -1.0f });
Colors.push_back({1.0f, 0.0f, 0.0f});
Colors.push_back({1.0f, 0.0f, 0.0f});
Colors.push_back({1.0f, 0.0f, 0.0f});
Colors.push_back({1.0f, 0.0f, 0.0f});
Normals.push_back({0.0f, 1.0f, 0.0f});
Normals.push_back({0.0f, 1.0f, 0.0f});
Normals.push_back({0.0f, 1.0f, 0.0f});
Normals.push_back({ 0.0f, 1.0f, 0.0f});
//아랫면
Positions.push_back({ 1.0f, -1.0f, 1.0f });
Positions.push_back({ 1.0f, -1.0f, -1.0f });
Positions.push_back({ -1.0f, -1.0f, -1.0f });
Positions.push_back({ -1.0f, -1.0f, 1.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Normals.push_back({ 0.0f, -1.0f, 0.0f });
Normals.push_back({ 0.0f, -1.0f, 0.0f });
Normals.push_back({ 0.0f, -1.0f, 0.0f });
Normals.push_back({ 0.0f, -1.0f, 0.0f });
//왼쪽
Positions.push_back({ -1.0f, -1.0f, -1.0f });
Positions.push_back({ -1.0f, -1.0f, 1.0f });
Positions.push_back({ -1.0f, 1.0f, 1.0f });
Positions.push_back({ -1.0f, 1.0f, -1.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Normals.push_back({ -1.0f, 0.0f, 0.0f });
Normals.push_back({ -1.0f, 0.0f, 0.0f });
Normals.push_back({ -1.0f, 0.0f, 0.0f });
Normals.push_back({ -1.0f, 0.0f, 0.0f });
//오른쪽
Positions.push_back({ 1.0f, 1.0f, -1.0f });
Positions.push_back({ 1.0f, 1.0f, 1.0f });
Positions.push_back({ 1.0f, -1.0f, 1.0f });
Positions.push_back({ 1.0f, -1.0f, -1.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Normals.push_back({ 1.0f, 0.0f, 0.0f });
Normals.push_back({ 1.0f, 0.0f, 0.0f });
Normals.push_back({ 1.0f, 0.0f, 0.0f });
Normals.push_back({ 1.0f, 0.0f, 0.0f });
//앞쪽
Positions.push_back({ -1.0f, 1.0f, 1.0f });
Positions.push_back({ -1.0f, -1.0f, 1.0f });
Positions.push_back({ 1.0f, -1.0f, 1.0f });
Positions.push_back({ 1.0f, 1.0f, 1.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Normals.push_back({ 0.0f, 0.0f, 1.0f });
Normals.push_back({ 0.0f, 0.0f, 1.0f });
Normals.push_back({ 0.0f, 0.0f, 1.0f });
Normals.push_back({ 0.0f, 0.0f, 1.0f });
//뒷쪽
Positions.push_back({ -1.0f, -1.0f, -1.0f });
Positions.push_back({ -1.0f, 1.0f, -1.0f });
Positions.push_back({ 1.0f, 1.0f, -1.0f });
Positions.push_back({ 1.0f, -1.0f, -1.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Colors.push_back({ 1.0f, 0.0f, 0.0f });
Normals.push_back({ 0.0f, 0.0f, -1.0f });
Normals.push_back({ 0.0f, 0.0f, -1.0f });
Normals.push_back({ 0.0f, 0.0f, -1.0f });
Normals.push_back({ 0.0f, 0.0f, -1.0f });
for (size_t i = 0; i < Positions.size(); i++)
{
Vertex NewVertex;
NewVertex.Position = Positions[i];
NewVertex.Color = Colors[i];
NewVertex.Normal = Normals[i];
Vertices.push_back(NewVertex);
}
Indices = { 0, 1, 2,
0, 2, 3,
4, 6, 5,
4, 7, 6,
8, 9, 10,
8, 10, 11,
12, 13, 14,
12, 14, 15,
16, 17, 18,
16, 18, 19,
20, 21, 22,
20, 22, 23 };
}
이렇게, 버텍스에 관한 정보를 모두 Vertices와 Indices에 담아주었다면, 이 정보를 기반으로 버퍼를 만들어야 한다.
버퍼를 만드는 함수는 RenderBase에 정의해두었다.
그런데, RenderBase에서 버텍스 버퍼를 만들려면 Device가 필요했는데, EngineBase의 Device를 어떻게 참조해야하나 고민하다가 그냥 EngineBase 클래스를 전역변수로 바꿔서 생성해주었다.
private:
EngineBase();
~EngineBase()
{
if (Instance != nullptr)
{
delete (Instance);
}
}
static EngineBase* Instance;
public:
static EngineBase* GetInstance()
{
if (Instance == nullptr)
{
Instance = new EngineBase();
}
return Instance;
}
이렇게, Enginebase를 싱글톤 방식으로 구성한 뒤, 전역변수로 선언해주었다.
이후, EngineBase내부에 GetDevice함수를 추가해주었다.
void RenderBase::CreateVertexBuffer()
{
D3D11_BUFFER_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(bufferDesc));
bufferDesc.Usage = D3D11_USAGE_IMMUTABLE;
bufferDesc.ByteWidth = UINT(sizeof(Vertex) * Vertices.size());
bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.StructureByteStride = sizeof(Vertex);
D3D11_SUBRESOURCE_DATA vertexBufferData = { 0, };
vertexBufferData.pSysMem = Vertices.data();
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
const HRESULT Result =
EngineBase::GetInstance()->GetDevice()->CreateBuffer(&bufferDesc, &vertexBufferData, VertexBuffer.GetAddressOf());
if (Result != S_OK)
{
std::cout << "CreateVertexBuffer() failed. " << std::hex << Result << std::endl;
};
}
그리고 규칙에 맞게 버텍스 버퍼를 생성해주었다.
이렇게 작성하고 보니, 어떤 렌더러에서 버퍼 생성이 실패했는지 가시적으로 확인하기 위해 이름을 부여해주는 것이 좋겠다 싶었고, RenderBase에 이름을 추가해주었다.
BoxRenderer::BoxRenderer()
{
Name = "BOX";
}
그리고 이렇게 생성자에서 이름을 설정해주고, 위의 버텍스 버퍼 생성 코드의 마지막 중에 있는 출력을 아래와 같이 바꿔주었다.
if (Result != S_OK)
{
std::cout << Name << " :" << "CreateVertexBuffer() failed." << std::hex << "\nResult : " << Result << std::endl;
};
void RenderBase::CreateIndexBuffer()
{
D3D11_BUFFER_DESC bufferDesc = {0, };
bufferDesc.Usage = D3D11_USAGE_IMMUTABLE;
bufferDesc.ByteWidth = UINT(sizeof(uint16_t) * Indices.size());
bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.StructureByteStride = sizeof(uint16_t);
D3D11_SUBRESOURCE_DATA indexBufferData = { 0 };
indexBufferData.pSysMem = Indices.data();
indexBufferData.SysMemPitch = 0;
indexBufferData.SysMemSlicePitch = 0;
HRESULT Result =
EngineBase::GetInstance()->GetDevice()->CreateBuffer(&bufferDesc, &indexBufferData, IndexBuffer.GetAddressOf());
if (Result != S_OK)
{
std::cout << Name << " :" << "CreateIndexBuffer() failed." << std::hex << "\nResult : " << Result << std::endl;
};
}
인덱스 버퍼도 만들어주었다.
이제, 상수버퍼도 만들고 쉐이더도 연결해야 하는데, 일단 밥먹고 나서 진행해야겠다.
'프로젝트 > Direct X 그래픽스' 카테고리의 다른 글
프로젝트 : DirectX를 활용한 그래픽스 (6 - 버텍스 쉐이더, 인풋 레이아웃, 픽셀 쉐이더) (1) | 2024.05.01 |
---|---|
프로젝트 : DirectX를 활용한 그래픽스 (5 - 상수 버퍼) (0) | 2024.05.01 |
프로젝트 : DirectX를 활용한 그래픽스(4 - 릭 제거) (0) | 2024.05.01 |
프로젝트 : DirectX를 활용한 그래픽스 (2 - 다이렉트X 초기화) (1) | 2024.04.30 |
프로젝트 : DirectX를 활용한 그래픽스 (1 - 윈도우 초기화) (0) | 2024.04.30 |