이제 3D모델을 파일로부터 읽어와서 렌더링하는 기능을 추가하려고 하였으나, 엔진에서 문제가 발견되었다

 

일단, 현재 구조는 하나의 렌더러가 하나의 매쉬를 렌더링하는 방식인데 실제 3D 모델은 대부분 여러개의 메쉬로 구성되어 있다. 각각 다른 쉐이더를 적용할 수도 있고, 서로 다른 버텍스버퍼, 인덱스 버퍼 등을 가지고 있어야 한다. 그렇기 때문에, 하나의 렌더러가 여러개의 메쉬 정보를 보유하도록 구조를 바꾸기로 하였다,.

 

struct EMeshData
{
	std::vector<struct EVertex> Vertices;
	std::vector<uint16_t> Indices;
	std::string TextureName = "";
};

 

일단, 버텍스와 인덱스 그리고 텍스쳐 이름을 하나의 구조체에 담아주었다.

파일로부터 데이터를 읽어올 때, 구조체를 사용하는 편이 아무래도 편할 것 같았다.

std::wstring VSShader = L"";
std::wstring PSShader = L"";

 

멤버변수에 세팅할 버텍스 쉐이더와 픽셀 쉐이더의 이름도 저장해주도록 하였다.

//std::list<EConstantBufferData> ConstantBuffers;

std::unordered_map<std::wstring, std::list<EConstantBufferData>> VSConstantBuffers;
std::unordered_map<std::wstring, std::list<EConstantBufferData>> PSConstantBuffers;

상수버퍼를 담고 있던 자료구조도 수정해주었다.

어떤 쉐이더에 대해 어떤 상수버퍼를 추가할 것인가를 설정하는 것이다.

 

기존에는 모든 상수버퍼를 모든 쉐이더에 세팅하도록 설정했었지만, 앞으로 다양한 쉐이더를 사용할 것에 대비하 이렇게 수정해주었다.

VertexShaderData VSData = EngineBase::GetInstance().GetVertexShaderData(VSShader);
Microsoft::WRL::ComPtr<ID3D11PixelShader> PS = EngineBase::GetInstance().GetPixelShaderData(PSShader);

 

렌더함수에서 쉐이더를 세팅하던 것을 기존엔 리터럴로 하고 있었는데, 이젠 설정된 쉐이더의 이름으로 하도록 하였다.

int Index = 0;
for (const EConstantBufferData& _Data : VSConstantBuffers[VSShader])
{
    EngineBase::GetInstance().GetContext()->VSSetConstantBuffers(Index, 1, _Data.ConstantBuffer.GetAddressOf());
    Index++;
}

Index = 0;
for (const EConstantBufferData& _Data : PSConstantBuffers[PSShader])
{
    EngineBase::GetInstance().GetContext()->PSSetConstantBuffers(Index, 1, _Data.ConstantBuffer.GetAddressOf());
    Index++;
}

 

상수버퍼를 세팅하는 함수도 이처럼 VS와 PS를 따로 세팅해주도록 하였다.

 

상수버퍼가 지금은 RenderBase에 있지만, 나중엔 렌더러에서 가지고 있도록 할 것이다.

 

바꿀 구조는 대충 이렇다.

RenderBase는 메쉬별로 렌더링을 담당하는 객체로 바꿀 것이다. Renderer클래스는 메쉬의 수만큼 RenderBase를 보유할 것이며 트랜스폼과 같은 상수버퍼는 Renderer단위로 보유할 것이다.

 

본격적으로 구조를 수정해보도록 하겠다.

#pragma once
#include "RenderBase.h"

class Renderer
{

public:

    Renderer();
    ~Renderer();

    Renderer(const Renderer& _Other) = delete;
    Renderer(Renderer&& _Other) noexcept = delete;
    Renderer& operator=(const Renderer& _Other) = delete;
    Renderer& operator=(Renderer&& _Other) noexcept = delete;

protected:

private:
    std::list<RenderBase> RenderUnits;
};

 

먼저 이렇게 렌더러를 만들어 주었다.

멤버변수에는 RenderBase를 저장할 자료구조를 선언해주었고, 하나하나가 렌더링을 실행하는 단위이기 때문에 RenderUnits라는 이름을 붙혀주었다.

 

