Interested Article - HLSL

HLSL ( англ. High Level Shader Language ) — C -подобный язык высокого уровня для программирования шейдеров .

Был создан корпорацией Microsoft и включён в пакет DirectX 9.0, выпущенный в 2002 году.

Типы данных

HLSL поддерживает скалярные типы, векторные типы, матрицы и структуры.

Скалярные типы

Название Характеристики типа
bool булевый тип
int 32-битовое знаковое целое
half 16-битовое число с плавающей точкой
float 32-битовое число с плавающей точкой
double 64-битовое число с плавающей точкой

Векторные типы

Примеры:

vector <float, 4> color;
float4 newcolor;
float oldcolor[4];
newcolor = float4(oldcolor[0], oldcolor[1], oldcolor[2], oldcolor[3]);

Матрицы

Примеры:

matrix <float, 4> view_matrix;
float 4x4 view_matrix;

Структуры

Примеры:

struct vs_input
{
   float4 pos:POSITION;
   float3 nor:NORMAL;
   float2 uv:TEXCOORD0;
};
struct ps_input
{
   float4 pos:POSITION;
   float3 nor:NORMAL;
   float2 uv:TEXCOORD0;
   float CustomVar;
   texture2D CustomTexture;
   //и так далее… :POSITION :NORMAL и т. д. это сентиматики, о них ниже.
};

Операторы

Операции Операторы
Арифметические -, +, *, /, %
Инкремент, декремент ++, --
Логические \|, ?:
Унарные !, -, +
Сравнения <, >, <=, >=, ==, !=
Назначение =, -=, +=, *=, /=
Приведение типов (тип)
Запятая ,
Член структуры .
Член массива [индекс]

Ветвления

if (выражение) <оператор> [ else <оператор>]

Циклы

В HLSL различают 3 вида циклов:

  • do <оператор> while (<выражение>);
  • while (<выражение>) <оператор>;
  • for (<выражение1>; <выражение2>; <выражение3>) <оператор>

Функции

математические функции

abs(x) возвращает абсолютную величину каждого компонента x
acos(x) возвращает арккосинус каждого компонента x. Каждый компонент должен быть в диапазоне [-1, 1]
asin(x) возвращает арксинус каждого компонента x. Каждый компонент должен быть в диапазоне [-pi/2, pi/2]
atan(x) возвращает арктангенс каждого компонента x. Каждый компонент должен быть в диапазоне [-pi/2, pi/2]
ceil(x) возвращает наименьшее целое число, которое больше чем или равно x (округление вверх)
cos(x) возвращает косинус x
cosh(x) возвращает гиперболический косинус x
clamp(x, a, b) Если x < a, то возвращает а, если x > b, то возвращает b, иначе возвращает x.
ddx(x) возвращает частную производную x относительно screen-space x-координаты
ddy(x) возвращает частную производную x относительно screen-space y-координаты
degrees(x) Конвертирование x с радианы в градусы
distance(a, b) возвращает расстояние между двумя точками a и b
dot(a, b) возвращает скалярное произведение двух векторов a и b
exp(x) возвращает экспоненту с основанием e, или e x
floor(x) возвращает самое большое целое число, которое является меньше чем или равным x (округление вниз)
frac( x ) возвращает дробную часть x.
fwidth(x) возвращает abs(ddx(x))+abs(ddy(x))
len(v) Векторная длина
length(v) возвращает длину вектора v
lerp(a, b, s) возвращает a + s (b — a)
log(x) возвращает логарифм x
log10(x) возвращает десятичный логарифм x
modf(x, out ip) возвращает на дробную и целую части x, каждая часть имеет тот же знак, что и x
mul(a, b) делает матричное умножение между a и b
normalize(v) возвращает нормированный вектор v
pow(x, y) возвращает x y
radians(x) конвертирует x из градусов в радианы
reflect(i, n) возвращает вектор отражения
refract(i, n, eta) возвращает вектор преломления.
round( x ) возвращает ближайшее целое.
rsqrt(x) возвращает 1 / sqrt(x)
saturate(x) Аналогично clamp(x,0,1)
sin(x) возвращает синус x.
sincos(x, out s, out c) возвращает синус и косинус x
sinh(x) возвращает гиперболический синус x
sqrt(x) возвращает квадратный корень каждого компонента
step(a, x) возвращает 1 если x >= a, иначе возвращает 0
tan(x) возвращает тангенс x
tanh(x) возвращает гиперболический тангенс x

