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


Перенос объектов шейдера

Важные API

При переносе простого отрисовщика из OpenGL ES 2.0 сначала необходимо настроить эквивалентные объекты вершин и шейдеров фрагментов в Direct3D 11 и убедиться, что основная программа может взаимодействовать с объектами шейдера после их компиляции.

Примечание.

Вы создали новый проект Direct3D? Если нет, следуйте инструкциям в статье "Создание нового проекта DirectX 11 для универсальная платформа Windows (UWP)". В этом пошаговом руководстве предполагается, что вы создали ресурсы DXGI и Direct3D для рисования на экране и которые предоставляются в шаблоне.

Как и OpenGL ES 2.0, скомпилированные шейдеры в Direct3D должны быть связаны с контекстом рисования. Однако Direct3D не имеет концепции объекта программы шейдера на себя; Вместо этого необходимо назначить шейдеры непосредственно идентификатору ID3D11DeviceContext. На этом шаге выполняется процесс OpenGL ES 2.0 для создания и привязки объектов шейдера и предоставляется соответствующее поведение API в Direct3D.

Instructions

Шаг 1. Компиляция шейдеров

В этом простом примере OpenGL ES 2.0 шейдеры хранятся в виде текстовых файлов и загружаются в виде строковых данных для компиляции во время выполнения.

OpenGL ES 2.0: компиляция шейдера

GLuint __cdecl CompileShader (GLenum shaderType, const char *shaderSrcStr)
// shaderType can be GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. Returns 0 if compilation fails.
{
  GLuint shaderHandle;
  GLint compiledShaderHandle;
   
  // Create an empty shader object.
  shaderHandle = glCreateShader(shaderType);

  if (shaderHandle == 0)
  return 0;

  // Load the GLSL shader source as a string value. You could obtain it from
  // from reading a text file or hardcoded.
  glShaderSource(shaderHandle, 1, &shaderSrcStr, NULL);
   
  // Compile the shader.
  glCompileShader(shaderHandle);

  // Check the compile status
  glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compiledShaderHandle);

  if (!compiledShaderHandle) // error in compilation occurred
  {
    // Handle any errors here.
              
    glDeleteShader(shaderHandle);
    return 0;
  }

  return shaderHandle;

}

В Direct3D шейдеры не компилируются во время выполнения; они всегда компилируются в файлы CSO, когда остальная часть программы компилируется. При компиляции приложения с помощью Microsoft Visual Studio файлы HLSL компилируются в файлы CSO (CSO), которые приложение должно загрузить. Убедитесь, что эти файлы CSO включены в приложение при его упаковке!

Обратите внимание, что в следующем примере выполняется загрузка и компиляция шейдера асинхронно с помощью автоматического ключевого слова и лямбда-синтаксиса. ReadDataAsync() — это метод, реализованный для шаблона, который считывает в CSO-файле в виде массива данных байтов (fileData).

 

Direct3D 11: компиляция шейдера

auto loadVSTask = DX::ReadDataAsync(m_projectDir + "SimpleVertexShader.cso");
auto loadPSTask = DX::ReadDataAsync(m_projectDir + "SimplePixelShader.cso");

