Перенос буферов вершин и данныхPort the vertex buffers and data

Важные APIImportant APIs

На этом шаге вы определите буферы вершин, которые будут содержать ваши сетки, и буферы индексов, которые позволят шейдерам обходить вершины в указанном порядке.In this step, you'll define the vertex buffers that will contain your meshes and the index buffers that allow the shaders to traverse the vertices in a specified order.

Теперь давайте изучим кодированную модель для используемой сетки куба.At this point, let's examine the hardcoded model for the cube mesh we are using. Оба представления имеют вершины, упорядоченные в виде списка треугольников (в отличие от полосы или другого, более эффективного, макета из треугольников).Both representations have the vertices organized as a triangle list (as opposed to a strip or other more efficient triangle layout). Все вершины в обоих представлениях также имеют связанные с ними индексы и значения цвета.All vertices in both representations also have associated indices and color values. Большая часть кода Direct3D в этом разделе содержит ссылки на переменные и объекты, определенные в проекте Direct3D.Much of the Direct3D code in this topic refers to variables and objects defined in the Direct3D project.

Вот куб для обработки в OpenGL ES 2.0.Here's the cube for processing by OpenGL ES 2.0. В реализации образца каждая вершина имеет 7 значений типа float: 3 координаты позиции и последующие 4 значения цветов RGBA.In the sample implementation, each vertex is 7 float values: 3 position coordinates followed by 4 RGBA color values.

#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.And here's the same cube for processing by 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 представлен в левовинтовой системе координат.Reviewing this code, you notice that the cube in the OpenGL ES 2.0 code is represented in a right-hand coordinate system, whereas the cube in the Direct3D-specific code is represented in a left-hand coordinate system. При импорте ваших данных сетки необходимо обратить координаты по оси Z в вашей модели и соответственно изменить индексы для каждой сетки, чтобы обходить треугольники согласно измененной системе координат.When importing your own mesh data, you must reverse the z-axis coordinates for your model and change the indices for each mesh accordingly to traverse the triangles according to the change in the coordinate system.

Предполагая, что мы успешно переместили сетку куба из правовинтовой системы координат OpenGL ES 2.0 в левовинтовую систему координат Direct3D, посмотрим, как загружать данные куба для обработки в обеих моделях.Assuming that we have successfully moved the cube mesh from the right-handed OpenGL ES 2.0 coordinate system to the left-handed Direct3D one, let's see how to load the cube data for processing in both models.

InstructionsInstructions

Шаг 1. Создание макета входных данныхStep 1: Create an input layout

В OpenGL ES 2.0 данные вершин поставляются как атрибуты, которые передаются в объекты шейдера и читаются ими.In OpenGL ES 2.0, your vertex data is supplied as attributes that will be supplied to and read by the shader objects. Обычно вы предоставляете в объект программы-шейдера строку, которая содержит имя атрибута, используемое в GLSL шейдера, и получаете в ответ расположение в памяти, которое вы можете передать шейдеру.You typically provide a string that contains the attribute name used in the shader's GLSL to the shader program object, and get a memory location back that you can supply to the shader. В этом примере объект буфера вершин содержит список пользовательских структур Vertex, определенных и отформатированных следующим образом:In this example, a vertex buffer object contains a list of custom Vertex structures, defined and formatted as follows:

OpenGL ES 2.0: настройка атрибутов, которые содержат информацию о вершинахOpenGL ES 2.0: Configure the attributes that contain the per-vertex information.

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

В OpenGL ES 2,0 макеты ввода являются неявными; Вы принимаете _ буфер массива элементов общего назначения GL _ _ и предоставляете шаг и смещение, чтобы шейдер вершин мог интерпретировать данные после отправки.In OpenGL ES 2.0, input layouts are implicit; you take a general purpose GL_ELEMENT_ARRAY_BUFFER and supply the stride and offset such that the vertex shader can interpret the data after uploading it. Перед прорисовкой вы сообщаете шейдеру, какие атрибуты сопоставляются с частями каждого блока данных вершин, с помощью glVertexAttribPointer.You inform the shader before rendering which attributes map to which portions of each block of vertex data with glVertexAttribPointer.

