Перенос объектов шейдера
Важные 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