다음을 통해 공유


OpenGL ES 2.0 버퍼, 유니폼 및 꼭짓점 특성과 Direct3D 비교

중요 API

OpenGL ES 2.0에서 Direct3D 11로 포팅하는 동안 앱과 셰이더 프로그램 간에 데이터를 전달하기 위한 구문 및 API 동작을 변경해야 합니다.

OpenGL ES 2.0에서 데이터는 네 가지 방식으로 셰이더 프로그램으로 그리고 프로그램에서 전달됩니다. 즉, 상수 데이터와 일관되게, 꼭짓점 데이터의 특성, 다른 리소스 데이터(예: 텍스처)에 대한 버퍼 개체로 전달됩니다. Direct3D 11에서는 상수 버퍼, 꼭짓점 버퍼, 하위 리소스에 대략 매핑됩니다. 피상적인 공통성에도 불구하고 사용법은 매우 다르게 처리됩니다.

기본 매핑은 다음과 같습니다.

OpenGL ES 2.0 Direct3D 11
uniform 상수 버퍼(cbuffer) 필드입니다.
attribute 입력 레이아웃으로 지정되고 특정 HLSL 의미 체계로 표시된 꼭짓점 버퍼 요소 필드입니다.
버퍼 개체 버퍼; 일반 사용 버퍼 정의는 D3D11_SUBRESOURCE_DATAD3D11_BUFFER_DESC를 참조하세요.
프레임 버퍼 개체(FBO) 렌더링 대상, ID3D11Texture2D를 사용한 ID3D11RenderTargetView를 참조하세요.
백 버퍼 '백 버퍼' 표면이 있는 스왑 체인, IDXGISurface1이 연결된 IDXGISwapChain1을 참조하세요.

 

포트 버퍼

OpenGL ES 2.0에서 모든 종류의 버퍼를 생성하고 바인딩하는 프로세스는 일반적으로 이 패턴을 따릅니다.

  • glGenBuffers를 호출하여 하나 이상의 버퍼를 생성하고 핸들을 반환합니다.
  • glBindBuffer를 호출하여 버퍼의 레이아웃(예: GL_ELEMENT_ARRAY_BUFFER)을 정의합니다.
  • glBufferData를 호출하여 특정 레이아웃에서 특정 데이터(예: 꼭짓점 구조, 인덱스 데이터 또는 색 데이터)로 버퍼를 채웁니다.

가장 일반적인 종류의 버퍼는 꼭짓점 버퍼로, 일부 좌표계에서 꼭짓점의 위치를 최소한으로 포함합니다. 일반적으로 꼭짓점은 위치 좌표, 꼭짓점 위치에 대한 일반 벡터, 꼭짓점 위치에 대한 탄젠트 벡터, 텍스처 조회(uv) 좌표를 포함하는 구조체로 표현됩니다. 버퍼에는 이러한 꼭짓점의 연속 목록(예: 삼각형 목록이나 스트립 또는 팬)이 포함되며 장면에 표시되는 다각형을 전체적으로 나타냅니다. (Direct3D 11 및 OpenGL ES 2.0에서는 그리기 호출당 여러 꼭짓점 버퍼가 있는 것이 비효율적입니다.)

다음은 OpenGL ES 2.0을 사용하여 만든 꼭짓점 버퍼 및 인덱스 버퍼의 예제입니다.

OpenGL ES 2.0: 꼭짓점 버퍼 및 인덱스 버퍼를 생성하고 채웁니다.

glGenBuffers(1, &renderer->vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, renderer->vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * CUBE_VERTICES, renderer->vertices, GL_STATIC_DRAW);

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

다른 버퍼에는 픽셀 버퍼 및 텍스처와 같은 맵이 포함됩니다. 셰이더 파이프라인은 텍스처 버퍼(pixmaps)로 렌더링하거나 버퍼 개체를 렌더링하고 이후 셰이더 패스에서 해당 버퍼를 사용할 수 있습니다. 가장 간단한 사례에서 호출 흐름은 다음과 같습니다.

  • glGenFramebuffers를 호출하여 프레임 버퍼 개체를 생성합니다.
  • glBindFramebuffer를 호출하여 쓰기를 위해 프레임 버퍼 개체를 바인딩합니다.
  • glFramebufferTexture2D를 호출하여 지정된 텍스처 맵에 그립니다.

Direct3D 11에서 버퍼 데이터 요소는 '하위 리소스'로 간주되며 개별 꼭짓점 데이터 요소에서 MIP 맵 텍스처에 이르기까지 다양할 수 있습니다.

Direct3D 11: 꼭짓점 버퍼 및 인덱스 버퍼 생성 및 채우기

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);

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);
    

쓰기 가능한 픽셀 버퍼 또는 맵(예: 프레임 버퍼)은 ID3D11Texture2D 개체로 생성할 수 있습니다. ID3D11RenderTargetView 또는 ID3D11ShaderResourceView에 대한 리소스로 바인딩할 수 있습니다. 그러면 연결된 스왑 체인으로 표시하거나 셰이더에 각각 전달할 수 있습니다.

