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


Перенос 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).

Instructions

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

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

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

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

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

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

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