Урок 9. Шейдеры в Direct3D11

22.09.2012

Урок 9. Шейдеры в Direct3D11

В этом уроке вы узнаете: какая архитектура шейдеров; какую роль выполняют шейдеры в DirectX приложении; что такое язык HLSL и для чего он предназначен, ознакомитесь со базовыми принципами программирования HLSL.

Шейдеры

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

Сейчас шейдеры являются ключевым аспектом DirectX приложения. Фактически, трудно что-нибудь сделать без их использования. Вся графика игр, многочисленные спецэффекты, и тот реализм, который на сегодняшний день достигла графическая часть игр – это всё получилось благодаря использованию шейдеров. Взглянем лишь на некоторые примеры использования шейдеров:

  • Обычное применение: матрицы камеры и освещениями в вершинномшейдере, текстуры и определение текущего цвета пикселя в пиксельном шейдере
  • Композиции из нескольких текстур, использования глубоких текстур, для тонкой детализации ландшафта и моделей
  • Эффекты освещения и теней
  • Системы частиц – туман, задымление а также различные виды вспышек

На самом деле, данный список можно продолжать, кажется бесконечно. Существует слишком много примеров применения шейдеров, чтобы даже их просто перечислить. Запустите любую новую игру и существующий список применения шейдеров может быть пополнен несколькими, а то и десятками новых применений шейдеров.

Особенности компиляции и загрузки шейдера

Хотя шейдер является очень низкоуровневой структурой – он должен выполняться очень быстро, но вы можете его писать на языке, подобном C++, этот язык называется HLSL. Это потому, что шейдер на ходу компилируется в машинный код видеоускорителя. Кроме того, не нужен специальный компилятор. Язык HLSL является стандартным и его компиляция осуществляется на ходу любой 3d системой, например DirectX или OpenGL. Таким образом, вы просто указываете на исходный файл формата .fx и он автоматически загружается, компилируется и выполняется непосредственно в вашем 3d приложении.

Язык HLSL

Взглянем на пример шейдера с точки зрения программирования. Посмотрим на типичный код шейдера:

//------------------------------------------------------------------------------------
// Constant Buffer Variables
//------------------------------------------------------------------------------------
Texture2D txDiffuse : register( t0 );
SamplerState samLinear : register( s0 );
//------------------------------------------------------------------------------------
cbuffer ConstantBuffer: register( b0 )
{
    matrix World;
    matrix View;
    matrix Projection;
    float4 vMeshColor;
 
};
//------------------------------------------------------------------------------------
struct VS_INPUT
{
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD0;
};
 
struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float2 Tex : TEXCOORD0;
};
//------------------------------------------------------------------------------------
// Vertex Shader
//------------------------------------------------------------------------------------
PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = mul( input.Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Tex = input.Tex;
 
    return output;
}
//------------------------------------------------------------------------------------
// Pixel Shader
//------------------------------------------------------------------------------------
float4 PS( PS_INPUT input) : SV_Target
{
    return txDiffuse.Sample( samLinear, input.Tex ) * vMeshColor;
}

Вы видите, что весь код шейдера может быть разбит на три ключевых раздела. Это: объявления глобальных переменных – констант шейдера представленных в виде константного буфера, код вершинного шейдера, и наконец,  код пиксельного шейдера. В дальнейших разделах мы поэтапно рассмотрим структуру языка HLSL.

Типы данных HLSL

В каждом языке существуют различные встроенные типы данных. В языке HLSL типы данных носят некоторый математический оттенок, то есть в нем много типов данных наподобие векторов и матриц. Итак, таблица типов данных HLSL:

Тип данных Примечание
Buffer Массив
Scalar Одно компонентная скалярная величина
Vector,Matrix Несколько компонентный вектор или матрица
Sampler, Texture Встроенные типы данных – сэмплер, текстура
Struct, User Defined Пользовательский тип данных

Рассмотрим эти типы данных. Тип данных Scalar определяет скалярную величину и может быть одним из следующих:

bool
int
uint
half
float
double

Тип данных Vector и Matrix обозначается добавлением количества компонент к типу данных scalar. Например, Float4 определяет четырехкомпонентный вектор. Для матрицы, например Float4x4 добавляется NxN, то есть указывается размеренность матрицы. Пример определения вектора и матрицы:

