Перенос GLSL
Важные API
После перемещения по коду, который создает и настраивает буферы и объекты шейдеров, пора перенести код внутри этих шейдеров из OpenGL ES 2.0 в ЯЗЫК шейдеров GL (GLSL) на высокоуровневый язык шейдеров Direct3D 11 (HLSL).
В OpenGL ES 2.0 шейдеры возвращают данные после выполнения с помощью встроенных функций, таких как gl_Position, gl_FragColor или gl_FragData[n] (где n — индекс для определенного целевого объекта отрисовки). В Direct3D нет конкретных встроенных функций, а шейдеры возвращают данные в качестве возвращаемого типа соответствующих функций main().
Данные, которые требуется интерполировать между этапами шейдера, например положение вершины или нормальное, обрабатываются с помощью разного объявления. Однако Direct3D не имеет этого объявления; вместо этого все данные, передаваемые между этапами шейдера, должны быть помечены семантикой HLSL. Определенная семантика указывает назначение данных и имеет значение. Например, вы объявите данные вершин, которые необходимо интерполировать между шейдером фрагментов следующим образом:
float4 vertPos : POSITION;
or
float4 vertColor : COLOR;
Где position является семантикой, используемой для указания данных положения вершин. POSITION также является особым случаем, так как после интерполяции он не может быть доступ к шейдеру пикселей. Поэтому необходимо указать входные данные для шейдера пикселей с SV_POSITION, а интерполированные данные вершин будут помещены в эту переменную.
float4 position : SV_POSITION;
Семантика может быть объявлена в основной (основной) методах шейдеров. Для шейдеров пикселей SV_TARGET[n], указывающих целевой объект отрисовки, требуется для метода body. (SV_TARGET без числовых суффиксов по умолчанию для отображения целевого индекса 0.)
Кроме того, обратите внимание, что шейдеры вершин необходимы для вывода семантики SV_POSITION системного значения. Эта семантика разрешает данные положения вершины для координат значений, где x составляет от -1 до 1, y составляет от -1 до 1, z делится на исходное однородное значение координаты w (z/w), а w — 1, разделенное на исходное значение w (1/w). Шейдеры пикселей используют семантику системного значения SV_POSITION, чтобы получить расположение пикселя на экране, где x составляет от 0 до ширины целевого объекта отрисовки Y от 0 до высоты целевого объекта отрисовки (каждое смещение на 0,5). Уровень компонентов 9_x шейдеры пикселей не могут считываться из значения SV_POSITION.
Буферы констант должны объявляться с cbuffer и быть связаны с определенным начальным регистром поиска.
Direct3D 11: объявление буфера констант HLSL
cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
matrix mvp;
};
Здесь буфер констант использует регистр b0 для хранения упакованного буфера. Все регистры ссылаются на форму b#. Дополнительные сведения о реализации буферов констант, регистров и упаковки данных HLSL считывают константы шейдера (HLSL).
В нашем простом примере OpenGL ES 2.0 шейдер вершин имеет три входных данных: матрицу константной модели-представления проекции 4x4 и два 4-координатных вектора. Эти два вектора содержат положение вершины и его цвет. Шейдер преобразует вектор положения в координаты перспективы и назначает его gl_Position встроенным для растризации. Цвет вершин копируется в переменную для интерполяции во время растеризации, а также.
OpenGL ES 2.0: шейдер вершин для объекта куба (GLSL)
uniform mat4 u_mvpMatrix;
attribute vec4 a_position;
attribute vec4 a_color;
varying vec4 destColor;
void main()
{
gl_Position = u_mvpMatrix * a_position;
destColor = a_color;
}
Теперь в Direct3D матрица проекции константной модели-представления содержится в буфере констант, упакованном в регистр b0, а положение вершин и цвет вершины специально отмечены соответствующей семантикой HLSL: POSITION и COLOR. Так как наш входной макет указывает на конкретное расположение этих двух значений вершин, вы создадите структуру для их хранения и объявите его в качестве типа входного параметра в функции тела шейдера (main). (Вы также можете указать их как два отдельных параметра, но это может получить громоздкие.) Вы также указываете тип вывода для этого этапа, который содержит интерполированную позицию и цвет, и объявляет его в качестве возвращаемого значения для функции тела вершинного шейдера.
Direct3D 11: шейдер вершин для объекта куба (HLSL)
cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
matrix mvp;
};
// Per-vertex data used as input to the vertex shader.
struct VertexShaderInput
{
float3 pos : POSITION;
float3 color : COLOR;
};
// Per-vertex color data passed through the pixel shader.
struct PixelShaderInput
{
float3 pos : SV_POSITION;
float3 color : COLOR;
};
PixelShaderInput main(VertexShaderInput input)
{
PixelShaderInput output;
float4 pos = float4(input.pos, 1.0f); // add the w-coordinate
pos = mul(mvp, projection);
output.pos = pos;
output.color = input.color;
return output;
}
Тип выходных данных PixelShaderInput заполняется во время растризации и предоставляется шейдеру фрагмента (пикселя).
Наш пример шейдера фрагментов в GLSL очень прост: укажите gl_FragColor встроенные с интерполированным значением цвета. OpenGL ES 2.0 записывает его в целевой объект отрисовки по умолчанию.
OpenGL ES 2.0: шейдер фрагментов для объекта куба (GLSL)
varying vec4 destColor;
void main()
{
gl_FragColor = destColor;
}
Direct3D почти так просто. Единственное существенное различие заключается в том, что функция тела шейдера пикселей должна возвращать значение. Так как цвет является 4-координатным значением с плавающей запятой (RGBA), вы указываете float4 в качестве возвращаемого типа, а затем укажите целевой объект отрисовки по умолчанию в качестве семантики системного значения SV_TARGET.
Direct3D 11: шейдер пикселей для объекта куба (HLSL)
struct PixelShaderInput
{
float4 pos : SV_POSITION;
float3 color : COLOR;
};
float4 main(PixelShaderInput input) : SV_TARGET
{
return float4(input.color, 1.0f);
}
Цвет пикселя в позиции записывается в целевой объект отрисовки. Теперь давайте посмотрим, как отобразить содержимое целевого объекта отрисовки на экране!
Перенос буферов вершин и данных
Понимание семантики HLSL и упаковки буферов констант может сэкономить немного головной боли отладки, а также обеспечить возможности оптимизации. Если вы получаете возможность, прочитайте синтаксис переменной (HLSL), введение в буферы в Direct3D 11 и практическое руководство. Создание буфера констант. Если нет, однако, вот несколько начальных советов, чтобы помнить о семантике и константных буферах:
- Всегда дважды проверьте код конфигурации Direct3D отрисовщика, чтобы убедиться, что структуры для буферов констант соответствуют объявлениям структуры cbuffer в HLSL и что скалярные типы компонентов соответствуют обоим объявлениям.
- В коде C++ отрисовщика используйте типы DirectXMath в объявлениях буфера констант, чтобы обеспечить правильную упаковку данных.
- Лучший способ эффективного использования буферов констант — упорядочить переменные шейдера в буферы констант на основе их частоты обновления. Например, если у вас есть однородные данные, обновляемые один раз на кадр, и другие унифицированные данные, обновляемые только при перемещении камеры, рассмотрите возможность разделения этих данных на два отдельных буфера констант.
- Семантика, которую вы забыли применить или которую вы применили неправильно, будет самым ранним источником ошибок компиляции шейдера (FXC). Дважды проверьте их! Документы могут быть немного запутанными, так как многие старые страницы и примеры ссылаются на различные версии семантики HLSL до Direct3D 11.
- Убедитесь, что вы знаете, какой уровень функций Direct3D предназначен для каждого шейдера. Семантика для уровня компонентов 9_* отличается от семантики для 11_1.
- Семантика SV_POSITION разрешает связанные данные позиции после интерполяции для координат значений, где x составляет от 0 до ширины целевого объекта отрисовки от 0 до высоты целевого объекта отрисовки, z делится на исходное однородное значение координаты (z/w), а w — 1, разделенное исходным значением w (1/w).
Практическое руководство. Перенос простого отрисовщика OpenGL ES 2.0 в Direct3D 11
Перенос буферов вершин и данных