Поделиться через


Перенос буферов вершин и данных

Важные API

На этом шаге вы определите буферы вершин, которые будут содержать сетки и буферы индекса, которые позволяют шейдерам проходить вершины в указанном порядке.

На этом этапе давайте рассмотрим жестко закодированную модель для используемой сетки куба. Оба представления имеют вершины, упорядоченные как список треугольников (в отличие от полосы или другого более эффективного макета треугольника). Все вершины в обоих представлениях также имеют связанные индексы и значения цвета. Большая часть кода Direct3D в этом разделе относится к переменным и объектам, определенным в проекте Direct3D.

Ниже приведен куб для обработки OpenGL ES 2.0. В примере реализации каждая вершина составляет 7 значений с плавающей запятой: 3 координаты положения, за которыми следует 4 значения цвета RGBA.

#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, давайте посмотрим, как загрузить данные куба для обработки в обеих моделях.

Instructions

Шаг 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 общего назначения и предоставляете шаг и смещение, чтобы вершинный шейдер может интерпретировать данные после отправки. Перед отображением шейдера перед отрисовкой атрибутов сопоставляются с частями каждого блока данных вершин с 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 },
};

Это описание входных данных определяет вершину как пару 2 3-координатных векторов: один трехмерный вектор для хранения положения вершины в координатах модели, а другой трехмерный вектор для хранения значения цвета RGB, связанного с вершиной. В этом случае используется формат с плавающей запятой 3x32, элементы которого мы представляем в коде как XMFLOAT3(X.Xf, X.Xf, X.Xf). Типы из библиотеки DirectXMath следует использовать при обработке данных, которые будут использоваться шейдером, так как это обеспечивает правильную упаковку и выравнивание этих данных. (Например, используйте XMFLOAT3 или XMFLOAT4 для векторных данных и XMFLOAT4X4 для матриц.)

Список всех возможных типов форматов см . в DXGI_FORMAT.

При определении макета ввода вершин создается объект макета. В следующем коде он записывается в m_inputLayout, переменная типа ComPtr (которая указывает на объект типа ID3D11InputLayout). 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, а затем задать буфер контекста устройства Direct3D, вызвав метод set, характерный для типа буфера, например ID3DDeviceContext::IASetVertexBuffers.

При установке буфера необходимо задать шаг (размер элемента данных для отдельной вершины), а также смещение (где фактически начинается массив данных вершин) с начала буфера.

Обратите внимание, что указатель на массив вершинIndices присваивается полю pSysMem структуры D3D11_SUBRESOURCE_DATA. Если это неправильно, сетка будет повреждена или пуста!

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 в идентификатор 3D11DeviceContext , созданный при настройке Direct3D. Для этого вызовите ID3D11DeviceContext::IASetIndexBuffer с настроенным подресурсом для массива индексов, как показано ниже. (Опять же, обратите внимание, что указатель на массив cubeIndices присваивается полю pSysMem структуры D3D11_SUBRESOURCE_DATA.)

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::D rawIndexed (или ID3D11DeviceContext::D raw для неиндексированных вершин), как показано ниже. (Для получения дополнительных сведений перейдите впередРисование на экране.)

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), с другой стороны, помещается в метод Render , так как именно здесь вы создаете этапы шейдера и привязываете данные.