-
[DirectX11] Constant Buffer2024년 02월 07일
- 유니얼
-
작성자
-
2024.02.07.:05
728x90DirectX 11로 게임 엔진 아키텍처 만들기
이 블로그 글은 DirectX 11과 그래픽스 프로그래밍 학습 과정을 공유하며, 데이터를 효율적으로 쉐이더에 전달하는 방법 중 하나인 Constant Buffer에 대해 정리하고자 합니다. Constant Buffer를 활용하여 유니티 로고 이미지가 화면에서 이동하는 간단한 시나리오를 구현해보면서 얻은 지식을 바탕으로 해당 내용을 정리하고, 중요한 개념을 추가하여 설명해보겠습니다.
참고강의 링크:
Constant Buffer란?
Constant Buffer는 DirectX 11에서 쉐이더에 상수 데이터를 전달하기 위해 사용되는 버퍼입니다. 이 버퍼는 주로 변하지 않는 데이터(예: 변환 행렬, 조명 정보 등)를 저장하는 데 사용되며, 한 번 설정하면 여러 쉐이더에서 재사용할 수 있어 효율적인 데이터 관리가 가능합니다.
Constant Buffer의 장점
- 효율성: Constant Buffer를 사용하면 한 번에 많은 양의 데이터를 GPU에 전달할 수 있어, 데이터 전송에 필요한 API 호출 수를 줄일 수 있습니다. 이는 CPU와 GPU 사이의 통신 오버헤드를 최소화하는 데 도움이 됩니다.
- 조직성: 관련된 데이터를 하나의 버퍼에 그룹화함으로써 셰이더 코드의 가독성과 유지 보수성이 향상됩니다.
1. Constant Buffer 정의
먼저, 이동 정보를 담을 구조체 TransformData를 정의합니다. 여기서 offset은 유니티 로고의 이동 방향과 거리를 정의합니다.
struct TransformData { Vec3 offset; float dummy; // 패딩을 위한 더미 변수 };
2. Constant Buffer 생성
TransformData 구조체를 기반으로 Constant Buffer를 생성합니다. D3D11_USAGE_DYNAMIC을 사용하여 CPU에서 버퍼를 동적으로 업데이트할 수 있도록 설정합니다.
void Game::CreateConstantBuffer() { D3D11_BUFFER_DESC desc; ZeroMemory(&desc, sizeof(desc)); desc.Usage = D3D11_USAGE_DYNAMIC; // CPU에서 쓰기, GPU에서 읽기 가능 desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.ByteWidth = sizeof(TransformData); // 구조체 크기만큼 할당 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // CPU 접근 권한 설정 HRESULT hr = _device->CreateBuffer(&desc, nullptr, &_constantBuffer.GetAddressOf()); CHECK(hr); }
3. Constant Buffer 업데이트
게임의 메인 루프에서 로고의 위치를 업데이트하는 Update 함수를 구현합니다. 여기서는 간단하게 offset 값을 변경하여 로고를 이동시킵니다.
void Game::Update() { _transformData.offset.x += 0.003f; // x축 방향으로 이동 _transformData.offset.y += 0.003f; // y축 방향으로 이동 D3D11_MAPPED_SUBRESOURCE subResource; _deviceContext->Map(_constantBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource); ::memcpy(subResource.pData, &_transformData, sizeof(_transformData)); _deviceContext->Unmap(_constantBuffer.Get(), 0); }
4. 렌더링
Render 함수에서는 Constant Buffer를 사용하여 쉐이더에 데이터를 전달합니다. 이 예제에서는 정점 쉐이더(VS)에서 로고의 위치를 변경하는 데 사용됩니다.
void Game::Render() // 렌더링 함수 구현 { RenderBegin(); // 렌더링 시작 전 설정 // 렌더링 할 내용 TODO: 실제 렌더링 로직 추가 //IA - VS - RS - PS - OM { uint32 stride = sizeof(Vertex); // 정점 데이터의 크기 uint32 offset = 0; // 버퍼 시작 위치 // 입력 어셈블러(IA) 스테이지 설정 _deviceContext->IASetVertexBuffers(0, 1, _vertexBuffer.GetAddressOf(), &stride, &offset); _deviceContext->IASetIndexBuffer(_indexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0); _deviceContext->IASetInputLayout(_inputLayout.Get()); _deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // 정점 쉐이더(VS) 스테이지 설정 _deviceContext->VSSetShader(_vertexShader.Get(), nullptr, 0); _deviceContext->VSSetConstantBuffers(0, 1, _constantBuffer.GetAddressOf()); //RS // 픽셀 쉐이더(PS) 스테이지 설정 _deviceContext->PSSetShader(_pixelShader.Get(), nullptr, 0); _deviceContext->PSSetShaderResources(0, 1, _shaderResourceView.GetAddressOf()); _deviceContext->PSSetShaderResources(1, 1, _shaderResourceView2.GetAddressOf()); // 출력 병합(OM) 스테이지 설정 //_deviceContext->Draw(_vertices.size(), 0); // 정점들을 그림 _deviceContext->DrawIndexed(_indices.size(), 0, 0); } RenderEnd(); // 렌더링 후 처리 }
5. 쉐이더 구현
정점 쉐이더에서는 Constant Buffer로부터 offset 값을 받아 로고의 위치를 조정합니다.
cbuffer TransformData : register(b0) { float4 offset; } //VS => Vertex Shader //IA - VS - RS - PS - OM VS_OUTPUT VS(VS_INPUT input) { VS_OUTPUT output; // 출력 구조체 초기화 output.position = input.position + offset; // 로고의 위치 조정 //output.color = input.color; // 입력 색상을 출력 색상으로 전달 output.uv= input.uv; // 입력 색상을 출력 색상으로 전달 return output; // 변환된 출력 반환 }
결론
DirectX 11의 Constant Buffer를 사용하는 과정은 고성능 그래픽스 애플리케이션 개발의 핵심 요소로, 예제 코드를 통해 변환 데이터 같은 정보를 쉐이더에 효율적으로 전달하는 방법을 보여줍니다. 이는 CPU와 GPU 간의 데이터 전송을 최적화하고, 쉐이더 프로그램의 유연성을 높이며, 복잡한 그래픽 효과와 애니메이션 구현을 용이하게 합니다.
반응형다음글이전글이전 글이 없습니다.댓글