Direct3D 11: 프레임 버퍼 개체 생성

ComPtr<ID3D11RenderTargetView> m_d3dRenderTargetViewWin;
// ...
ComPtr<ID3D11Texture2D> frameBuffer;

m_swapChainCoreWindow->GetBuffer(0, IID_PPV_ARGS(&frameBuffer));
m_d3dDevice->CreateRenderTargetView(
  frameBuffer.Get(),
  nullptr,
  &m_d3dRenderTargetViewWin);

유니폼 및 유니폼 버퍼 개체를 Direct3D 상수 버퍼로 변경

Open GL ES 2.0에서 유니폼은 개별 셰이더 프로그램에 상수 데이터를 제공하는 메커니즘입니다. 이 데이터는 셰이더에서 변경할 수 없습니다.

일반적으로 uniform 설정에서는 앱 메모리의 데이터에 대한 포인터와 함께 GPU에서 업로드 위치에서 glUniform* 메서드 중 하나를 제공하는 작업이 포함됩니다. glUniform* 메서드를 실행한 후 uniform 데이터는 GPU 메모리에 있으며 uniform을 선언한 셰이더가 액세스할 수 있습니다. 셰이더가 호환되는 형식을 사용하여 셰이더의 유니폼 선언에 따라 데이터를 해석할 수 있는 방식으로 데이터가 압축되었는지 확인해야 합니다.

OpenGL ES 2.0 유니폼 생성 및 데이터 업로드

renderer->mvpLoc = glGetUniformLocation(renderer->programObject, "u_mvpMatrix");

// ...

glUniformMatrix4fv(renderer->mvpLoc, 1, GL_FALSE, (GLfloat*) &renderer->mvpMatrix.m[0][0]);

셰이더의 GLSL에서 해당 유니폼 선언은 다음과 같습니다.

GL ES 2.0 열기: GLSL 유니폼 선언

uniform mat4 u_mvpMatrix;

Direct3D는 유니폼 데이터를 '상수 버퍼'로 지정하며, 유니폼과 마찬가지로 개별 셰이더에 제공된 상수 데이터가 포함됩니다. 균일한 버퍼와 마찬가지로 셰이더가 해석해야 하는 방식과 동일하게 메모리에 상수 버퍼 데이터를 압축하는 것이 중요합니다. 플랫폼 형식(예 float* 또는 float[4]) 대신 DirectXMath 유형(예: XMFLOAT4)을 사용하면 적절한 데이터 요소 맞춤이 보장됩니다.

상수 버퍼에는 GPU에서 해당 데이터를 참조하는 데 사용되는 연결된 GPU 레지스터가 있어야 합니다. 데이터는 버퍼의 레이아웃에 표시된 대로 레지스터 위치에 압축됩니다.

Direct3D 11: 상수 버퍼 생성 및 데이터 업로드

struct ModelViewProjectionConstantBuffer
{
     DirectX::XMFLOAT4X4 mvp;
};

// ...

ModelViewProjectionConstantBuffer   m_constantBufferData;

// ...

XMStoreFloat4x4(&m_constantBufferData.mvp, mvpMatrix);

CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);
m_d3dDevice->CreateBuffer(
  &constantBufferDesc,
  nullptr,
  &m_constantBuffer);

셰이더의 HLSL에서 해당 상수 버퍼 선언은 다음과 같습니다.

Direct3D 11: 상수 버퍼 HLSL 선언

cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
  matrix mvp;
};

각 상수 버퍼에 대해 레지스터를 선언해야 합니다. Direct3D 기능 수준은 사용 가능한 최대 레지스터가 다르기 때문에 대상으로 하는 가장 낮은 기능 수준의 최대 수를 초과하지 마세요.

Direct3D 입력 레이아웃 및 HLSL 의미 체계에 대한 꼭짓점 특성 포트

셰이더 파이프라인에서 꼭짓점 데이터를 수정할 수 있으므로 OpenGL ES 2.0에서는 'uniforms' 대신 '특성'으로 지정해야 합니다. (이후 버전의 OpenGL 및 GLSL에서는 변경되었습니다.) 꼭짓점 위치, 법선, 탄젠트 및 색상 값과 같은 꼭짓점 관련 데이터는 셰이더에 특성 값으로 제공됩니다. 이러한 특성 값은 각 꼭짓점 데이터 요소에 대한 특정 오프셋에 해당합니다. 예를 들어 첫 번째 특성은 개별 꼭짓점의 위치 구성 요소를 가리키고 두 번째 특성은 법선 등을 가리킬 수 있습니다.

