| netlib.narod.ru | < Назад | Оглавление | Далее > |
XNA поддерживает программирование шейдеров с использованием предлагаемого Microsoft высокоуровневого языка шейдеров (HLSL, High Level Shading Language). В HLSL есть небольшой набор функций, включающий математические операции, доступ к текстурам и управление потоком исполнения. Поддерживаемые HLSL типы данных похожи на используемые в языке C, за исключением векторов, матриц и объектов выборки.
HLSL поддерживает много различных типов данных, включая скаляры, векторы, матрицы и объекты выборки. В таблице 8.1 показаны представленные в языке скалярные типы данных. Заметьте, что для всех представленных в языке скалярных типов данных можно создавать векторы и матрицы, такие как float2, float4, bool3x3, double2x2 и т.д.
Таблица 8.1. Скалярные типы HLSL
| Тип | Значение |
| bool | true или false |
| int | 32-разрядное целое со знаком |
| half | 16-разрядное с плавающей точкой |
| float | 32-разрядное с плавающей точкой |
| double | 64-разрядное с плавающей точкой |
Другим представленным в HLSL типом данных является тип sampler, используемый для чтения (выборки) данных из текстур. Есть несколько различных типов объектов выборки, таких как sampler, sampler1D, sampler2D и sampler3D, используемых для выборки из одномерных, двухмерных и трехмерных текстур. Объект выборки имеет несколько связанных с ним состояний, которые задают текстуру из которой производится выборка, применяемый тип фильтрации и режим адресации (обертывания) текстуры. Ниже приведен пример объекта выборки для двухмерной текстуры:
// Объявляем входную текстуру
texture skyTexture;
// Объявляем объект выборки, используемый для
// доступа к skyTexture
sampler2D skySampler = sampler_state
{
Texture = skyTexture;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
AddressU = Wrap;
AddressV = Wrap;
AddressW = Wrap;
}
Состояние Texture представляет текстуру из которой будет производиться выборка; используя данный объект выборкм можно будет читать данные только из этой текстуры. MinFilter, MagFilter и MipFilter это состояния фильтрации, а AddressU, AddressV и AddressW — состояния адресации. За дополнительной информацией обращайтесь к документации DirectX SDK (http://msdn2.microsoft.com/en-us/library/bb509638.aspx), где есть полный справочник по представленным в HLSL типам данных.
В HLSL есть две разновидности входных типов данных: неизменные и меняющиеся. Неизменные входные данные — это данные, остающиеся в шейдере постоянными в ходе обработки всех входных данных. Например, во время визуализации машины, текстура и мировая матрица постоянны.
В шейдере неизменные входные данные обычно объявляются как глобальные переменные, в начале шейдера вне области видимости функций. Неизменные переменные запрашиваются и устанавливаются приложением, обычно с использованием их имен.
Меняющиеся входные данные — это данные, которые меняются при каждом исполнении шейдера. Например, во время визуализации машины, все вершины модели машины передаются этапу обработки вершин. Таким образом, каждый раз при выполнении вершинный шейдер получает в качестве входных данных вершину с отличающимися атрибутами (местоположением, цветом, нормалью и т.д.). Итак, атрибуты вершин не постоянны в процессе визуализации всей машины. В отличие от неизменных входных данных, вы объявляете меняющиеся входные данные используя семантику.
HLSL использует семантику для отображения входных и выходных данных на переменные. Например, вы используете семантику POSITION0 на этапе обработки вершин, чтобы отобразить атрибуты местоположения каждой вершины на меняющуюся переменную следующим образом:
float4 vertexPosition : POSITION0;
Семантика требуется для всех меняющихся входных данных (получаемых из приложения или передающихся между этапами визуализации). Например, всем выходным данным вершинного шейдера, которые будут использоваться в пиксельном шейдере, должна быть назначена семантика. Семантика нечувствительна к регистру символов и задается после имени переменной через двоеточие (:). В таблице 8.2 показаны некоторые семантики вершинных шейдеров.
Таблица 8.2. Семантики вершинных шейдеров
| Входная семантика | Описание | Тип |
| POSITION[n] | Местоположение вершины в пространстве объекта | float4 |
| COLOR[n] | Рассеиваемый и отражаемый цвет | float4 |
| NORMAL[n] | Вектор нормали | float4 |
| TEXCOORD[n] | Координаты текстуры | float4 |
| TANGENT[n] | Вектор касательной | float4 |
| BINORMAL[n] | Вектор бинормали | float4 |
| BLENDINDICES[n] | Индексы смешивания костей | int4 |
| BLENDWEIGHT[n] | Веса смешивания костей | float4 |
| Выходная семантика | Описание | Тип |
| POSITION[n] | Местоположение вершины в однородном пространстве (X, Y, Z, W) | float4 |
| COLOR[n] | Рассеиваемый или отражаемый цвет | float4 |
| TEXCOORD[n] | Координаты текстуры | float4 |
| FOG | Туман вершины | float |
Вы используете входные семантики вершинного шейдера для меняющихся данных, получаемых вершинным шейдером. Наиболее часто используются семантики POSITION, COLOR, NORMAL и TEXCOORD. Семантики TANGENT и BINORMAL вы применяете, если у вершины есть вектор касательной (tangent) или вектор бинормали (binormal), которые перпендикулярны вектору нормали вершины. Вы используете эти три вектора для создания системы координат, которая будет параллельна поверхности объекта в заданной точке (называемой касательным пространством). Семантики BLENDINDICES и BLENDWEIGHT вы используете когда вершины связаны с костями (bone). Кости используются для деформирования вершин сетки и будут рассмотрены в главе 11.
Обратите внимание, что [n] — это необязательное целое число, определяющее количество используемых ресурсов. Например, если у модели три текстуры, [n] в семантике TEXCOORD может варьироваться от 0 до 2. Таким образом, допустимыми входными семантиками для вершинного шейдера будут TEXCOORD0, TEXCOORD1 и TEXCOORD2. Таблица 8.3 показывает некоторые семантики пиксельных шейдеров.
Таблица 8.3. Семантики пиксельных шейдеров
| Входная семантика | Описание | Тип |
| COLOR[n] | Рассеиваемый или отражаемый цвет | float4 |
| TEXCOORD[n] | Координаты текстуры | float4 |
| Выходная семантика | Описание | Тип |
| COLOR[n] | Итоговый цвет | float4 |
| DEPTH[n] | Итоговая глубина | float |
Поскольку пиксельный шейдер выполняется после этапа растеризации, доступными входными семантиками являются цвет пикселя и некоторые координаты текстуры. Координаты текстуры являются позволяют адресоваться к местоположению в текстуре, отображаемому на текущий пиксель. Обратите внимание, что используя координаты текстуры вы можете выводить из вершинного шейдера свои собственные произвольные данные.
Итоговыми выходными данными пиксельного шейдера являются цвет пикселя и его глубина, причем вывод цвета пикселя обязателен, а глубину пикселя можно не выводить.
HLSL позволяет создавать функции, используя синтаксис, подобный языку C, где у каждой функции есть объявление и тело. Объявление функции содержит имя функции и тип возвращаемого значения, а также может иметь список параметров. Кроме того, у типа, возвращаемого функцией, может быть связанная с ним семантика. Ниже показан код функции, используемой как точка входа пиксельного шейдера. Мы покажем, как определить, какие функции будут использованы в качестве точек входа для вершинного и пиксельного шейдера, в разделе «Эффекты».
float4 simplePS(float4 inputColor : COLOR0) : COLOR0
{
return inputColor * 0.5f;
}
Поскольку функция simplePS используется как точка входа пиксельного шейдера, каждому из ее параметров должна быть назначена семантика. В данном случае функция simplePS масштабирует полученный параметр цвета, используя коэффициент 0.5, и возвращает итоговый цвет пикселя. Обратите внимание, что параметры функции могут иметь другие модификаторы, такие как in, out и inout, применяемые для описания входных, выходных и двунаправленных параметров.
В HLSL есть небольшой набор функций, включающий математические операции, доступ к текстурам и управление потоком выполнения. Эти функции называются встроенными, поскольку они реализованы в HLSL и не всегда непосредственно отображаются на инструкции ассемблера GPU. Фактически, многим из этих инструкций соответствует несколько команд ассемблера GPU, и они, вероятно, обеспечат лучшее выполнение поставленной задачи. В таблице 8.4 показаны некоторые из имеющихся в HLSL функций.
Таблица 8.4. Некоторые функции HLSL
| Функция | Описание |
| dot | Возвращает скалярное произведение двух векторов |
| cross | Возвращает векторное произведение двух трехмерных векторов с плавающей точкой |
| lerp | Выполняет линейную интерполяцию между двумя значениями |
| mul | Выполняет матричное умножение X и Y |
| normalize | Нормализует заданный вектор с плавающей точкой |
| pow | Возвращает X в степени Y |
| reflect | Возвращает вектор отражения, получая направление входящего луча и нормаль поверхности |
| refract | Возвращает вектор преломления, используя направление входящего луча, нормаль поверхности и коэффициент преломления |
| saturate | Ограничивает заданное значение диапазоном от 0 до 1 |
| tex2d | Выполняет просмотр двухмерной текстуры |
| tex3d | Выполняет просмотр трехмерной объемной текстуры |
В этом разделе вы создадите свой первый шейдер, используя HLSL. Сперва вы должны объявить неизменные и меняющиеся переменные шейдера:
// Матрица, получаемая от приложения - неизменная
// (Мировая * Вид * Проекция)
float4x4 matWVP : WorldViewProjection;
// Структура, используемая для входных вершин - меняющаяся
struct vertexInput
{
float4 position : POSITION0;
};
// Структура, используемая для передачи выходных данных
// вершинного шейдера на вход пиксельного шейдера - меняющаяся
struct vertexOutput
{
float4 hposition : POSITION;
float3 color : COLOR0;
};
В показанном коде вы используете структуру vertexInput для передачи данных от приложения вершинному шейдеру, и структуру vertexOutput для передачи данных от вершинного шейдера к пиксельному шейдеру.
У структуры vertexInput есть единственный атрибут: местоположение вершины. В структуре vertexOutput два атрибута, и ими являются итоговое местоположение вершины и цвет. Итак, функция вершинного шейдера получает местоположение вершины и должна вывести итоговое местоположение и цвет.
Заметьте, что выводимое вершинным шейдером местоположение вершины недоступно пиксельному шейдеру. Затем объявим функцию, используемую как точка входа вершинного шейдера:
// Код вершинного шейдера
pixelInput SimpleVS(vertexInput IN)
{
pixelInput OUT;
// Преобразуем местоположение вершины
OUT.hposition = mul(IN.position, matWVP);
OUT.color = float3(1.0f, 1.0f, 0.0f);
return OUT;
}
В функции SimpleVS вы вычисляете местоположение выходной вершины трансформировав (умножив) ее посредством матрицы matWVP (которая является комбинацией мировой матрицы, матрицы вида и матрицы проекции). Выходной цвет вершины делается желтым, RGB(1, 1, 0). Теперь вы должны объявить функцию, используемую как точка входа пиксельного шейдера:
// Код пиксельного шейдера
float4 SimplePS(pixelInput IN) : COLOR0
{
return float4(IN.color.rgb, 1.0f);
}
Созданный пиксельный шейдер просто возвращает цвет, полученный от этапа обработки вершин. Этот цвет будет использован как итоговый цвет пикселя.
Эффекты — это сущности, которые могут хранить различные шейдеры, техники и конфигурации для фиксированных этапов конвейера визуализации. Помимо шейдеров в эффекте должна быть одна или несколько техник (technique). Техника используется, чтобы определить, как находящиеся внутри эффекта шейдеры должны быть скомпилированы и скомпонованы для визуализации. Например, поскольку в эффекте может быть много вершинных и пиксельных шейдеров, каждая техника должна определить свою комбинацию (компоновку) вершинных и пиксельных шейдеров. В техниках также может быть больше одного прохода, и весь процесс визуализации повторяется для каждого прохода. Более того, внутри техники можно сконфигурировать некоторые параметры этапов растеризации и объединения вывода.
Использование эффектов облегчает программирование шейдеров, позволяя повторно использовать код шейдера в различных техниках, а также создавать различные техники, нацеленные на слабые и мощные графические процессоры. Ниже приведен код техники:
technique basicShader
{
pass p0
{
VertexShader = compile vs_2_0 SimpleVS();
PixelShader = compile ps_2_0 SimplePS();
}
}
В технике basicShader всего один проход, названный p0. Внутри каждого прохода вы определяете, какие функции будут использованы в качестве точек входа вершинного и пиксельного шейдеров. В данном случае вы используете функцию SimpleVS как точку входа вершинного шейдера и функцию SimplePS как точку входа пиксельного шейдера. Вам также надо определить какая шейдерная модель будет использована при компиляции шейдеров. В данном случае вы используете шейдерную модель 2.0.
Шейдерные модели эволюционируют, начиная с DirectX 8.1, и XNA поддерживает шейдерные модели вплоть до 3.0. Каждая шейдерная модель отличается своими возможностями и позволяет создавать шейдеры с различным количеством команд. Например, операции динамического управления потоком выполнения (if, while и т.д.) для вершинных и пиксельных шейдеров доступны только в шейдерной модели версии 3.0 или выше. Таблица 8.5 показывает количество слотов инструкций, доступное в каждой из шейдерных моделей. Обратите внимание, что каждая функция HLSL может использовать один или несколько слотов инструкций, поскольку нет прямого отображения на команды ассемблера GPU.
Таблица 8.5. Количество слотов инструкций в шейдерных моделях
| Версия шейдеров | Количество инструкций |
| VS_1_1 | 128 |
| VS_2_0 | 256 |
| VS_2_A | 256 |
| VS_3_0 | >= 512 |
| PS_1_1 | 12 (4 текстурных и 8 арифметических) |
| PS_2_0 | 96 (32 текстурных и 64 арифметических) |
| PS_2_A | 512 |
| PS_3_0 | >= 512 |
| netlib.narod.ru | < Назад | Оглавление | Далее > |