다음을 통해 공유


꼭짓점 버퍼 및 데이터 포팅

중요 API

이 단계에서는 메시를 포함할 꼭짓점 버퍼와 셰이더가 지정된 순서로 꼭짓점을 트래버스할 수 있게 하는 인덱스 버퍼를 정의합니다.

이 시점에서 사용 중인 큐브 메시에 하드 코딩된 모델을 살펴보겠습니다. 두 표현 모두 꼭짓점이 삼각형 목록으로 구성됩니다(스트립 또는 보다 효율적인 기타 삼각형 레이아웃과 반대). 두 표현의 모든 꼭짓점에 연결된 인덱스 및 색 값이 있습니다. 이 항목의 Direct3D 코드 대부분은 Direct3D 프로젝트에 정의된 변수 및 개체를 참고합니다.

다음은 OpenGL ES 2.0에서 처리하기 위한 큐브입니다. 샘플 구현에서 각 꼭짓점은 부동 소수점 값 7개(위치 좌표 3개, RGBA 색 값 4개)입니다.

#define CUBE_INDICES 36
#define CUBE_VERTICES 8

GLfloat cubeVertsAndColors[] = 
{
  -0.5f, -0.5f,  0.5f, 0.0f, 0.0f, 1.0f, 1.0f,
  -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
  -0.5f,  0.5f,  0.5f, 0.0f, 1.0f, 1.0f, 1.0f,
  -0.5f,  0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
  0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 1.0f, 1.0f,
  0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f,  
  0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f,
  0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f
};

GLuint cubeIndices[] = 
{
  0, 1, 2, // -x
  1, 3, 2,

  4, 6, 5, // +x
  6, 7, 5,

  0, 5, 1, // -y
  5, 6, 1,

  2, 6, 3, // +y
  6, 7, 3,

  0, 4, 2, // +z
  4, 6, 2,

  1, 7, 3, // -z
  5, 7, 1
};

Direct3D 11에서 처리하는 데 사용할 수 있는 동일한 큐브는 다음과 같습니다.

VertexPositionColor cubeVerticesAndColors[] = 
// struct format is position, color
{
  {XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT3(0.0f, 0.0f, 0.0f)},
  {XMFLOAT3(-0.5f, -0.5f,  0.5f), XMFLOAT3(0.0f, 0.0f, 1.0f)},
  {XMFLOAT3(-0.5f,  0.5f, -0.5f), XMFLOAT3(0.0f, 1.0f, 0.0f)},
  {XMFLOAT3(-0.5f,  0.5f,  0.5f), XMFLOAT3(0.0f, 1.0f, 1.0f)},
  {XMFLOAT3( 0.5f, -0.5f, -0.5f), XMFLOAT3(1.0f, 0.0f, 0.0f)},
  {XMFLOAT3( 0.5f, -0.5f,  0.5f), XMFLOAT3(1.0f, 0.0f, 1.0f)},
  {XMFLOAT3( 0.5f,  0.5f, -0.5f), XMFLOAT3(1.0f, 1.0f, 0.0f)},
  {XMFLOAT3( 0.5f,  0.5f,  0.5f), XMFLOAT3(1.0f, 1.0f, 1.0f)},
};
        
unsigned short cubeIndices[] = 
{
  0, 2, 1, // -x
  1, 2, 3,

  4, 5, 6, // +x
  5, 7, 6,

  0, 1, 5, // -y
  0, 5, 4,

  2, 6, 7, // +y
  2, 7, 3,

  0, 4, 6, // -z
  0, 6, 2,

  1, 3, 7, // +z
  1, 7, 5
};

이 코드를 검토하면 OpenGL ES 2.0 코드의 큐브가 오른쪽 좌표계로 표시되는 반면 Direct3D 관련 코드의 큐브는 왼쪽 좌표계로 표시됩니다. 사용자 고유 메시 데이터를 가져올 경우, 모델의 z축 좌표를 역방향으로 변경하고 그에 따라 각 메시의 인덱스를 변경하여 좌표계의 변화에 따라 삼각형을 트래버스해야 합니다.

큐브 메시를 오른손 OpenGL ES 2.0 좌표계에서 왼손 Direct3D 좌표계로 성공적으로 이동한 경우 두 모델에서 처리하기 위해 큐브 데이터를 로드하는 방법을 살펴보겠습니다.

지침

1단계: 입력 레이아웃 생성

OpenGL ES 2.0에서 꼭짓점 데이터는 셰이더 개체에 제공되고 읽을 특성으로 제공됩니다. 일반적으로 셰이더의 GLSL에 사용되는 특성 이름을 포함하는 문자열을 셰이더 프로그램 개체에 제공하고 셰이더에 제공할 수 있는 메모리 위치를 다시 가져옵니다. 이 예제에서 꼭짓점 버퍼 개체에는 다음과 같이 정의되고 서식이 지정된 사용자 지정 꼭짓점 구조 목록이 포함됩니다.

