렌더링 파이프라인을 설정하기 전에, 렌더러의 간단한 구조를 먼저 잡고 가는게 좋을 것 같아서 렌더러를 일단 구성해보았다.

#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;
    };
}

 

인덱스 버퍼도 만들어주었다.

 

이제, 상수버퍼도 만들고 쉐이더도 연결해야 하는데, 일단 밥먹고 나서 진행해야겠다.

+ Recent posts