RenderBase에는 RenderSetting이라는 함수를 만들어주었다.

void RenderBase::RenderSetting()
{
    UINT Stride = sizeof(EVertex);
    UINT Offset = 0;

    VertexShaderData VSData = EngineBase::GetInstance().GetVertexShaderData(VSShader);
    Microsoft::WRL::ComPtr<ID3D11PixelShader> PS = EngineBase::GetInstance().GetPixelShaderData(PSShader);

    EngineBase::GetInstance().GetContext()->IASetVertexBuffers(0, 1, VertexBuffer.GetAddressOf(), &Stride, &Offset);
    EngineBase::GetInstance().GetContext()->IASetIndexBuffer(IndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);
    EngineBase::GetInstance().GetContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    EngineBase::GetInstance().GetContext()->VSSetShader(VSData.VertexShader.Get(), 0, 0);
    EngineBase::GetInstance().GetContext()->IASetInputLayout(VSData.InputLayout.Get());
    EngineBase::GetInstance().GetContext()->PSSetShader(PS.Get(), 0, 0);

    //추후 텍스쳐 여러개 세팅할 수도 있다.
    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> SRV = EngineBase::GetInstance().GetTextureData(MeshData.TextureName).ShaderResourceView;
    Microsoft::WRL::ComPtr<ID3D11SamplerState> Sampler = EngineBase::GetInstance().GetSampler(SamplerName);

    EngineBase::GetInstance().GetContext()->PSSetShaderResources(0, 1, SRV.GetAddressOf());
    EngineBase::GetInstance().GetContext()->PSSetSamplers(0, 1, Sampler.GetAddressOf());
}

 

기존의 Render함수에 있던 코드 일부이다.

Context에 쉐이더, 버퍼 등을 세팅하는 코드이다.

 

void RenderBase::Render(float _DeltaTime)
{
    UINT IndexCount = (UINT)MeshData.Indices.size();
    EngineBase::GetInstance().GetContext()->DrawIndexed(IndexCount, 0, 0);
}

 

RenderBase의 Render에는 이렇게 드로우콜을 보내는 코드만 남겨두었다.

 

void Renderer::Render(float _DeltaTime)
{
    for (const std::shared_ptr<RenderBase> _RenderUnit : RenderUnits)
    {
        _RenderUnit->RenderSetting();
        SetConstantBuffer(_RenderUnit->GetVSShaderName(), _RenderUnit->GetPSShaderName());
        _RenderUnit->Render(_DeltaTime);
    }
}

void Renderer::SetConstantBuffer(const std::wstring& _VSShaderName, const std::wstring& _PSShaderName)
{
    int Index = 0;
    for (const EConstantBufferData& _Data : VSConstantBuffers[_VSShaderName])
    {
        EngineBase::GetInstance().GetContext()->VSSetConstantBuffers(Index, 1, _Data.ConstantBuffer.GetAddressOf());
        Index++;
    }

    Index = 0;
    for (const EConstantBufferData& _Data : PSConstantBuffers[_PSShaderName])
    {
        EngineBase::GetInstance().GetContext()->PSSetConstantBuffers(Index, 1, _Data.ConstantBuffer.GetAddressOf());
        Index++;
    }
}

 

Renderer의 Render함수에선, 먼저 RenderUnit의 RenderSetting을 호출해준 뒤,Renderer가 가지고 있는 상수버퍼를 세팅해주었고, 이후 RenderUnit의 Render함수를 호출하여 드로우콜을 보내도록 하였다.

 

Update코드도 Renderer로 옮겨주었고, Init함수도 옮겨주었다. 그 외에 Name이나 Material같은 변수들도 모두 Renderer로 옮겨주었다.

 

std::list<std::shared_ptr<class Renderer>> Renderers;

 

EngineBase에서도 RenderBase가 아니라 Renderer단위로 렌더링하도록 자료구조를 수정해주었다.

 

기존의 BoxRenderer가 Renderer를 상속받도록 하였고, 임시로 RenderUnit을 하나 추가하여 렌더링을 테스트해보았다.

 

 

 

일단은 실행이 잘된다.

아마 놓친 부분이 꽤 있을 것 같은데, 3D 모델 로딩을 구현하고 테스트하면서 천천히 찾아가면서 고쳐가면 될 듯 하다.

+ Recent posts