В Direct3D необходимо предоставить макет входных данных, чтобы описать структуру данных вершин в буфере вершин, перед созданием буфера, а не перед рисованием геометрии.In Direct3D, you must provide an input layout to describe the structure of the vertex data in the vertex buffer when you create the buffer, instead of before you draw the geometry. Для этого используется макет входных данных, соответствующий макету данных индивидуальных вершин в памяти.To do this, you use an input layout which corresponds to layout of the data for our individual vertices in memory. Очень важно делать это аккуратно!It is very important to specify this accurately!

Здесь вы создадите входное описание как массив **D3D11 _ элементов входных данных _ _ ** типа "DESC".Here, you create an input description as an array of D3D11_INPUT_ELEMENT_DESC structures.

Direct3D: определение описания макета входных данныхDirect3D: Define an input layout description.

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

Это описание входных данных определяет вершину как пару 3-координатных векторов: один трехмерный вектор для сохранения позиции вершины в координатах модели и второй трехмерный вектор для сохранения RGB-значения цвета, соответствующего вершине.This input description defines a vertex as a pair of 2 3-coordinate vectors: one 3D vector to store the position of the vertex in model coordinates, and another 3D vector to store the RGB color value associated with the vertex. В данном случае используется 3x32-битовый формат с плавающей точкой, элементы которого представляются в коде как XMFLOAT3(X.Xf, X.Xf, X.Xf).In this case, you use 3x32 bit floating point format, elements of which we represent in code as XMFLOAT3(X.Xf, X.Xf, X.Xf). При любой обработке данных, которые будут использоваться шейдером, необходимо использовать библиотеку DirectXMath, обеспечивающую правильную упаковку и выравнивание данных.You should use types from the DirectXMath library whenever you are handling data that will be used by a shader, as it ensure the proper packing and alignment of that data. (Например, используйте XMFLOAT3 или XMFLOAT4 для данных векторов и XMFLOAT4X4 для матриц.)(For example, use XMFLOAT3 or XMFLOAT4 for vector data, and XMFLOAT4X4 for matrices.)

Список всех возможных типов форматов см. в ** _ формате DXGI**.For a list of all the possible format types, refer to DXGI_FORMAT.

Когда макет входных данных для вершин определен, вы создаете объект макета.With the per-vertex input layout defined, you create the layout object. В следующем коде вы записываете его в m _ инпутлайаут, переменную типа ComPtr (которая указывает на объект типа ID3D11InputLayout).In the following code, you write it to m_inputLayout, a variable of type ComPtr (which points to an object of type ID3D11InputLayout). fileData содержит компилированный объект вершинного шейдера из предыдущего шага, Перенос шейдеров.fileData contains the compiled vertex shader object from the previous step, Port the shaders.

Direct3D: создание макета входных данных, используемого буфером вершинDirect3D: Create the input layout used by the vertex buffer.

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

// ...

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

Мы определили макет входных данных.We've defined the input layout. Теперь создадим буфер, который использует этот макет и загружает его с данными сетки куба.Now, let's create a buffer that uses this layout and load it with the cube mesh data.

Шаг 2. Создание и загрузка буферов вершинStep 2: Create and load the vertex buffer(s)

В OpenGL ES 2.0 создается пара буферов, один для данных позиции и второй для данных цвета.In OpenGL ES 2.0, you create a pair of buffers, one for the position data and one for the color data. (Можно также создать элемент struct, содержащий данные позиции и цвета, и единый буфер.) Вы выполняете привязку каждого буфера и записываете в них позицию и цвет.(You could also create a struct that contains both and a single buffer.) You bind each buffer and write position and color data into them. Далее в своей функции обработчика снова выполните привязку буферов и передайте формат данных в буфере в шейдер, чтобы он мог правильно интерпретировать их.Later, during your render function, bind the buffers again and provide the shader with the format of the data in the buffer so it can correctly interpret it.

OpenGL ES 2.0: привязка буфера вершинOpenGL ES 2.0: Bind the vertex buffers

// 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 .In Direct3D, shader-accessible buffers are represented as D3D11_SUBRESOURCE_DATA structures. Чтобы привязать расположение этого буфера к объекту шейдера, необходимо создать _ _ структуру буфера DESC CD3D11 для каждого буфера с ID3DDevice:: CreateBuffer, а затем задать буфер контекста устройства Direct3D, вызвав метод Set, относящийся к типу буфера, например ID3DDeviceContext:: иасетвертексбуфферс.To bind the location of this buffer to shader object, you need to create a CD3D11_BUFFER_DESC structure for each buffer with ID3DDevice::CreateBuffer, and then set the buffer of the Direct3D device context by calling a set method specific to the buffer type, such as ID3DDeviceContext::IASetVertexBuffers.