Функции для работы с текстурами

tex1D(s, t) Чтение из одномерной текстуры
s — sampler, t — скаляр.
tex1D(s, t, ddx, ddy) Чтение из одномерной текстуры, с производными
s — sampler, t, ddx, и ddy — скаляры.
tex1Dproj(s, t) Чтение из одномерной проективной текстуры
s — sampler, t — 4D вектор.
t делится на t.w перед выполнением функции.
tex1Dbias(s, t) Чтение из одномерной текстуры со смещением, s — sampler, t — 4-мерный вектор.
Мип-уровень смещается на t.w до того, как производится поиск.
tex2D(s, t) Чтение из двухмерной текстуры
s — sampler, t — 2D вектор.
tex2D(s, t, ddx, ddy) Чтение из двухмерной текстуры, с производными.
s — sampler, t — 2D текстурные координаты. ddx, ddy- 2D вектора.
tex2Dproj(s, t) Чтение из двумерной проективной текстуры.
s — sampler, t — 4D вектор.
t делится на t.w перед выполнением функции.
tex2Dbias(s, t) Чтение из двумерной текстуры со смещением.
s — sampler, t — 4-мерный вектор.
Мип-уровень смещается на t.w до того, как производится поиск.
tex3D(s, t) Чтение из трёхмерной текстуры.
s — sampler, t — 3D вектор.
tex3D(s, t, ddx, ddy) Чтение из трёхмерной текстуры, с производными.
s — sampler, t — 2D текстурные координаты, ddx, ddy — 3D вектора.
tex3Dproj(s, t) Чтение из трёхмерной проективной текстуры.
s — sampler, t — 4D вектор.
t делится на t.w перед выполнением функции.
tex3Dbias(s, t) Чтение из трёхмерной текстуры со смещением.
s — sampler, t — 4-мерный вектор.
Мип-уровень смещается на t.w до того, как производится поиск.
texCUBE(s, t) Чтение из кубической текстуры.
s — sampler, t — 3D текстурные координаты.
texCUBE(s, t, ddx, ddy) Чтение из кубической текстуры.
s — sampler, t — 3D текстурные координаты, ddx, ddy — 3D вектора.
texCUBEproj(s, t) Чтение из кубической проективной текстуры.
s — sampler, t — 4D вектор.
t делиться на t.w перед выполнением функции.
texCUBEbias(s, t) Чтение из кубической текстуры.
sampler, t — 4D вектор.
Мип-уровень смещается на t.w до того, как производится поиск.

Входящие и исходящие данные для вершинного и пиксельного шейдеров

Вершинные и фрагментные шейдеры имеют два типа входящих данных: varying и uniform .

Uniform — данные, которые постоянны для многократного использования в шейдере. Объявление uniform данных в HLSL можно сделать двумя способами:

1)Объявить данные как extern переменную. Например:

float4 value;

float4 main () : COLOR
{
  return value;
}

2)Объявить данные через определитель uniform. Например:

float4 main (uniform float4 value) : COLOR
{
  return value;
}

Uniform переменные задаются через таблицу констант. Таблица констант содержит все регистры, которые постоянно используются в шейдере.

Varying — данные, которые являются уникальными для каждого вызова шейдера. Например: позиция, нормаль и т. д. В вершинном шейдере такая семантика описывает varying данные, которые передаются из вершинного буфера, а во фрагментном шейдере — интерполированные данные, полученные из вершинного шейдера.

Основные входящие семантические типы:

