При написании HLSL шейдерных программ, входные и выходные переменные должны иметь свое «намерение», указанное через семантику. Это стандартная концепция языка шейдеров HLSL; дополнительные сведения см. в документации по семантике в MSDN.
Семантика ввода вершинного шейдера
Главная вершина шейдерПрограмма, работающая на графическом процессоре. Подробнее
See in Словарь (обозначается #вершина прагмы
директива) должна иметь семантику для всех входных параметров.
Они соответствуют отдельным Meshосновному графическому примитиву Unity. Меши составляют большую часть ваших 3D-миров. Unity поддерживает триангулированные или четырехугольные полигональные сетки. Поверхности Nurbs, Nurms, Subdiv должны быть преобразованы в полигоны. Подробнее
Просмотр в Словарь элементов данных, таких как положение вершины, сетка нормалей и координаты текстуры .
Дополнительные сведения см. в разделе входные данные вершинной программы.
Вот пример простого вершинного шейдерапрограммы, которая запускается для каждой вершины 3D-модели, когда модель предоставлено. Подробнее
См. в Словарь, который принимает позицию вершины и координату текстуры в качестве входных данных. пиксельНаименьшая единица компьютерного изображения. Размер пикселя зависит от разрешения вашего экрана. Пиксельное освещение рассчитывается для каждого пикселя экрана. Подробнее
См. в шейдере Словарь
визуализирует координату текстуры в виде цвета.
Shader "Unlit/Show UVs"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct v2f {
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
v2f vert (
float4 vertex : POSITION, // vertex position input
float2 uv : TEXCOORD0 // first texture coordinate input
)
{
v2f o;
o.pos = UnityObjectToClipPos(vertex);
o.uv = uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.uv, 0, 0);
}
ENDCG
}
}
}
Вместо того, чтобы указывать все отдельные входные данные один за другим, также можно объявить их структуру и указать семантику для каждой отдельной переменной-члена структуры.
Семантика вывода фрагментного шейдера
Чаще всего фрагментный (пиксельный) шейдер выводит цвет и имеет семантику SV_Target
. Фрагментный шейдер в приведенном выше примере делает именно это:
fixed4 frag (v2f i) : SV_Target
Функция frag
имеет возвращаемый тип fixed4
(цвет RGBA низкой точности). Поскольку она возвращает только одно значение, семантика указана в самой функции, : SV_Target
.
Также можно вернуть структуру с выводами. Приведенный выше фрагментный шейдер можно было бы переписать таким же образом, и он будет работать точно так же:
struct fragOutput {
fixed4 color : SV_Target;
};
fragOutput frag (v2f i)
{
fragOutput o;
o.color = fixed4(i.uv, 0, 0);
return o;
}
Возврат структур из фрагментного шейдера в основном полезен для шейдеров, которые не просто возвращают один цвет. Дополнительная семантика, поддерживаемая выходными данными фрагментного шейдера, следующая.
SV_TargetN: Несколько целей рендеринга
SV_Target1
, SV_Target2
и т. д.: это дополнительные цвета, записываемые шейдером. Используется при рендерингепроцессе вывода графики на экран (или в текстуру рендеринга). По умолчанию основная камера в Unity отображает изображение на экране. Подробнее
Просмотр в Словарь одновременно в нескольких целевых объектах рендеринга (известный как Множественный Метод рендеринга Render Targets, или MRT). SV_Target0
совпадает с SV_Target
.
SV_Depth: вывод глубины пиксельного шейдера
Обычно фрагментный шейдер не переопределяет значение буфера Z, и используется значение по умолчанию из правильного треугольника растеризация процесс создания изображения путем вычисления пикселей для каждого многоугольника или треугольника во всей геометрии. Это альтернатива трассировке лучей.
См. в Словарь. Однако для некоторых эффектов полезно выводить пользовательские значения глубины Z-буфера на пиксель.
Обратите внимание, что на многих графических процессорах это отключает некоторый буфер глубиныхранилище памяти, в котором хранится глубина z-значения каждого пикселя. в изображении, где z-значение — это глубина каждого отображаемого пикселя от плоскости проекции. Подробнее
См. Словарь оптимизации, поэтому не переопределяйте значение буфера Z без уважительной причины. . Стоимость SV_Depth
варьируется в зависимости от архитектуры графического процессора, но в целом она довольно похожа на стоимость альфа-тестирования (с использованием встроенного clip ()
в HLSL). Отрисовывайте шейдеры, изменяющие глубину, после всех обычных непрозрачных шейдеров (например, с помощью AlphaTest
очереди рендеринга).
Выходное значение глубины должно быть одним числом с плавающей запятой
.
Выходные данные вершинного шейдера и входные данные фрагментного шейдера
Вершинный шейдер должен выводить окончательную позицию вершины в пространстве отсечения, чтобы графический процессор знал, где на экране ее растеризовать и на какой глубине. Этот вывод должен иметь семантику SV_POSITION
и иметь тип float4
.
Любые другие выходные данные («интерполяторы» или «вариации»), создаваемые вершинным шейдером, соответствуют требованиям вашего конкретного шейдера. Значения, выдаваемые вершинным шейдером, будут интерполированы по поверхности отображаемых треугольников, а значения каждого пикселя будут переданы в качестве входных данных для фрагментного шейдера.
Многим современным графическим процессорам на самом деле все равно, какая семантика у этих переменных; однако в некоторых старых системах (особенно в графических процессорах шейдерной модели 2 на Direct3D 9) были особые правила в отношении семантики:
-
TEXCOORD0
,TEXCOORD1
и т. д. используются для указания произвольных высокоточных данных, таких как координаты и положения текстур. -
Семантика
COLOR0
иCOLOR1
для выходных данных вершин и входных данных фрагментов предназначена для низкоточных данных диапазона 0–1 (например, простых значений цвета). ).
Для лучшей межплатформенной поддержки пометьте выходные данные вершин и входные данные фрагментов как семантику TEXCOORDn
.
Ограничения количества интерполяторов
Существуют ограничения на общее количество переменных интерполятора, которые можно использовать для передачи информации из вершины во фрагментный шейдер. Ограничение зависит от платформы и графического процессора, и общие рекомендации таковы:
-
До 8 интерполяторов: OpenGL ES 2.0 (Android), уровень Direct3D 11 9.x (Windows Phone) и модель шейдеров Direct3D 9 версии 2.0 (старые ПК). Поскольку количество интерполяторов ограничено, но каждый интерполятор может быть 4-компонентным вектором, некоторые шейдеры упаковывают все вместе, чтобы оставаться в пределах ограничений. Например, в одной переменной
float4
можно передать две координаты текстуры (.xy для одной координаты, .zw для второй координаты). -
До 10 интерполяторов: модель шейдеров Direct3D 9 версии 3.0 (
#pragma target 3.0
). - До 16 интерполяторов: OpenGL ES 3.0 (Android), Metal (iOS).
-
До 32 интерполяторов: модель шейдеров Direct3D 10 4.0 (
#pragma target 4.0
).
Независимо от вашего конкретного целевого оборудования, рекомендуется использовать как можно меньше интерполяторов из соображений производительности.
Другая специальная семантика
Позиция пикселя на экране: VPOS
Фрагментный шейдер может получать положение визуализируемого пикселя в виде специальной семантики VPOS
. Эта функция существует только начиная с модели шейдера 3.0, поэтому шейдер должен иметь директиву компиляции #pragma target 3.0
.
На разных платформах базовый тип ввода положения на экране различается, поэтому для максимальной переносимости используйте для него тип UNITY_VPOS_TYPE
(это будет float4
на большинстве платформ и float2 на Direct3D 9).
Кроме того, использование семантики положения пикселя затрудняет использование положения пространства отсечения (SV_POSITION) и VPOS в одной и той же структуре вершин-фрагментов. Таким образом, вершинный шейдер должен выводить позицию отсечения в пространстве как отдельную переменную «out». См. пример шейдера ниже:
Shader "Unlit/Screen Position"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
// note: no SV_POSITION in this struct
struct v2f {
float2 uv : TEXCOORD0;
};
v2f vert (
float4 vertex : POSITION, // vertex position input
float2 uv : TEXCOORD0, // texture coordinate input
out float4 outpos : SV_POSITION // clip space position output
)
{
v2f o;
o.uv = uv;
outpos = UnityObjectToClipPos(vertex);
return o;
}
sampler2D _MainTex;
fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
{
// screenPos.xy will contain pixel integer coordinates.
// use them to implement a checkerboard pattern that skips rendering
// 4x4 blocks of pixels
// checker value will be negative for 4x4 blocks of pixels
// in a checkerboard pattern
screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
float checker = -frac(screenPos.r + screenPos.g);
// clip HLSL instruction stops rendering a pixel if value is negative
clip(checker);
// for pixels that were kept, read the texture and output it
fixed4 c = tex2D (_MainTex, i.uv);
return c;
}
ENDCG
}
}
}
Ориентация лица: VFACE
Фрагментный шейдер может получать переменную, указывающую, обращена ли отображаемая поверхность к камереКомпонент, создающий изображение конкретная точка зрения в вашей сцене. Вывод либо рисуется на экране, либо фиксируется в виде текстуры. Подробнее
Смотрите в Словарь или смотрите в сторону от камеры. Это полезно при рендеринге геометрии, которая должна быть видна с обеих сторон — часто используется для листьев и подобных тонких объектов. Семантическая входная переменная VFACE
будет содержать положительное значение для передних треугольников и отрицательное значение для обращенных назад.
Эта функция доступна только начиная с модели шейдера 3.0 и выше, поэтому шейдер должен иметь директиву компиляции #pragma target 3.0
.
Shader "Unlit/Face Orientation"
{
Properties
{
_ColorFront ("Front Color", Color) = (1,0.7,0.7,1)
_ColorBack ("Back Color", Color) = (0.7,1,0.7,1)
}
SubShader
{
Pass
{
Cull Off // turn off backface culling
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
float4 vert (float4 vertex : POSITION) : SV_POSITION
{
return UnityObjectToClipPos(vertex);
}
fixed4 _ColorFront;
fixed4 _ColorBack;
fixed4 frag (fixed facing : VFACE) : SV_Target
{
// VFACE input positive for frontbaces,
// negative for backfaces. Output one
// of the two colors depending on that.
return facing > 0 ? _ColorFront : _ColorBack;
}
ENDCG
}
}
}
Приведенный выше шейдер использует состояние Cull, чтобы отключить отсечение обратной стороны (по умолчанию обращенные назад треугольники вообще не отображаются). Вот шейдер, примененный к набору Quadпримитивного объекта, который напоминает плоскость, но его края имеют длину всего одну единицу. использует только 4 вершины, а поверхность ориентирована в плоскости XY локального координатного пространства. Подробнее
См. в Словарь сетки, повернутые в разные стороны:
Идентификатор вершины: SV_VertexID
Вершинный шейдер может получать переменную, содержащую «номер вершины» в виде целого числа без знака. Это в основном полезно, когда вы хотите получить дополнительные данные для каждой вершины из текстур или ComputeBuffers.
Эта функция существует только в DX10 (модель шейдера 4.0) и GLCore/OpenGL ES 3, поэтому шейдер должен иметь директиву компиляции #pragma target 3.5
.
Shader "Unlit/VertexID"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.5
struct v2f {
fixed4 color : TEXCOORD0;
float4 pos : SV_POSITION;
};
v2f vert (
float4 vertex : POSITION, // vertex position input
uint vid : SV_VertexID // vertex ID, needs to be uint
)
{
v2f o;
o.pos = UnityObjectToClipPos(vertex);
// output funky colors based on vertex ID
float f = (float)vid;
o.color = half4(sin(f/10),sin(f/100),sin(f/1000),0) * 0.5 + 0.5;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return i.color;
}
ENDCG
}
}
}