Vertex Buffer에 대해 알아보자. 마이크로 소프트 홈페이지에서 공식적으로 정점 버퍼를 만드는 방법에 대해 설명하고 있다. 우선 가장 먼저 정점 버퍼를 만드는 예제 코드를 보자.
ID3D11Buffer* g_pVertexBuffer;
// Define the data-type that
// describes a vertex.
struct SimpleVertexCombined
{
XMFLOAT3 Pos;
XMFLOAT3 Col;
};
// Supply the actual vertex data.
SimpleVertexCombined verticesCombo[] =
{
XMFLOAT3( 0.0f, 0.5f, 0.5f ),
XMFLOAT3( 0.0f, 0.0f, 0.5f ),
XMFLOAT3( 0.5f, -0.5f, 0.5f ),
XMFLOAT3( 0.5f, 0.0f, 0.0f ),
XMFLOAT3( -0.5f, -0.5f, 0.5f ),
XMFLOAT3( 0.0f, 0.5f, 0.0f ),
};
// Fill in a buffer description.
D3D11_BUFFER_DESC bufferDesc;
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
bufferDesc.ByteWidth = sizeof( SimpleVertexCombined ) * 3;
bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.MiscFlags = 0;
// Fill in the subresource data.
D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem = verticesCombo;
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;
// Create the vertex buffer.
hr = g_pd3dDevice->CreateBuffer( &bufferDesc, &InitData, &g_pVertexBuffer );
Vertex Structure 정의하기
정점 버퍼를 만들기 위해서 우선 정점의 데이터 형태를 정의할 필요가 있다.
struct SimpleVertexCombined
{
XMFLOAT3 Pos;
XMFLOAT3 Col;
};
위치와 색깔을 가지는 간단한 형태의 정점 구조체
왜 구조체의 형태여야 할까? 좀 더 원시적으로 생각해서 float Vertex[ 5 ]
이렇게 정의해도 된다. 쉐이더에서도 이렇게 전달받은 데이터를 각 인덱스별로 읽어서 사용할 수 있다. 하지만 이 경우 이 정점 데이터가 어떠한 요소들로 이루어져 있는지 이해하기 어렵게 된다. 정점 구조체를 정의하는 것은 정점 데이터를 좀 더 읽기 쉽도록 하기 위함이다.
Vertex Data 준비하기
정점 버퍼 객체, 그러니까 ID3D11Buffer
의 인스턴스를 생성한다는 것은 결국 시스템 메모리에 있는 데이터들을 GPU메모리에 복사한다는 뜻이다. 따라서 일단 시스템 메모리에 정점 데이터가 적재되어 있어야 한다.
SimpleVertexCombined verticesCombo[] =
{
XMFLOAT3( 0.0f, 0.5f, 0.5f ), // Pos
XMFLOAT3( 0.0f, 0.0f, 0.5f ), // Col
XMFLOAT3( 0.5f, -0.5f, 0.5f ), // Pos
XMFLOAT3( 0.5f, 0.0f, 0.0f ), // Col
XMFLOAT3( -0.5f, -0.5f, 0.5f ), // Pos
XMFLOAT3( 0.0f, 0.5f, 0.0f ), // Col
};
SimpleVertexCombined
타입의 정점 데이터 배열
구조체의 멤버 Pos
, Col
모두 XMFLOAT3
타입이기 때문에 심플하게 XMFLOAT3
의 배열 형태로 시스템 메모리에 정점 데이터를 일단 올린 것이다.
Vertex Buffer Instance 생성하기
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof( D3D11_BUFFER_DESC ) );
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleVertexCombined ) * 3;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA sd;
ZeroMemory( &sd, sizeof( D3D11_SUBRESOURCE_DATA ) );
sd.pSysMem = verticesCombo;
hr = g_pd3dDevice->CreateBuffer( &bufferDesc, &sd, &g_pVertexBuffer );
D3D11_BUFFER_DESC
D3D11_BUFFER_DESC
를 통해 버퍼에 대한 특정 명세 사항들을 지정한다. 버퍼의 타입 ( D3D11_BIND_VERTEX_BUFFER
)과 버퍼의 크기( sizeof( SimpleVertexCombiled ) * 3
)등을 지정한다. 자세한 레퍼런스틑 마이크로 소프트에서 확인하면 된다.
D3D11_SUBRESOURCE_DATA
GPU 메모리에 복사할 시스템 메모리에 있는 정점 데이터 위치를 지정한다. 자세한 레퍼런스는 마찬가지로 마이크로 소프트를 참고하면 된다.
이렇게 정점 버퍼( ID3D11Buffer
)가 생성되면 정점 데이터들이 모두 GPU메모리에 적재된 상태가 된다. 따라서 시스템 메모리에 적재되어 있는 정점 데이터들은 따로 사용하지 않는 이상 메모리 해제를 해줘도 무방하다. 예제 코드에서는 따로 메모리 할당을 받아 적재한 것은 아니기 때문에 해제 코드가 따로 없다.
Rendering Pipeline에 연결하기
이렇게 생성된 정점 버퍼를 렌더링하기 위해서는 렌더링 파이프라인에 연결이 필요하다.
unsigned int stride = sizeof( SimpleVertexCombined );
unsigned int offset = 0;
ID3D11DeviceContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );
ID3D11DeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
IASetVertexBuffers
함수가 렌더링 파이프라인에 정점 버퍼를 연결하는 함수이다. 렌더링 파이프라인에 정점 버퍼는 1개가 아닌 여러개가 연결 가능하다. D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT
만큼 연결 가능하다. 정확한 갯수는 이건 하드웨어 특성이나 DirectX
버전에 따라 달라질 수 있으니 참고해야 한다. 레퍼런스는 마이크로 소프트에서 참고한다.
IASetPrimitiveTopology
함수는 정점들을 어떤 도형으로 해석할지를 지정하는 함수이다. D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
는 전통적인 삼각형 리스트 이므로 정점 3개씩 단위로 1개의 삼각형을 렌더링 한다. 레퍼런스는 마이크로 소프트에서 참고한다.
Draw
정점 버퍼를 렌더링 파이프라인에 연결한 후 Draw
함수를 통해 렌더링 파이프라인의 렌더링을 실행할 수 있다. 정점 버퍼와 인덱스 버퍼의 구성에 따라 DrawIndexed
, DrawInstanced
등의 여러 바리에이션이 있다. IASetVertexBuffers
을 통해 여러 슬롯에 정점 버퍼를 연결해둔 경우 쉐이더에서 슬롯으로 구분된 버텍스 버퍼를 따로 처리할 수 있다. 이에 대한 자세한 내용은 Vertex Shader에서 다루도록 한다.
일단 정점 버퍼만 생성하고 렌더링 파이프라인에 연결해서 렌더링을 걸어도 화면에 정상적으로 출력되지 않는다. 아직 렌더링 파이프라인에 정점들을 처리해줄 Vertex Shader
와 마지막 각 픽셀을 처리해줄 Pixel Shader
가 셋팅되지 않았기 때문이다.
이전글 : [DirectX11] 4. 삼각형 – Vertex Buffer, Vertex Shader, Pixel Shader
다음글 : [DirectX11] 6. 삼각형 – Vertex Shader에 대해 알아보자.