XNA для начинающих. Структура шейдерной программы

XNA для начинающих. Структура шейдерной программы

Введение

Как говорилось в прошлой главе, XNA Framework поддерживает шейдеры на High Level Shader Language (HLSL). Описание шейдеров хранится в .fx файле. В данной главе будет рассмотрена структура шейдерной программы и приведен пример простейшего шейдера на HLSL. Также будет рассказано о том, как работать с шейдерами в XNA Game Studio.

Из чего состоит шейдерная программа

Шейдерная программа похожа на обычный файл с исходними кодами на любом С-подобном языке. Такая программа компилируется в машинный код для видео-карты и, затем, встраивается в графически конвейер на этапах обработки вершин и пикселей.

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

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

MSDN Library -> Win32 and COM Development -> Graphics and Multimedia -> DirectX -> SDK Documentation -> DirectX SDK -> DirectX Graphics -> HLSL

http://msdn.microsoft.com/en-us/library/ee418149(VS.85).aspx

Из чего же состоит шейдерная программа? Она состоит из следующих основных элементов:

  • Описание переменных и константы
  • Описание текстурных сэмплеров
  • Описание структур
  • Описание вспомогательных функций
  • Описание произвольного количества вершинных шейдеров
  • Описание произвольного количества пиксельных шейдеров
  • Описание техник

Далее рассмотрим каждый из элементов по отдельности:

HLSL является строго типизированным языком и каждая переменная и константа в шейдере должны иметь тип.

HLSL поддерживает следующие основные типы переменных:

  • Скаляры — int, float, double и т.д.
  • Векторы — float2, float3, float4 и т.д.
  • Матрицы — float3x3, float4x4 и т.д.

Так, например, если нужно описать переменную содержащую координаты объекта в трехмерной системе координат, можно введсти переменную:

float3 Position;

При описании переменной можно сразу задать для нее значение по умолчанию, это делается следующим образом:

float3 Position = float3(0,1,0);

В данном примере задается позиция, x- и z-координаты которой равны 0, а y-координа равна 1.

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

Position.x – обращение к x-координате (первая компонента вектора)

Position.y – обращение к y-координате (вторая компонента вектора)

Position.z – обращение к z-координате (третья компонента вектора)

А, например, Position.xy позволяет обратиться одновременно к x- и y-координатам. Примечательно то, что тип float3 также может обозначать цвет в формате rgb, поэтому можно обращаться к компонентам вектора следующим образом:

Position.r, Position.g, Position.b, Position.rgb и т.д.

Структуры:

Стуртуры позволяют вводить собственные составные типы данных в программы на HLSL.

struct MyStruct

{

float4 Position;

float4 Color;

};

В данном примере создается структура состоящая из двух переменных типа float4: Position и Color. Работа с переменными типа MyStructure осуществляется следующим образом

MyStruct myVariable;

myVariable.Position = float4(0,1,0,1);

myVariable.Color = float4(1,1,1,1);

Использование структур очень удобно использовать в сочетании с функциями описания вершинных и пиксельных шейдеров (о них речь пойдет далее). В таком случае для каждого элемента структуру указывается семантика, она позволяет видео-карте определить как именно работать с данной структурой.

struct НазваниеСтруктуры

{


ТипДанных НазваниеПеременной : Семантика;


};

Рассмотрим пример описания структуры включающей переменные семантикой:

struct VertexShaderInput

{


float4 Position : POSITION0;


float4 Color : COLOR0;

};

В данном примере создается стуктура аналогичная структуре в предыдущем примере с тем лишь отличием, что для элементов данной структуры задана семантика. Так, указывается, что переменная Position соответствует Позиции (то есть координатам), а Color – Цвету.

Вспомогательные функции:

HLSL позволяет определять вспомогательные функции в С-подобном стиле. Описание функции имеет следующий вид:

ТипВозвращаемогоЗначения Sum(Параметры)

{

Тело функции

}

Например, функция Sum возвращает сумму двух переданных ей параметров:

float Sum(float x, float y)

{

return x+y;

}

Вспомогательные функции удобно собирать в библиотеки и подключать к файлу с описанием шейдеров следующим образом:

#include «mylib.fx»

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

Сэмплеры

Сэмплеры (sampler) – специальный тип переменных. Они позволяют получать доступ к текстурам с учетом параметров фильтрации и множества других параметров, например, если имеется текстурный сэмплер MySampler и текстурные координаты texCoords, цвет нужного пикселя из текстуры можно получить следующим образом:

tex2D(mySampler, texCoords)

А следующий код создает переменную с типом сэмплер и названием s.

sampler s = sampler_state

{

texture = NULL;

mipfilter = LINEAR;

};

Вершинный шейдер

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

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

Пиксельный шейдер

Пиксельный шейдер – это специальная функция, которая получает интерполированные данные от вершинного шейдера. Каждый пиксель обрабатываетмого объекта поступает на пиксельный шейдер где для него определятся цвет.

Пиксельный шейдер должен возвращать как минимум цвет пикселя, однако в некоторых техниках удобно также возвращать и другие параметры, они никак не будут отображаться на экране монитора, однако могут быть использованы и использовании промежуточных поверхностей отрисовки, например, при разработке шейдеров отложенного шейдинга (deferred shading).

Описание техник