При установке буфера необходимо задать шаг по индексу (размер данных элемента для отдельной вершины) и смещение (место, где фактически начинается массив данных вершины) от начала буфера.When you set the buffer, you must set the stride (the size of the data element for an individual vertex) as well the offset (where the vertex data array actually starts) from the beginning of the buffer.

Обратите внимание, что мы присваиваем указателю на массив вертексиндицес в поле псисмем структуры ** _ _ данных подресурса D3D11** .Notice that we assign the pointer to the vertexIndices array to the pSysMem field of the D3D11_SUBRESOURCE_DATA structure. Если это сделать неправильно, сетка будет поврежденной или пустой!If this isn't correct, your mesh will be corrupt or empty!

Direct3D: создание и установка буфера вершинDirect3D: Create and set the vertex buffer

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. Создание и загрузка буфера индексовStep 3: Create and load the index buffer

Буферы индексов являются эффективным средством разрешить вершинному шейдеру находить отдельные вершины.Index buffers are an efficient way to allow the vertex shader to look up individual vertices. Хотя они не являются обязательными, мы используем их в этом образце обработчика.Although they are not required, we use them in this sample renderer. Как и буферы вершин в OpenGL ES 2.0, буфер индексов создается и привязывается как буфер общего назначения, и в него копируются созданные ранее индексы вершин.As with vertex buffers in OpenGL ES 2.0, an index buffer is created and bound as a general purpose buffer and the vertex indices you created earlier are copied into it.

Когда вы готовы приступить к рисованию, вы снова выполняете привязку буфера вершин и буфера индексов, а затем вызываете glDrawElements.When you're ready to draw, you bind both the vertex and the index buffer again, and call glDrawElements.

OpenGL ES 2.0: отправка порядка индексов в вызов метода рисованияOpenGL ES 2.0: Send the index order to the draw call.

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 выполняется похожим образом, хотя и несколько более дидактически.With Direct3D, it's a bit very similar process, albeit a bit more didactic. Предоставьте буфер индексов как подчиненный ресурс Direct3D в ID3D11DeviceContext, созданный при настройке Direct3D.Supply the index buffer as a Direct3D subresource to the ID3D11DeviceContext you created when you configured Direct3D. Для этого вызывается ID3D11DeviceContext::IASetIndexBuffer с настроенным подчиненным ресурсом, как в следующем примере.You do this by calling ID3D11DeviceContext::IASetIndexBuffer with the configured subresource for the index array, as follows. (Опять же, обратите внимание, что указатель на массив кубеиндицес присваивается полю псисмем структуры ** _ _ данных подресурса D3D11** .)(Again, notice that you assign the pointer to the cubeIndices array to the pSysMem field of the D3D11_SUBRESOURCE_DATA structure.)

Direct3D: создание буфера индексовDirect3D: Create the index buffer.

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 для неиндексированных вершин), как в следующем примере.Later, you will draw the triangles with a call to ID3D11DeviceContext::DrawIndexed (or ID3D11DeviceContext::Draw for unindexed vertices), as follows. (Для получения более подробных сведений перейдите к разделу Рисование на экране.)(For more details, jump ahead to Draw to the screen.)

Direct3D: рисование индексированных вершинDirect3D: Draw the indexed vertices.

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

// ...

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

Предыдущий шагPrevious step

Перенос объектов шейдеровPort the shader objects

Дальнейшие действияNext step

Перенос GLSLPort the GLSL

RemarksRemarks

При структуризации Direct3D отдельно записывайте код, который вызывает методы для ID3D11Device, в метод, который вызывается каждый раз, когда требуется заново создавать ресурсы устройства.When structuring your Direct3D, separate the code that calls methods on ID3D11Device into a method that is called whenever the device resources need to be recreated. В шаблоне проекта Direct3D этот код находится в методах CreateDeviceResource объекта обработчика.(In the Direct3D project template, this code is in the renderer object's CreateDeviceResource methods. С другой стороны, код, обновляющий контекст устройства (ID3D11DeviceContext), помещается в метод Render, поскольку именно здесь вы конструируете стадии шейдера и привязываете данные.The code that updates the device context (ID3D11DeviceContext), on the other hand, is placed in the Render method, since this is where you actually construct the shader stages and bind the data.