기존의 상수버퍼는 하드코딩되어있어서, 업데이트를 할 때마나 데이터 하나하나씩 map, unmap과정을 거쳐주어야 했다.
상수버퍼가 1개일 때는 별 상관 없겠지만, 여러개를 추가하려고 했더니 너무 번거롭고 귀찮은 부분이 많아서 구조를 수정해주었다.
struct ConstantBufferData
{
Microsoft::WRL::ComPtr<ID3D11Buffer> ConstantBuffer;
void* Data = nullptr;
UINT DataSize = 0;
};
먼저, 상수버퍼의 데이터를 담을 구조체를 선언해주었다.
다양한 자료형에 대응해야하기 때문에 void*타입으로 데이터를 담도록 하였다.
또한, 상수버퍼를 업데이트할 때 데이터의 size가 필요한데 void*타입으로는 이를 계산할 수 없으므로 미리 계산하여 구조체에 함께 저장하도록 하였다.
std::list<ConstantBufferData> ConstantBuffers;
각 렌더러는 위와 같이, 상수버퍼를 저장하도록 하였다.
template <typename DataType>
void CreateConstantBuffer(DataType& _Data)
{
D3D11_BUFFER_DESC CBDesc;
CBDesc.ByteWidth = sizeof(DataType);
CBDesc.Usage = D3D11_USAGE_DYNAMIC;
CBDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
CBDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
CBDesc.MiscFlags = 0;
CBDesc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem = &_Data;
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;
Microsoft::WRL::ComPtr<ID3D11Buffer> NewBuffer;
HRESULT Result =
EngineBase::GetInstance().GetDevice()->CreateBuffer(&CBDesc, &InitData, NewBuffer.GetAddressOf());
if (Result != S_OK)
{
std::cout << Name << " :" << "CreateConstantBuffer() failed." << std::hex << "\nResult : " << Result << std::endl;
};
ConstantBufferData NewBufferData;
NewBufferData.ConstantBuffer = NewBuffer;
NewBufferData.Data = reinterpret_cast<void*>(&_Data);
NewBufferData.DataSize = sizeof(_Data);
ConstantBuffers.push_back(NewBufferData);
}
상수버퍼를 생성하는 함수이다. 코드는 전반적으로 동일하지만, 마지막에 list에 상수버퍼데이터를 삽입하는 과정만 추가해주었다.
for (const ConstantBufferData& _Data : ConstantBuffers)
{
D3D11_MAPPED_SUBRESOURCE Ms;
EngineBase::GetInstance().GetContext()->Map(_Data.ConstantBuffer.Get(), NULL, D3D11_MAP_WRITE_DISCARD, NULL, &Ms);
memcpy(Ms.pData, _Data.Data, _Data.DataSize);
EngineBase::GetInstance().GetContext()->Unmap(_Data.ConstantBuffer.Get(), NULL);
}
업데이트는 위와 같이 list의 원소를 순회하며 하나씩 업데이트하도록 해주었다.
int Index = 0;
for (const ConstantBufferData& _Data : ConstantBuffers)
{
EngineBase::GetInstance().GetContext()->VSSetConstantBuffers(Index, 1, _Data.ConstantBuffer.GetAddressOf());
EngineBase::GetInstance().GetContext()->PSSetConstantBuffers(Index, 1, _Data.ConstantBuffer.GetAddressOf());
Index++;
}
상수버퍼를 세팅하는 부분에선 위와 같이 해주었다.
여기서 본인은 동일한 상수버퍼를 버텍스쉐이더와 픽셀쉐이더에 모두 세팅하도록 해주었다. 구조체에 VS의 버퍼인지 PS의 버퍼인지 아니면 둘 다 사용하는 버퍼인지 enum 을 사용하여 분류해줄 수도 있지만, 쉐이더에서 레지스터의 번호와 상수버퍼를 일치시키는 부분에 있어서 위의 코드처럼 하는 것이 더 유리할 것이라고 생각하였다.
예를 들면, 위에서 버퍼를 세팅할 때, list에서 원소의 위치를 기반으로 슬롯 번호를 세팅해주고 있다.
이 때, list의 첫번째 원소는 vs의 버퍼이고 두번째 원소는 ps의 버퍼라고 했을 때, 각각 쉐이더에선 register(b0)을 사용하게 될 것이다. 상수버퍼가 많아지면 레지스터 번호를 매칭하는 것이 다소 복잡해질 수 있다.
하지만, 위처럼 PS와 VS를 동시에 세팅해주면, 첫번째원소는 b0을 사용하고, 두번쨰 원소는 b1을 사용하게 된다. 원소의 위치를 기반으로 레지스터 번호를 쉽게 매칭할 수 있다.
하지만 이 방법도 좋은 방법은 아니라고 생각한다. 왜냐하면 이렇게 하면 사용가능한 상수버퍼의 수가 줄어들기 때문이다.
PS에서만 사용하는 상수버퍼와 VS에서만 사용하는 상수버퍼를 분리하여 세팅하면 각 쉐이더당 16개의 상수버퍼를 등록할 수 있다. 하지만, 위의 코드처럼 세팅하게 되면 PS와 VS를 합쳐서 16개밖에 사용하지 못한다.
그래서 아래와 같은 방법도 생각해보았다.
PS에서 사용하는 상수버퍼와 VS에서 사용하는 상수버퍼를 다른 자료구조에 분리해서 저장한 뒤 사용하는 방식이다.
그런데 이렇게 하면 VS와 PS에서 둘 다 사용하는 상수버퍼를 관리하는 것이 다소 까다로워진다.
그래서 본인은 위의 코드와 같은 방식을 일단 사용하기로 하였다. 왜냐하면 16개 이상의 상수버퍼를 사용할 일이 없을 것 같았기 때문이다.
바뀐 구조를 테스트하기 위해 상수버퍼를 하나 추가해보았다.
상수버퍼의 x값보다 텍스쳐좌표의 x값이 더 크면 파란색을 출력하고, 더 작다면 빨간색을 출력하도록 하였다.
cbuffer UV : register(b1)
{
float4 UV;
};
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 color : COLOR;
float2 TexCoord : TEXCOORD;
};
float4 main(PixelShaderInput _Input) : SV_TARGET
{
if(UV.x > _Input.TexCoord.x)
{
return float4(1.0f, 0.0f, 0.0f, 1.0f);
}
else
{
return float4(0.0f, 0.0f, 1.0f, 1.0f);
}
}
위는 간단하게 작성해본 쉐이더 코드이다.
결과는 아주 만족스럽게 잘 나온다.
x값을 기준으로 했는데 특정 면에서는 좌우가 아니라 상하가 분리되는 이유는 박스를 만들때 버텍스를 그렇게 만들어서 그렇다... 아무튼 잘된다!
'프로젝트 > Direct X 그래픽스' 카테고리의 다른 글
프로젝트 : DirectX를 활용한 그래픽스 (11 - IMGUI, 델타타임 추가) (0) | 2024.05.07 |
---|---|
프로젝트 : DirectX를 활용한 그래픽스 (10 - 텍스쳐링) (0) | 2024.05.07 |
프로젝트 : DirectX를 활용한 그래픽스 (8 - 텍스쳐 좌표 추가) (0) | 2024.05.04 |
프로젝트 : DirectX를 활용한 그래픽스 (7 - 렌더링) (1) | 2024.05.02 |
프로젝트 : DirectX를 활용한 그래픽스 (6 - 버텍스 쉐이더, 인풋 레이아웃, 픽셀 쉐이더) (1) | 2024.05.01 |