auto createVSTask = loadVSTask.then([this](Platform::Array<byte>^ fileData) {

m_d3dDevice->CreateVertexShader(
  fileData->Data,
  fileData->Length,
  nullptr,
  &m_vertexShader);

auto createPSTask = loadPSTask.then([this](Platform::Array<byte>^ fileData) {
  m_d3dDevice->CreatePixelShader(
    fileData->Data,
    fileData->Length,
    nullptr,
    &m_pixelShader;
};

Шаг 2. Создание и загрузка шейдеров вершин и фрагментов (пикселей)

OpenGL ES 2.0 имеет понятие шейдера "program", который служит интерфейсом между основной программой, работающей на ЦП и шейдерах, которые выполняются на GPU. Шейдеры компилируются (или загружаются из скомпилированных источников) и связаны с программой, которая обеспечивает выполнение на GPU.

OpenGL ES 2.0: загрузка шейдеров вершин и фрагментов в программу заливки

GLuint __cdecl LoadShaderProgram (const char *vertShaderSrcStr, const char *fragShaderSrcStr)
{
  GLuint programObject, vertexShaderHandle, fragmentShaderHandle;
  GLint linkStatusCode;

  // Load the vertex shader and compile it to an internal executable format.
  vertexShaderHandle = CompileShader(GL_VERTEX_SHADER, vertShaderSrcStr);
  if (vertexShaderHandle == 0)
  {
    glDeleteShader(vertexShaderHandle);
    return 0;
  }

   // Load the fragment/pixel shader and compile it to an internal executable format.
  fragmentShaderHandle = CompileShader(GL_FRAGMENT_SHADER, fragShaderSrcStr);
  if (fragmentShaderHandle == 0)
  {
    glDeleteShader(fragmentShaderHandle);
    return 0;
  }

  // Create the program object proper.
  programObject = glCreateProgram();
   
  if (programObject == 0)    return 0;

  // Attach the compiled shaders
  glAttachShader(programObject, vertexShaderHandle);
  glAttachShader(programObject, fragmentShaderHandle);

  // Compile the shaders into binary executables in memory and link them to the program object..
  glLinkProgram(programObject);

  // Check the project object link status and determine if the program is available.
  glGetProgramiv(programObject, GL_LINK_STATUS, &linkStatusCode);

  if (!linkStatusCode) // if link status <> 0
  {
    // Linking failed; delete the program object and return a failure code (0).

    glDeleteProgram (programObject);
    return 0;
  }

  // Deallocate the unused shader resources. The actual executables are part of the program object.
  glDeleteShader(vertexShaderHandle);
  glDeleteShader(fragmentShaderHandle);

  return programObject;
}

// ...

glUseProgram(renderer->programObject);

Direct3D не имеет концепции объекта программы шейдера. Скорее, шейдеры создаются при вызове одного из методов создания шейдера в интерфейсе ID3D11Device (например, ID3D11Device::CreateVertexShader или ID3D11Device::CreatePixelShader). Чтобы задать шейдеры для текущего контекста рисования, мы предоставляем им соответствующий идентификатор ID3D11DeviceContext с помощью метода шейдера набора, например ID3D11DeviceContext::VSSetShader для шейдера вершин или ID3D11DeviceContext::P SSetShader для шейдера фрагмента.

Direct3D 11: задайте шейдеры для контекста рисования графического устройства.

m_d3dContext->VSSetShader(
  m_vertexShader.Get(),
  nullptr,
  0);

m_d3dContext->PSSetShader(
  m_pixelShader.Get(),
  nullptr,
  0);

Шаг 3. Определение данных для предоставления шейдерам

В нашем примере OpenGL ES 2.0 у нас есть одна форма для объявления для конвейера шейдера:

  • u_mvpMatrix: массив 4x4 с плавающей запятой, представляющий окончательную матрицу преобразования проекции представления модели, которая принимает координаты модели для куба и преобразует их в 2D-координаты проекции для преобразования сканирования.

И два значения атрибута для данных вершин:

  • a_position: 4-плавающий вектор для координат модели вершины.
  • a_color: 4-плавающий вектор для значения цвета RGBA, связанного с вершиной.

Open GL ES 2.0: определения GLSL для униформ и атрибутов

uniform mat4 u_mvpMatrix;
attribute vec4 a_position;
attribute vec4 a_color;

В этом случае соответствующие основные переменные программы определяются как поля объекта отрисовщика. (См. заголовок в Практическое руководство. Перенос простого отрисовщика OpenGL ES 2.0 в Direct3D 11.) После этого необходимо указать расположения в памяти, в которых основная программа будет предоставлять эти значения для конвейера шейдера, который обычно выполняется прямо перед вызовом рисования:

OpenGL ES 2.0: маркировка расположения данных единого и атрибута


// Inform the shader of the attribute locations
loc = glGetAttribLocation(renderer->programObject, "a_position");
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 
    sizeof(Vertex), 0);
glEnableVertexAttribArray(loc);

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


// Inform the shader program of the uniform location
renderer->mvpLoc = glGetUniformLocation(renderer->programObject, "u_mvpMatrix");

Direct3D не имеет понятия "атрибут" или "униформы" в том же смысле (или, по крайней мере, он не использует этот синтаксис). Скорее, он имеет постоянные буферы, представленные как подресурсы Direct3D — ресурсы, совместно используемые между основной программой и программами шейдера. Некоторые из этих подресурсов, такие как позиции вершин и цвета, описываются как семантика HLSL. Дополнительные сведения о буферах констант и семантике HLSL по мере их связи с понятиями OpenGL ES 2.0, чтение объектов буфера кадра порта, униформ и атрибутов.

При перемещении этого процесса в Direct3D мы преобразуем униформу в буфер констант Direct3D (cbuffer) и назначаем ему регистр для поиска с семантикой HLSL. Два атрибута вершины обрабатываются как входные элементы этапов конвейера шейдера, а также назначаются семантики HLSL (POSITION и COLOR0), которые сообщают шейдерам. Шейдер пикселей принимает SV_POSITION с префиксом SV_, указывающим, что это системное значение, созданное GPU. (В этом случае это положение пикселя, созданное во время преобразования сканирования.) VertexShaderInput и PixelShaderInput не объявлены в качестве буферов констант, так как первый будет использоваться для определения буфера вершин (см . порт буферов вершин и данных), а данные для последнего создаются в результате предыдущего этапа в конвейере, который в данном случае является шейдером вершин.

Direct3D: определения HLSL для буферов констант и данных вершин

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

// Per-vertex data used as input to the vertex shader.
struct VertexShaderInput
{
  float4 pos : POSITION;
  float4 color : COLOR0;
};

// Per-vertex color data passed through the pixel shader.
struct PixelShaderInput
{
  float4 pos : SV_POSITION;
  float3 color : COLOR0;
};

Дополнительные сведения о переносе в буферы констант и применение семантики HLSL, чтение объектов буфера кадра порта, униформ и атрибутов.

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

Direct3D 11: объявление макета констант и буферов вершин

// Constant buffer used to send MVP matrices to the vertex shader.
struct ModelViewProjectionConstantBuffer
{
  DirectX::XMFLOAT4X4 modelViewProjection;
};

// Used to send per-vertex data to the vertex shader.
struct VertexPositionColor
{
  DirectX::XMFLOAT4 pos;
  DirectX::XMFLOAT4 color;
};

Используйте типы DirectXMath XM* для элементов буфера констант, так как они обеспечивают правильную упаковку и выравнивание содержимого при отправке в конвейер шейдера. Если вы используете стандартные типы и массивы платформы Windows, необходимо выполнить упаковку и выравнивание самостоятельно.

Чтобы привязать буфер константы, создайте описание макета в виде структуры CD3D11_BUFFER_DESC и передайте его в ID3Device::CreateBuffer. Затем в методе отрисовки перед рисованием перед использованием буфера константы перед рисованием перед рисованием перед использованием буфера константы передается в id3D11DeviceContext::UpdateSubresource .

Direct3D 11: привязка буфера констант

CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);

m_d3dDevice->CreateBuffer(
  &constantBufferDesc,
  nullptr,
  &m_constantBuffer);

// ...

// Only update shader resources that have changed since the last frame.
m_d3dContext->UpdateSubresource(
  m_constantBuffer.Get(),
  0,
  NULL,
  &m_constantBufferData,
  0,
  0);

Буфер вершин создается и обновляется аналогично, и рассматривается на следующем шаге перенос буферов вершин и данных.

Следующий шаг

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

Практическое руководство. Перенос простого отрисовщика OpenGL ES 2.0 в Direct3D 11

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

Перенос GLSL

Рисование на экране