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


Перенос GLSL

Основные API

После того как вы перенесли код, создающий и настраивающий ваши буферы и объекты шейдеров, пора переписать код внутри этих шейдеров с OpenGL ES 2.0 GL Shader Language (GLSL) на высокоуровневый язык шейдеров Direct3D 11 (HLSL).

В OpenGL ES 2.0 шейдеры возвращают данные после выполнения с помощью встроенных функций, таких как gl_Position, gl_FragColor или gl_FragData[n] (где n — индекс для определенного целевого объекта отрисовки). В Direct3D нет конкретных встроенных функций, а шейдеры возвращают данные в качестве возвращаемого типа соответствующих функций main().

Данные, которые требуется интерполировать между этапами шейдера, например положение вершины или нормаль, обрабатываются с помощью объявления переменной. Однако Direct3D не имеет этого объявления; вместо этого любые данные, которые необходимо передавать между стадиями шейдера, должны быть помечены семантикой HLSL. Определенная семантика указывает назначение данных и имеет значение. Например, вы объявите данные вершин, которые необходимо интерполировать между шейдером фрагментов следующим образом:

float4 vertPos : POSITION;

или

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).

Инструкции

Шаг 1. Перенос шейдера вершин

В нашем простом примере OpenGL ES 2.0 шейдер вершин имеет три входных данных: матрицу константной модели-представления проекции 4x4 и два 4-координатных вектора. Эти два вектора содержат положение вершины и его цвет. Шейдер преобразует вектор положения в координаты перспективы и назначает их встроенной переменной gl_Position для растризации. Цвет вершин также копируется в переменную типа varying для интерполяции во время растеризации.

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 заполняется во время растризации и предоставляется шейдеру фрагмента (пикселя).

Шаг 2. Портирование фрагментного шейдера

Наш пример фрагментного шейдера в 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 до ширины целевого объекта отрисовки, y - от 0 до высоты целевого объекта отрисовки, z делится на исходное однородное значение координаты w (z/w), а w равно 1, делённое на исходное значение w (1/w).

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

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

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

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