버퍼는 메모리에서 데이터를 보관하기 위해 사용되는 공간을 의미한다. 컴퓨터 그래픽스에서는 주로 GPU의 메모리에서 데이터를 보관하기 위해 사용되는 공간을 의미한다. 주로 사용되는 버퍼는 다음과 같다.
- 버텍스 버퍼: 물체를 구성하는 각 정점에 대한 정보를 보관하는 용도로 사용된다. 일반적으로 위치, 색, 노말 벡터의 값 등이 포함된다.
- 인덱스 버퍼: 버텍스 버퍼를 통해 만들어진 정점 정보를 기반으로 각 정점의 순서를 정의하는 용도로 사용된다.
- 컨스턴트 버퍼: 쉐이더에서 사용할 상수들을 모아 놓은 버퍼이다. Model, View, Projection 정보가 일반적으로 포함된다.
1. D3D11_BUFFER_DESC
DX에서 GPU에서 사용할 버퍼에 대한 설정을 하기 위해서는 D3D11_BUFFER_DESC
을 만들어야 한다. D3D11_BUFFER_DESC
은 다른 DESC 접미사가 붙은 구조체와 마찬가지로 해당 버퍼를 어떻게 사용할지 기술하는 용도로 사용된다.
// https://learn.microsoft.com/ko-kr/windows/win32/api/d3d11/ns-d3d11-d3d11_buffer_desc
typedef struct D3D11_BUFFER_DESC {
UINT ByteWidth;
D3D11_USAGE Usage;
UINT BindFlags;
UINT CPUAccessFlags;
UINT MiscFlags;
UINT StructureByteStride;
} D3D11_BUFFER_DESC;
ByteWidth
: 버퍼 생성에 필요한 메모리의 크기를 나타낸다.Usage
: 버퍼의 읽고 쓰기를 설정한다. 버퍼에 따라 적절한 값을 설정해주면 된다.BindFlags
: 버퍼가 파이프라인에 바인딩되는 방법을 설정한다. 버퍼의 용도에 맞게 설정해주면 된다.CPUAccessFlags
: CPU 접근 여부를 설정한다.MiscFlags
: 기타 플래그를 설정한다. 사용하지 않으면 0이다.StructureByteStride
: GPU가 버퍼에 접근해서 한 번에 읽어 들일 데이터의 크기를 의미한다. 배열 형식으로 버퍼를 구성했다면, 배열의 원소 하나의 크기를 지정하면 된다.
2. D3D11_SUBRESOURCE_DATA
D3D11_SUBRESOURCE_DATA
는 버퍼를 만들 때만 사용하는 것은 아니다. D3D11_SUBRESOURCE_DATA
는 CPU에 있는 데이터를 GPU로 보낼 때, 어떤 데이터를 어떤 형식으로 보낼지 설정할 때 사용한다.
// https://learn.microsoft.com/ko-kr/windows/win32/api/d3d11/ns-d3d11-d3d11_subresource_data
typedef struct D3D11_SUBRESOURCE_DATA {
const void *pSysMem;
UINT SysMemPitch;
UINT SysMemSlicePitch;
} D3D11_SUBRESOURCE_DATA;
pSysMem
: GPU로 보낼 데이터의 시작 주소를 입력해준다.SysMemPitch
,SysMemSlicePitch
: GPU로 보낼 데이터가 1차원 배열 형식이라면 0을 사용하면 된다.
3. 버퍼 만들기
버텍스, 인덱스, 컨스탄트 버퍼를 만드는 골자는 크게 다르지 않다. D3D11_BUFFER_DESC
구조체를 만들어서 버퍼를 어떻게 사용할지 결정하고, D3D11_SUBRESOURCE_DATA
구조체를 만들어 CPU에 있는 데이터를 어떻게 보낼지 결정한 뒤에 필요한 버퍼를 만들기 위한 COM 객체 생성 함수를 호출하면 된다. 버퍼 생성 함수 CreateBuffer
는 디바이스 인터페이스(ID3D11Device
)에서 제공한다.
// https://learn.microsoft.com/ko-kr/windows/win32/api/d3d11/nf-d3d11-id3d11device-createbuffer
HRESULT CreateBuffer(
[in] const D3D11_BUFFER_DESC *pDesc,
[in, optional] const D3D11_SUBRESOURCE_DATA *pInitialData,
[out, optional] ID3D11Buffer **ppBuffer
);
CreateBuffer
함수의 매개 변수는 앞에서 만든 버퍼의 디스크립션과 서브리소스 데이터, 그리고 생성된 버퍼를 받기 위한 ppBuffer
를 넣어주면 된다. 실제로 버퍼를 만드는 과정을 살펴보면 다음과 같다.
// 버텍스 버퍼 만들기
// 버텍스 초기화 작업
vector<Vertex> vertices;
// do something ...
// 버텍스 버퍼를 위한 버퍼 디스크립션
D3D11_BUFFER_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(bufferDesc));
// 버퍼의 읽기/쓰기 여부 설정
// D3D11_USAGE_DEFAULT: GPU에서 읽기 및 쓰기 가능
// D3D11_USAGE_IMMUTABLE: 한 번 생성한 후에 GPU에서만 읽기 가능
// D3D11_USAGE_DYNAMIC: GPU에서는 읽기 전용, CPU에서는 쓰기 전용
// D3D11_USAGE_STAGING: GPU에서 CPU로 데이터 전송할 때 사용
bufferDesc.Usage = D3D11_USAGE_IMMUTABLE;
// 버퍼의 크기 설정
bufferDesc.ByteWidth = UINT(sizeof(Vertex) * vertices.size());
// 버텍스 버퍼로 사용하기 위한 바인드 플래그 설정
// D3D11_BIND_VERTEX_BUFFER: 버텍스 버퍼로 사용
// D3D11_BIND_INDEX_BUFFER: 인덱스 버퍼로 사용
// D3D11_BIND_CONSTANT_BUFFER: 컨스턴트 버퍼로 사용
bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
// CPU 액세스 설정
// 0: CPU 액세스 차단
// D3D11_CPU_ACCESS_WRITE: CPU 쓰기 가능
// D3D11_CPU_ACCESS_READ: CPU 읽기 가능
bufferDesc.CPUAccessFlags = 0;
// GPU에서 버퍼에 있는 데이터에 접근하기 위한 크기 설정
bufferDesc.StructureByteStride = sizeof(Vertex);
// 서브리소스 데이터
D3D11_SUBRESOURCE_DATA vertexBufferData = {0}; // MS 예제에서 초기화하는 방식
vertexBufferData.pSysMem = vertices.data();
// 일차원 형식의 버퍼에서는 아래 값을 0으로 사용한다.
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
const HRESULT hr =
m_device->CreateBuffer(&bufferDesc, &vertexBufferData, vertexBuffer.GetAddressOf());
버퍼를 만드는 기본적인 형식은 위의 코드와 같다. 일반적으로 생성되는 종류에 따라 달라질 수 있는 값은 다음과 같다.
bufferDesc.Usage
: 프로그램 내부에서 한 번 정해진 도형의 버텍스, 인덱스가 변할 수 있다면 CPU에서 업데이트가 가능하도록D3D11_USAGE_DYNAMIC
를 사용하면 된다. 그렇지 않고 고정되어 있다면D3D11_USAGE_IMMUTABLE
를 사용하여 불필요한 업데이트를 막을 수 있다.bufferDesc.BindFlags
: GPU에서 버퍼를 어떤 용도로 사용할지 결정하는 플래그다. 필요한 용도에 맞게 플래그를 설정해주면 된다.bufferDesc.CPUAccessFlags
: CPU에서 GPU 버퍼에 접근할 수 있는지 설정한다. CPU에서 쉐이더에서 사용할 상수들의 값을 업데이트한다면D3D11_CPU_ACCESS_WRITE
플래그를 사용하여 컨스턴트 버퍼에 있는 값에 쓰기 작업을 할 수 있다.
'그래픽스 > DirectX11' 카테고리의 다른 글
노멀 벡터 변환 (0) | 2024.05.08 |
---|---|
쉐이더 만들기(버텍스, 픽셀) (0) | 2024.04.22 |
RenderTargetView의 의미 (0) | 2024.04.19 |
DXGI_SWAP_CHAIN_DESC에 관하여 (1) | 2024.04.19 |
ID3D11Device 초기화, 생성 팁 (0) | 2024.04.16 |