float3 fVector = { 0.2f, 0.3f, 0.4f };
double3x3 dMatrix;   
float2x2 fMatrix = { 0.0f, 0.1, // row 1
                     2.1f, 2.2f // row 2  };

Типы Sampler используется для описания типа фильтрации текстуры. Тип Texture для указания на используемую текстуру. Примеры определений для этих типов данных, тип данных Sampler и Texture:

Texture2D txDiffuse : register( t0 );
SamplerState samLinear : register( s0 );

ТипданныхTexture:

float4 Color=txDiffuse.Sample( samLinear, input.Tex ) * vMeshColor;

Операции ветвления кода в HLSL

В большинстве случаев код HLSL выполняется построчно, то есть одна строка, или инструкция потом следующая и так далее. Но в некоторых случаях, можно добавлять циклы и условные переходы. Обратите внимание, что так как HLSL должен работать быстро, то ветвления в коде HLSL должны быть скорее исключением, чем практикой.

Операция ветвления Примечание
break Операция прерывания цикла
continue Продолжение цикла
discard Специфическое ветвление для пиксельногошейдера
do Ветвление
for Цикл
if Условный переход
switch Операция выбора
while Ветвление

Операции ветвления в HLSL имеют тот же синтаксис, что и в языке C++.

Определения функций и блоки в HLSL

Остальная структура языка может быть сведена к следующей таблице:

Элемент Примечание
Функции Определение функций
Операции ветвления См. предыдущий раздел
Блоки Блок определяется символами {}
Выражение return Операция возвращения из блока функции
Выражения Любое математическое выражение а также оператор присваивания.
Операторы Пример символов операторов: +,-,*,/,=,+=,++,<<,>>,==,!=,>=,!
Встроенные функции Intrinsic Functions

Определение функций имеет некоторые характерные черты в HLSL. Об этом далее.

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

Для вершинного и пиксельного шейдера существуют определенные типы данных, присущие им в качестве входящих данных и выходящих функций. Итак, вершинныйшейдер принимает в себя входящие данные, которые должны полностью соответствовать формату вершинного буфера. Пример формата входящих данных для вершинного шейдера:

struct VS_INPUT
{
    float3 Pos          : POSITION;         //position
    float3 Norm         : NORMAL;           //normal
    float2 Tex          : TEXCOORD0;        //texture coordinate
};

Выходящими данные вершинныйшейдер возвращает операцией return. Они должны содержать структуру, хранящую координаты а также любые другие произвольные данные.

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

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float3 Norm : TEXCOORD0;
    float2 Tex : TEXCOORD1;
};

Пример входящего формата пиксельного шейдера эта структура PS_INPUT. Пиксельный шейдер операцией return может возвратить единственную переменную формата float4, определяющую цвет точки.

float4 color= g_txDiffuse.Sample( ...
return color;

Встроенные функции HLSL

Встроенные функции HLSL позволяют осуществлять многие операции, которые невозможно выполнить обычным кодом HLSL. Встроенные функции обозначаются IntrinsicFuncions. В число этих функций входят математические функции, операции с матрицами и векторами, операции выборки из текстур а также много другое. Вот пример некоторых из встроенных функций HLSL:

abs
sin
cos
dot
cross
fmod
log
mul
max
min
round
sqrt
tan
tex1d
tex2d
texCUBE

В этом отрывке кода из вершинного шейдера

output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Norm = mul( input.Norm, World );

легко найти места, в которых вызывается встроенная функция шейдера mul, соответствующая операции умножения матриц.

Заключение

В этом уроке вы познакомились непосредственно с тем, что представляет из себяшейдер. HLSL не так сложен как многие другие языки, так как программы на нем – точнее фрагменты кода для пиксельного и вершинного шейдера – получаются очень короткими.

Заполнен: DirectX11
Присвоен тэг:

avatar

Об Авторе ()

Планета назначения, JO - 8703 - IV, обозначается светлой точкой на объемном экране корабля. К тому времени, как Робоид нанялся на юпитерианский круговой рейс, ему следовало бы стать главным инженером, но после Суховодного пионерского его вышибли, внеся в черный список, и высадили в Луна-Сити за то, что вместо слежения за приборами он провел время, программируя синтезатор пищи корабля строжайше запрещенной корабельными инструкциями последовательностью синтеза пива.

Комментирование закрыто.

Наверх