Последним из рассматриваемых элементов шейдерной программы является описание техник. Техника описывает метод обработки объектов, то есть указывает какие именно из описанных вершинных и пиксельных шейдеров следует применить для обрабатываемого объекта.

Файл с шейдерной программой может содержать произвольние количество различных техник, это нужно делать:

  • Для различных эффектов – файл с шейдерами может содержать совершенно различные эффекты, например эффекты анимации, освещения, шейдеры для систем частиц. Для каждого из таких эффектов можно использовать различные техники.
  • Для различных шейдерных моделей – можно разработать очень мощный шейдер, который будет создавать потрясающих визуальный эффект, однако никто не может гарантировать, что у игрока окажется достаточно мощная видео-карта, которая сможеть в полной мере справиться с данных эффектом. Для того, чтобы игра запустилась и работала на более старых видео-картах имеет слысм также разработать более простые шейдеры для более старых шейдерных моделей.
  • Для различных степеней качества – в некоторых случаях имеет смысл ухудшать качество шейдера, тем самым увеличивая быстродействие игры. Также можно разработать техники различных степеней удаления объектов от наблюдателя. Не всегда имеет смысл использовать технику, создающую наиболее красивое изображение, для объектов расположенных далеко от камеры. Визуальных эффект все равно будет слабо различим, а производительность будет низкой.
  • Для других нужд

Каждая техника может иметь несколько проходов. Это полезно для разработки сложных техник, однако пока мы не будет рассматривать такие техники.

Описание техники выглядит следующим образом:

technique НазваниеТехники

{


pass НазваниеПрохода

{


VertexShader = compile
vs_1_1 НазваниеФункцииВершинногоШейдера();


PixelShader = compile
ps_1_1 НазваниеФункцииПиксельногоШейдера ();

}

}

В данном примере
vs_1_1
и ps_1_1 — описание используемого шейдерного профиля, это не совсем то же самое, что и модель шейдеров, однако они связаны. О соответсвии шейдерных профилей и моделей можно узнать из библиотеки MSDN.

Основы работы с шейдерами в XNA Game Studio

Для того, чтобы начать создавать собственный шейдер, необходимо добавить в папку Content новый файл с эффектом (.fx-файл).

В появившемся окне выбрать Effect File и ввести имя файла.

XNA Game Studio создаст не пустой файл, а файл с уже написанным простейшим шейдеров. Рассмотрим исходный код данного шейдера:

float4x4 World;

float4x4 View;

float4x4 Projection;

// TODO: add effect parameters here.

struct VertexShaderInput

{


float4 Position : POSITION0;


// TODO: add input channels such as texture


// coordinates and vertex colors here.

};

struct VertexShaderOutput

{


float4 Position : POSITION0;


// TODO: add vertex shader outputs such as colors and texture


// coordinates here. These values will automatically be interpolated


// over the triangle, and provided as input to your pixel shader.

};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)

{

VertexShaderOutput output;


float4 worldPosition = mul(input.Position, World);


float4 viewPosition = mul(worldPosition, View);

output.Position = mul(viewPosition, Projection);


// TODO: add your vertex shader code here.


return output;

}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0

{


// TODO: add your pixel shader code here.


return
float4(1, 0, 0, 1);

}

technique Technique1

{


pass Pass1

{


// TODO: set renderstates here.


VertexShader = compile
vs_1_1 VertexShaderFunction();


PixelShader = compile
ps_1_1 PixelShaderFunction();

}

}

Шейдер имеет три переменные: матрицы World, View, Projection. Они не имеют значений по умолчанию и должны быть заполнены в программе на XNA Framework, то есть в Вашей будущей игре.

Не сложно догадаться, что эти переменные отвечают за Мировую матрицу, матрицу Вида и матрицу Проекции.

Далее следует описание двух структур: VertexShaderInput и VertexShaderOutput. Эти структуры будут использоваться в качестве входных и выходных параметров вершинного шейдера. Обратите внимание на то, что структуры состоят только из одного компонента – позиции вершины.

Далее идет описание вершинного шейдера, рассмотрим его более подробно:

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)

{

VertexShaderOutput output;


float4 worldPosition = mul(input.Position, World);


float4 viewPosition = mul(worldPosition, View);

output.Position = mul(viewPosition, Projection);


// TODO: add your vertex shader code here.


return output;

}

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

Далее создается переменная worldPosition, которая будет хранить значение мировых координат. Получить это значение можно домножив локальные координаты вершины, переданные в качестве элемента входной структуры, на Мировую матрицу. Это можно сделать при помощи функции mul, которая перемножает дле матрицы и возвращает результат перемножения.

Далее аналогичные способом вычисляются значения координат вида и проекции и последние сохраняеются в выходной структуре.

Вызов return output; возвращает из функции значение переменной output.

Теперь рассмотрим внимательнее пиксельный шейдер:

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0

{


// TODO: add your pixel shader code here.


return
float4(1, 0, 0, 1);

}

Функция пиксельного шейдера в данном случае возвращает только цвет пикселя и для нее указана семантика COLO0, которая указывает, что возращаемое значение является именно цветом.

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

Данный пиксельный шейдер никаким образом не использует входные параметры и закрашивает каждый пиксель объекта красным непрозрачным цветом (четвертая компонента цвета отвечает за прозрачность, она также назвается альфа-каналом).

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

Реклама
Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s