DirectX 11 스터디 - 상수 버퍼 (Constant Buffer)
버텍스 데이터/매쉬 데이터를 건들지 않고, Shader 에 버퍼 데이터를 넘겨서 버텍스셰이더를 통해 변경되도록 처리해본다.
순서는 아래와 같다.
1. 상수버퍼 생성
2. DeviceContext 에 버퍼 매핑
3. 렌더 함수에서 버퍼 전달
아래는 수정한 코드 블럭들
// 멤버 선언
TransformData _transformData;
ComPtr<ID3D11Buffer> _constantBuffer;
// 구조체 선언
struct TransformData
{
Vec3 offset;
float dummy; // 16byte 로 정렬해야 하기 때문에 더미 멤버를 추가.
};
// 버퍼 생성
void Game::CreateContantBuffer()
{
D3D11_BUFFER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Usage = D3D11_USAGE_DYNAMIC; // CPU Write + GPU Read
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.ByteWidth = sizeof(TransformData); // 구조체를 선언해준다.
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
HRESULT hr = _device->CreateBuffer(&desc, nullptr, _constantBuffer.GetAddressOf());
CHECK(hr);
}
// 업데이트에서 버퍼 매핑
void Game::Update()
{
// 1초동안 좌표가 이동하도록
DWORD dwCurTime = ::timeGetTime();
DWORD delta = dwCurTime - dwLastTime;
float deltaSec = delta * 0.001f;
dwLastTime = ::timeGetTime();
_transformData.offset.x += 1.f * deltaSec;
_transformData.offset.y += 1.f * deltaSec;
if (_transformData.offset.x >= 1.f) _transformData.offset.x = 0.f;
if (_transformData.offset.y >= 1.f) _transformData.offset.y = 0.f;
// 버퍼 복사
D3D11_MAPPED_SUBRESOURCE subResource;
ZeroMemory(&subResource, sizeof(subResource));
_deviceContext->Map(_constantBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource);
::memcpy(subResource.pData, &_transformData, sizeof(_transformData));
_deviceContext->Unmap(_constantBuffer.Get(), 0);
}
// 렌더 과정에서 버퍼 전달
// Vertex Shader
_deviceContext->VSSetShader(_vertexShader.Get(), nullptr, 0);
_deviceContext->VSSetConstantBuffers(0, 1, _constantBuffer.GetAddressOf());
// 셰이더 코드에서 버퍼 활용
cbuffer TransformData : register(b0)
{
float4 offset;
}
VS_OUTPUT VS(VS_INPUT input)
{
VS_OUTPUT output;
output.position = input.position + offset;
output.uv = input.uv;
return output;
}
1. 상수버퍼 생성
ID3D11Buffer 를 만들고, 생성한 버퍼로 복사할 구조체를 선언한다.
Usage 는 USAGE_DYNAMIC 으로. CPU 에서 쓰고, GPU 에서 읽는다.
BindFlags 는 CONSTANT_BUFFER
CPUAccessFlags 는 ACCESS_WRITE
2. DeviceContext 에 버퍼 매핑
Update() 함수에서 _deviceContext->Map() / Unmap() 함수 사이에 데이터를 복사해준다. 여기서 TransformData 의 offset 값은, 1초동안 위치가 계속 바뀌도록 수정함. 그래야 결과 확인이 용이하기 때문에.
유의해야할 것은, MAPPED_SUBRESOURCE 구조체를 통해서 전달한다는 점. Map 함수를 호출할 때 ID3D11Buffer 의 포인터가 subResource 구조체 안에 pData 로 전달.
3. 렌더 함수에서 버퍼 전달
Render() 함수의 VertexShader 아래에 _deviceContext->VSSetConstantBuffer 함수를 호출해서 D3D11Buffer 를 전달.
결과는 아래처럼, 1초마다 버텍스가 이동하는 것을 확인. 이 상수버퍼를 활용해서 메쉬를 셰이더에서 자유롭게 조작할 수 있음. 메쉬의 원래 버텍스 정보를 수정하지 않고도 가벼운 상수버퍼(Constant Buffer)를 전달함으로 인해 성능을 유리하게 가져갈 수 있음.
.
<리얼-타임 렌더링(REAL-TIME RENDERING) 4/e> 구입 링크
https://link.coupang.com/a/8VWas
<DirectX 12를 이용한 3D 게임 프로그래밍 입문> 구입 링크
https://link.coupang.com/a/8V4Wq
유니티 에셋 스토어 링크
https://assetstore.unity.com?aid=1011lvz7h
(링크를 통해 도서/에셋 구입시 일정액의 수수료를 지급받습니다.)