BINORMAL Бинормаль
BLENDWEIGHT Весовой коэффициент
BLENDINDICES Индекс весовой матрицы
COLOR Цвет
NORMAL Нормаль
POSITION Позиция
PSIZE Размер точки
TANGENT Тангент
TESSFACTOR Фактор тесселяции
TEXCOORD Текстурные координаты

Использование varying данных во фрагментном шейдере определяет состояние одного фрагмента. Основные входящие семантические типы:

COLOR Цвет
TEXCOORD Текстурные координаты

Исходящие данные для вершинного шейдера:

POSITION Позиция
PSIZE Размер точки
FOG Коэффициент «туманности» для вершины
COLOR Цвет
TEXCOORD Текстурные координаты

Исходящие данные для фрагментного шейдера:

COLOR Цвет
DEPTH Значение глубины

Программы для создания шейдеров

Для облегчения написания шейдеров существует ряд программ, позволяющих составлять шейдеры и тут же просматривать результат

Также пиксельные шейдеры используются визуализаторами, например,

  • Milkdrop от Nullsoft — Этот плагин позволяет создавать шейдеры, зависящие от музыки.

Примеры

Простейший шейдер «Texture mapping»

Код в этом листинге работает в ATI Rendermonkey и Nvidia FX composer. Для использования в кастомном движке нужно указать SamplerState и technique.

/* ========== ВЕРШИННЫЙ ШЕЙДЕР ========== */
/* world_matrix, view_matrix, proj_matrix необходимо получить из приложения, установив константы шейдера. 
Константы шейдера загружаются в регистры. */
float4x4 world_matrix; // мировая матрица
float4x4 view_matrix;  // матрица вида
float4x4 proj_matrix;  // матрица проекции

struct VS_OUTPUT // экземпляр этой структуры будет возвращать вершинный шейдер
{
   float4 Pos: POSITION0; /* POSITION0 и TEXCOORD0 - семантики, обозначающие слоты, из которых пиксельный 
шейдер будет в дальнейшем получать данные. Семантики, указанные здесь должны совпадать с семантиками во 
входных данных пиксельного шейдера. Имена переменных и их порядок может различаться.*/
   float2 TexCoord: TEXCOORD0;
};

VS_OUTPUT VS_Main(float4 InPos: POSITION0, float2 InTexCoord : TEXCOORD0) /* Вершинный шейдер выполняется 
для каждой вершины выводимого объекта. InPos и InTexCoord получены из данных stream-mapping'a */
{
   VS_OUTPUT Out;

   float4x4 worldViewProj_matrix = mul(world_matrix, view_matrix);
   worldViewProj_matrix = mul(worldViewProj_matrix, proj_matrix);

   Out.Pos = mul(InPos,  worldViewProj_matrix); // трансформируем вершину в clip-space
   Out.TexCoord = InTexCoord; // текстурные координаты мы получаем извне, ничего модифицировать не нужно

   return Out;
}

/* ========== ПИКСЕЛЬНЫЙ ШЕЙДЕР ========== */

sampler2D baseMap; // sampler2D - специальный слот "текстурный слот" в который можно загрузить текстуру.

float4 PS_Main(float2 texCoord: TEXCOORD0) : COLOR0 /* пиксельный шейдер всегда возвращает цвет выводимого 
пикселя с семантикой COLOR0 в формате float4. Пиксельный шейдер выполняется для каждого пикселя выводимого 
на экран изображения (а не для каждого текселя текстуры) */
{
   return tex2D( baseMap, texCoord ); /* tex2d(sampler2D, float2) читает из текстурного сэмплера 
(из текстуры) цвет её текселя с заданными текстурными координатами. Это и будет цвет выводимого пикселя. */
}

Простой шейдер «Головокружение»

float4x4 view_proj_matrix: register(c0);

struct VS_OUTPUT 
{
   float4 Pos: POSITION;
   float2 texCoord: TEXCOORD0;
};