OpenGL ES 2.0: 꼭짓점별 정보를 포함하는 특성을 구성합니다.

typedef struct 
{
  GLfloat pos[3];        
  GLfloat rgba[4];
} Vertex;

OpenGL ES 2.0에서는 입력 레이아웃은 암시적입니다. 범용 GL_ELEMENT_ARRAY_BUFFER를 가져오고 꼭짓점 셰이더가 데이터를 업로드 한 후 이 데이터를 해석할 수 있도록 stride 및 오프셋을 제공합니다. 렌더링하기 전 glVertexAttribPointer를 사용하여 각 꼭짓점 데이터 블록의 어떤 부분에 매핑되는 특성을 렌더링하기 전에 셰이더에 알릴 수 있습니다.

Direct3D에서는 기하 도형을 그리기 전에 버퍼를 생성할 때 꼭짓점 버퍼의 꼭짓점 데이터 구조를 설명하는 입력 레이아웃을 제공해야 합니다. 이를 위해 메모리에 있는 개별 꼭짓점의 데이터 레이아웃에 해당하는 입력 레이아웃을 사용합니다. 이를 정확하게 지정하는 것이 매우 중요합니다!

여기에서 입력 설명을 D3D11_INPUT_ELEMENT_DESC 구조의 배열로 작성합니다.

Direct3D: 입력 레이아웃 설명을 정의합니다.

struct VertexPositionColor
{
  DirectX::XMFLOAT3 pos;
  DirectX::XMFLOAT3 color;
};

// ...

const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = 
{
  { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
  { "COLOR",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

이 입력 설명은 꼭짓점을 모델 좌표에 꼭짓점 위치를 저장하는 3D 벡터 하나와 꼭짓점과 연결된 RGB 색 값을 저장하는 다른 3D 벡터 등 2개의 3좌표 벡터 쌍으로 정의합니다. 이 경우 XMFLOAT3(X.Xf, X.Xf, X.Xf) 코드에서 나타내는 요소인 3x32비트 부동 소수점 형식을 사용합니다. 셰이더에서 사용할 데이터를 처리할 때마다 DirectXMath 라이브러리의 형식을 사용해야 합니다. 이 형식은 해당 데이터의 적절한 압축 및 맞춤을 보장하므로 셰이더에서 사용할 수 있습니다. (예를 들어, 벡터 데이터의 경우 XMFLOAT3 또는 XMFLOAT4, 행렬의 경우 XMFLOAT4X4를 사용합니다.)

가능한 모든 형식 유형 목록은 DXGI_FORMAT을 참조하세요.

꼭짓점별 입력 레이아웃이 정의되면 레이아웃 개체를 생성합니다. 다음 코드에서 이 개체를 유형 ComPtr(ID3D11InputLayout 유형의 개체를 가리킴)의 변수인 m_inputLayout에 씁니다. fileData에는 이전 단계의 컴파일된 꼭짓점 셰이더 개체인 셰이더 포트가 포함되어 있습니다.

Direct3D: 꼭짓점 버퍼에서 사용하는 입력 레이아웃을 생성합니다.

Microsoft::WRL::ComPtr<ID3D11InputLayout>      m_inputLayout;

// ...

m_d3dDevice->CreateInputLayout(
  vertexDesc,
  ARRAYSIZE(vertexDesc),
  fileData->Data,
  fileShaderData->Length,
  &m_inputLayout
);

입력 레이아웃을 정의했습니다. 이제 이 레이아웃을 사용하는 버퍼를 생성하고 큐브 메시 데이터와 함께 로드해 보겠습니다.

2단계: 꼭짓점 버퍼 생성 및 로드

OpenGL ES 2.0에서 위치 데이터에 대한 버퍼와 색 데이터에 대한 버퍼 쌍을 생성합니다. (단일 버퍼를 모두 포함하는 구조체를 생성할 수도 있습니다.) 각 버퍼를 바인딩하고 위치 및 색 데이터를 해당 버퍼에 씁니다. 나중에 렌더링 함수 중에 버퍼를 다시 바인딩하고 셰이더에 버퍼의 데이터 형식을 제공하여 올바르게 해석할 수 있게 합니다.

OpenGL ES 2.0: 꼭짓점 버퍼 바인딩

// upload the data for the vertex position buffer
glGenBuffers(1, &renderer->vertexBuffer);    
glBindBuffer(GL_ARRAY_BUFFER, renderer->vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(VERTEX) * CUBE_VERTICES, renderer->vertices, GL_STATIC_DRAW);   

Direct3D에서 셰이더 액세스 가능한 버퍼는 D3D11_SUBRESOURCE_DATA 구조로 표시됩니다. 셰이더 개체에 이 버퍼의 위치를 바인딩하려면 각 버퍼에 대한 CD3D11_BUFFER_DESC 구조를 ID3DDevice::CreateBuffer를 사용하여 만든 다음 ID3DDeviceContext::IASetVertexBuffers 등 버퍼 유형 관련 set 메서드를 호출하여 Direct3D 디바이스 컨텍스트의 버퍼를 설정합니다.

버퍼를 설정할 경우, 버퍼의 시작 부분에서 스트라이드(개별 꼭짓점의 데이터 요소 크기)와 오프셋(꼭짓점 데이터 배열이 실제로 시작되는 위치)을 설정해야 합니다.

D3D11_SUBRESOURCE_DATA 구조의 pSysMem 필드에 대한 vertexIndices 배열에 포인터를 할당합니다. 올바르지 않을 경우, 메시가 손상되거나 비어 있게 됩니다!

Direct3D: 꼭짓점 버퍼 생성 및 설정

D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
vertexBufferData.pSysMem = cubeVertices;
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(cubeVertices), D3D11_BIND_VERTEX_BUFFER);
        
m_d3dDevice->CreateBuffer(
  &vertexBufferDesc,
  &vertexBufferData,
  &m_vertexBuffer);
        
// ...

UINT stride = sizeof(VertexPositionColor);
UINT offset = 0;
m_d3dContext->IASetVertexBuffers(
  0,
  1,
  m_vertexBuffer.GetAddressOf(),
  &stride,
  &offset);

3단계: 인덱스 버퍼 생성 및 로드

인덱스 버퍼는 꼭짓점 셰이더가 개별 꼭짓점을 조회할 수 있도록 하는 효율적인 방법입니다. 필수는 아니지만 이 샘플 렌더러에서 사용합니다. OpenGL ES 2.0의 꼭짓점 버퍼와 마찬가지로 인덱스 버퍼가 만들어지고 범용 버퍼로 바인딩되며 이전에 만든 꼭짓점 인덱스가 해당 버퍼에 복사됩니다.

그릴 준비가 되면 꼭짓점과 인덱스 버퍼를 모두 다시 바인딩하고 glDrawElements를 호출합니다.

OpenGL ES 2.0: 인덱스 순서를 그리기 호출로 전송합니다.

GLuint indexBuffer;

// ...

glGenBuffers(1, &renderer->indexBuffer);    
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->indexBuffer);   
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
  sizeof(GLuint) * CUBE_INDICES, 
  renderer->vertexIndices, 
  GL_STATIC_DRAW);