기본 메모리에서 GPU로 꼭짓점 버퍼 데이터를 이동하는 기본 프로세스는 다음과 같습니다.

  • glBindBuffer로 꼭짓점 데이터를 업로드합니다.
  • glGetAttribLocation으로 GPU에서 특성의 위치를 가져옵니다. 꼭짓점 데이터 요소의 각 특성에 대해 호출합니다.
  • glVertexAttribPointer를 호출하여 개별 꼭짓점 데이터 요소 내에서 올바른 특성 크기 및 오프셋을 설정합니다. 각 특성에 대해 이 작업을 수행합니다.
  • glEnableVertexAttribArray를 사용하여 꼭짓점 데이터 레이아웃 정보를 사용하도록 설정합니다.

OpenGL ES 2.0: 셰이더 특성에 꼭짓점 버퍼 데이터 업로드

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->vertexBuffer);
loc = glGetAttribLocation(renderer->programObject, "a_position");

glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 
  sizeof(Vertex), 0);
loc = glGetAttribLocation(renderer->programObject, "a_color");
glEnableVertexAttribArray(loc);

glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, 
  sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glEnableVertexAttribArray(loc);

이제 꼭짓점 셰이더에서 glGetAttribLocation 호출에서 정의한 것과 동일한 이름의 특성을 선언합니다.

OpenGL ES 2.0: GLSL에서 특성 선언

attribute vec4 a_position;
attribute vec4 a_color;                     

어떤 면에서는 Direct3D에 동일한 프로세스가 유지됩니다. 꼭짓점 데이터는 특성 대신 꼭짓점 버퍼 및 해당 인덱스 버퍼를 포함하는 입력 버퍼에 제공됩니다. 그러나 Direct3D에는 '특성' 선언이 없으므로 꼭짓점 버퍼에서 데이터 요소의 개별 구성 요소를 선언하는 입력 레이아웃과 꼭짓점 셰이더에서 해당 구성 요소를 해석할 위치와 방법을 나타내는 HLSL 의미 체계를 지정해야 합니다. HLSL 의미 체계를 사용하려면 셰이더 엔진의 용도를 알려주는 특정 문자열을 사용하여 각 구성 요소의 사용을 정의해야 합니다. 예를 들어, 꼭짓점 위치 데이터는 POSITION으로 표시되고, 일반 데이터는 NORMAL로 표시되며, 꼭짓점 색 데이터는 COLOR로 표시됩니다. (다른 셰이더 단계에도 특정 의미 체계가 필요하며, 이러한 의미 체계는 셰이더 단계에 따라 다르게 해석됩니다.) HLSL 의미 체계에 대한 자세한 내용은 셰이더 파이프라인 포팅HLSL 의미 체계를 참조하세요.

전체적으로 꼭짓점 및 인덱스 버퍼를 설정하고 입력 레이아웃을 설정하는 프로세스를 Direct3D 그래픽 파이프라인의 '입력 어셈블리'(IA) 단계라고 합니다.

Direct3D 11: 입력 어셈블리 단계 구성

// Set up the IA stage corresponding to the current draw operation.
UINT stride = sizeof(VertexPositionColor);
UINT offset = 0;
m_d3dContext->IASetVertexBuffers(
        0,
        1,
        m_vertexBuffer.GetAddressOf(),
        &stride,
        &offset);

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

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

꼭짓점 데이터 요소의 형식과 각 구성 요소에 사용되는 의미 체계를 선언하여 입력 레이아웃이 선언되고 꼭짓점 셰이더와 연결됩니다. 만든 D3D11_INPUT_ELEMENT_DESC에 설명된 꼭짓점 요소 데이터 레이아웃은 해당 구조의 레이아웃과 일치해야 합니다. 여기서는 다음 두 구성 요소가 있는 꼭짓점 데이터에 대한 레이아웃을 생성합니다.

  • 기본 메모리에 XMFLOAT3로 표시되는 꼭짓점 위치 좌표로, (x, y, z) 좌표에 대한 332비트 부동 소수점 값의 정렬된 배열입니다.
  • RGBA(색)에 대한 4개의 32비트 부동 소수점 값으로 정렬된 배열인 XMFLOAT4로 표시되는 꼭짓점 색 값입니다.

각각에 대한 의미 체계와 형식 형식을 할당합니다. 그런 다음 ID3D11Device1::CreateInputLayout에 설명을 전달합니다. 입력 레이아웃은 렌더링 메서드 중에 입력 어셈블리 설정 시 ID3D11DeviceContext1::IASetInputLayout을 호출할 때 사용됩니다.

Direct3D 11: 특정 의미 체계를 사용하여 입력 레이아웃 설명

ComPtr<ID3D11InputLayout> m_inputLayout;

// ...

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 },
};

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

// ...
// When we start the drawing process...

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

마지막으로 셰이더가 입력을 선언하여 입력 데이터를 이해할 수 있는지 확인합니다. 레이아웃에서 할당한 의미 체계는 GPU 메모리에서 올바른 위치를 선택하는 데 사용됩니다.

Direct3D 11: HLSL 의미 체계를 사용하여 셰이더 입력 데이터 선언

struct VertexShaderInput
{
  float3 pos : POSITION;
  float3 color : COLOR;
};