- •Содержание
- •Очистка буферов графического устройства
- •Избирательная очистка буферов
- •Устранение скрытых дефектов приложения
- •Общие сведения о примитивах
- •Введение в hlsl
- •Типы данных hlsl
- •Функции и семантики hlsl
- •Техники, проходы и профили hlsl
- •IncludeHandler – объект, используемый для обработки директив #include в fx-файле. Так как наш файл не содержит директив #include, мы будем использовать значение null
- •Визуализация объекта, использующего эффект
- •Запускающее приложение
Функции и семантики hlsl
Данные в HLSL упаковываются в функции для выполнения определенных действий, например, можно объявить такую функцию
float4 MainVS(float3 pos)
{
return float4(pos, 1.0);
}
Но она не является полноценным векторным шейдером VS. С точки зрения DirectX это всего лишь простая функция, принимающая в качестве параметра трехмерный вектор и возвращающая четырехмерный вектор. Описание вершины может содержать множество разнородных данных: цвет, геометрические координаты и т.п. Поэтому мы должны указать, какой именно вид данных нужно обрабатывать в функции. В HLSL для этой цели используются так называемые семантики (semantics), предназначенные для указания именно тех данных, которые будут проходить через различные ступени обработки графического конвейера.
Для более сложных данных в названии семантики требуется указывать целочисленный индекс. При отсутствии индекса в названии семантики он полагается равным 0. Вот пример некоторых семантик с индексом n:
Семантика |
Описание |
POSITION[n] |
Координаты вершины |
COLOR[n] |
Цвет вершины |
PSIZE[n] |
Размер точки (при визуализации набора точек) |
Уточняющая семантика пишется через двоеточие после объявления обрабатываемого данного: для входного параметра функции - после его объявления в списке параметров, для выходного - после заголовка функции. Таким образом, в нашем примере для связи входного (выходного) параметра pos функции MainVS() с координатами вершины необходимо использовать семантику POSITION:
float4 MainVS(float3 pos : POSITION) : POSITION
{
return float4(pos, 1.0);
}
Вот теперь
мы наконец-то получили полноценный
вершинный шейдер VS.
Следующий этап - написание пиксельного
шейдера PS.
Наш пиксельный шейдер будет просто
закрашивать все пикселы цветом морской
волны (
Aqua = {0,
255, 255} ).
Но в HLSL для
определения цветов приняты вещественные
значения и яркость соответствующего
цвета модели RGB задается
в диапазоне (0.0, 1.0). Поэтому для цвета Aqua и
непрозрачного альфа-канала окончательный
код пиксельного шейдера будет таким
float4 MainPS() : COLOR
{
return float4(0.0, 1.0, 1.0, 1.0); // RGB hex=(0x00FFFF) и непрозрачный альфа-канал 0xFF
}
Техники, проходы и профили hlsl
Мы получили функции для вершинного и пиксельного процессора - вершинный и пиксельный шейдеры. Заключительный этап написания эффекта - создание техники (technique), использующей эти шейдеры. Ниже приведено определение техники с названием Fill, использующей вершинный шейдерMainVS() и пиксельный шейдер MainPS():
technique Fill
{
pass p0
{
VertexShader = compile vs_1_1 MainVS();
PixelShader = compile ps_1_1 MainPS();
}
}
Как видно, техника определяется с использованием ключевого слова technique. Каждая техника содержит один или несколько проходов, объявляемых с использованием ключевого слова pass. В свою очередь, каждому проходу ставится в соответствие пиксельный и вершинный шейдер. Наша техникаFill содержит единственный проход с названием p0, внутри которого используется синтаксис:
VertexShader = compile {используемый профиль} {вершинный шейдер};
PixelShader = compile {используемый профиль} {пиксельный шейдер};
Профиль шейдера (shader profile) определяет версию языка HLSL, на котором будет скомпилирован шейдер. Профиль учитывает архитектурные особенности целевого графического процессора при генерации промежуточного ассемблерного кода. В большинстве случаев каждой версии HLSLсоответствует один профиль. Например, языку Vertex Shader 1.1 соответствует профиль vs_1_1, Pixel Shader 1.4 – профиль ps_1_4, Pixel Shader 2.0 – профиль ps_2_0, и так далее. Однако некоторым языкам, вроде Pixel Shader 2.x, соответствует два профиля: в данном случае это ps_2_a и ps_2_b, при этом первый профиль генерирует код Pixel Shader 2.x, оптимизированный под архитектуру графического процессора NV3x, а второй – под R4xx. Ниже приведено соответствие между профилями и версиями HLSL.
Профиль |
Версия вершинных шейдеров |
vs_1_0 |
1.0 |
vs_1_1 |
1.1 |
vs_2_0 |
2.0 |
vs_2_a |
2.x |
vs_3_0 |
3.0 |
Профиль |
Версия пиксельных шейдеров |
ps_1_0 |
1.0 |
ps_1_1 |
1.1 |
ps_1_2 |
1.2 |
ps_1_3 |
1.3 |
ps_1_4 |
1.4 |
ps_2_0 |
2.0 |
ps_2_a |
2.x (оптимизация для NV3x) |
ps_2_b |
2.x (оптимизация для R4xx) |
ps_3_0 |
3.0 |
Большинство видеокарт поддерживает несколько профилей вершинных и пиксельных шейдеров. В результате каждый разработчик сталкивается с проблемой выбора используемого профиля. Чаще всего выбор версии шейдеров определяется минимальными требованиями к приложению.
Допустим, необходимо, чтобы наша программа могла работать на видеокартах класса ATI Radeon 9500 (R3xx) и выше, NVIDIA GeForce FX 5200 (NV3x)и выше, а так же Intel GMA 900 и выше. Все эти видеокарты поддерживают профили вершинных шейдеров vs_1_0, vs_1_1, vs_2_0 и профили пиксельные шейдеров ps_1_0, ps_1_1, ps_1_2, ps_1_3, ps_1_4 и ps_2_0. Таким образом, можно смело использовать профили vs_2_0 и ps_2_0 для всех шейдеров. При этом для некоторых эффектов можно предусмотреть дополнительные техники (technique) для видеокарт класса High End, использующих профили vs_3_0 и ps_3_0.
Если мы хотим поступиться эффективностью в пользу масштабируемости приложения, следует использовать минимальную версию профилей, необходимую для нормальной компиляции шейдеров, например, профили vs_1_1 и ps_1_1. Это позволит работать нашему приложению даже на стареньких видеокартах семейства GeForce3 (NV20).
Добавление эффекта в приложение
Пришла пора создать эффект для нашего упражнения - файл с расширением .fx, содержащий инструкции для графических ускорителей видеокарты. Эффект будет ориентирован на минимальную версию профилей.
В панели Solution Explorer вызовите контекстное меню для узла Application2 и командой Add/New Folder создайте папку с именем Data
В панели Solution Explorer вызовите контекстное меню для папки Data и командой Add/New Item добавьте в нее текстовый файл с именемColorFill.fx
увеличить изображение
В панели Solution Explorer выделите файл ColorFill.fx и в панели Properties установите для него свойство Copy to Output Directory в значение Copy if newer (копировать в каталог сборки свежую версию файла)
Заполните файл с эффектом следующим кодом
struct VertexInput
{
float4 pos : POSITION;
float4 color : COLOR;
};
struct VertexOutput
{
float4 pos : POSITION;
float4 color : COLOR;
};
VertexOutput MainVS(VertexInput input)
{
return input;
}
float4 MainPS(VertexOutput input):COLOR
{
return input.color;
}
technique Fill
{
pass p0
{
VertexShader = compile vs_1_1 MainVS();
PixelShader = compile ps_1_1 MainPS();
}
}
Для использования созданного эффекта его нужно загрузить из файла эффекта в приложение и откомпилировать в байт-код. Для этого применяется одна из перегрузок статического метода класса Effect, в том числе
public static Microsoft.Xna.Framework.Graphics.CompiledEffect CompileEffectFromFile(
string effectFile,
Microsoft.Xna.Framework.Graphics.CompilerMacro[] preprocessorDefines,
Microsoft.Xna.Framework.Graphics.CompilerIncludeHandler includeHandler,
Microsoft.Xna.Framework.Graphics.CompilerOptions options,
Microsoft.Xna.Framework.TargetPlatform platform
)