// ...
// Drawing function

// Bind the index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->indexBuffer);
glDrawElements (GL_TRIANGLES, renderer->numIndices, GL_UNSIGNED_INT, 0);

Direct3D를 사용하면 조금 더 교훈적이더라도 매우 유사한 프로세스입니다. Direct3D를 구성할 때 생성한 ID3D11DeviceContext에 Direct3D 하위 리소스로 인덱스 버퍼를 제공합니다. 다음과 같이 인덱스 배열에 대해 구성된 하위 리소스를 사용하여 ID3D11DeviceContext::IASetIndexBuffer를 호출하여 이 작업을 수행합니다. 다시, D3D11_SUBRESOURCE_DATA 구조체의 pSysMem 필드에 대한 cubeIndices 배열에 포인터를 할당합니다.

Direct3D: 인덱스 버퍼를 생성합니다.

m_indexCount = ARRAYSIZE(cubeIndices);

D3D11_SUBRESOURCE_DATA indexBufferData = {0};
indexBufferData.pSysMem = cubeIndices;
indexBufferData.SysMemPitch = 0;
indexBufferData.SysMemSlicePitch = 0;
CD3D11_BUFFER_DESC indexBufferDesc(sizeof(cubeIndices), D3D11_BIND_INDEX_BUFFER);

m_d3dDevice->CreateBuffer(
  &indexBufferDesc,
  &indexBufferData,
  &m_indexBuffer);

// ...

m_d3dContext->IASetIndexBuffer(
  m_indexBuffer.Get(),
  DXGI_FORMAT_R16_UINT,
  0);

나중에 다음과 같이 ID3D11DeviceContext::DrawIndexed(또는 ID3D11DeviceContext::Draw로 인덱싱되지 않은 꼭짓점의 경우 그리기)를 호출하여 삼각형을 그립니다. (자세한 내용은 화면에 그리기로 이동합니다.)

Direct3D: 인덱싱된 꼭짓점을 그립니다.

m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_d3dContext->IASetInputLayout(m_inputLayout.Get());

// ...

m_d3dContext->DrawIndexed(
  m_indexCount,
  0,
  0);

이전 단계

셰이더 개체 포팅

다음 단계

GLSL 포팅

설명

Direct3D를 구조화할 때 ID3D11Device의 메서드를 호출하는 코드를 디바이스 리소스를 다시 생성해야 할 때마다 호출되는 메서드로 구분합니다. (Direct3D 프로젝트 템플릿에서 이 코드는 렌더러 개체의 CreateDeviceResource 메서드에 있습니다. 반면에 디바이스 컨텍스트(ID3D11DeviceContext)를 업데이트하는 코드는 실제로 셰이더 단계를 구성하고 데이터를 바인딩하는 위치이므로 렌더링 메서드에 배치됩니다.