VS_OUTPUT VS_Dizzy(float4 Pos: POSITION)
{
   VS_OUTPUT Out;

   Pos.xy = sign(Pos.xy);

   Out.Pos = float4(Pos.xy, 0, 1);
   Out.texCoord = Pos.xy;

   return Out;
}

float time_0_X: register(c0);
float rings: register(c1);
float speed: register(c2);
float exponent: register(c3);

float4 PS_Dizzy(float2 texCoord: TEXCOORD0) : COLOR 
{
   float ang = atan2(texCoord.x, texCoord.y);
   float rad = pow(dot(texCoord, texCoord), exponent);

   return 0.5 * (1 + sin(ang + rings * rad + speed * time_0_X));
}

Шейдер, имитирующий электрический разряд

struct VS_OUTPUT 
{
   float4 Pos: POSITION;
   float2 texCoord: TEXCOORD;
};

VS_OUTPUT VS_Electricity(float4 Pos: POSITION)
{
   VS_OUTPUT Out;

   // Clean up inaccuracies
   Pos.xy = sign(Pos.xy);

   Out.Pos = float4(Pos.xy, 0, 1);
   Out.texCoord = Pos.xy;

   return Out;
}

float4 color: register(c1);
float glowStrength: register(c2);
float height: register(c3);
float glowFallOff: register(c4);
float speed: register(c5);
float sampleDist: register(c6);
float ambientGlow: register(c7);
float ambientGlowHeightScale: register(c8);
float vertNoise: register(c9);
float time_0_X: register(c0);
sampler Noise: register(s0);

float4 PS_Electricity(float2 texCoord: TEXCOORD) : COLOR 
{
   float2 t = float2(speed * time_0_X * 0.5871 - vertNoise * abs(texCoord.y), speed * time_0_X);

   // Sample at three positions for some horizontal blur
   // The shader should blur fine by itself in vertical direction
   float xs0 = texCoord.x - sampleDist;
   float xs1 = texCoord.x;
   float xs2 = texCoord.x + sampleDist;

   // Noise for the three samples
   float noise0 = tex3D(Noise, float3(xs0, t));
   float noise1 = tex3D(Noise, float3(xs1, t));
   float noise2 = tex3D(Noise, float3(xs2, t));

   // The position of the flash
   float mid0 = height * (noise0 * 2 - 1) * (1 - xs0 * xs0);
   float mid1 = height * (noise1 * 2 - 1) * (1 - xs1 * xs1);
   float mid2 = height * (noise2 * 2 - 1) * (1 - xs2 * xs2);

   // Distance to flash
   float dist0 = abs(texCoord.y - mid0);
   float dist1 = abs(texCoord.y - mid1);
   float dist2 = abs(texCoord.y - mid2);

   // Glow according to distance to flash
   float glow = 1.0 - pow(0.25 * (dist0 + 2 * dist1 + dist2), glowFallOff);

   // Add some ambient glow to get some power in the air feeling
   float ambGlow = ambientGlow * (1 - xs1 * xs1) * (1 - abs(ambientGlowHeightScale * texCoord.y));

   return (glowStrength * glow * glow + ambGlow) * color;
}

Пластилиновая модель

float4x4 view_proj_matrix: register(c0);

float4 view_position: register(c4);

struct VS_OUTPUT 
{
	float4 Pos: POSITION;
	float3 normal: TEXCOORD0;
	float3 viewVec: TEXCOORD1;
};

VS_OUTPUT VS_Plastic(float4 Pos: POSITION, float3 normal: NORMAL)
{
	VS_OUTPUT Out;

	Out.Pos = mul(view_proj_matrix, Pos);

	Out.normal = normal;
	Out.viewVec = view_position - Pos;

	return Out;
}

float4 color: register(c0);

float4 PS_Plastic(float3 normal: TEXCOORD0, float3 viewVec: TEXCOORD1) : COLOR 
{
	float v = 0.5 * (1 + dot(normalize(viewVec), normal));

	return v * color;
}

Ссылки

Источник —

Same as HLSL