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 | < Назад | Оглавление | Далее > |