Перейти к содержимому
Strategium.ru

Программирование шейдеров на HLSL


kuzmich774

Рекомендованные сообщения

kuzmich774
(изменено)

Нынешнее графическое железо, начиная с видеокарт GeForce FX5900 или Radeon9800 предоставляет пользователю возможность замены некоторых частей графического конвейера шейдером. Шейдер — это специальная программа, которая использует определенные программируемые регистры видеокарты для создания различных графических эффектов. Более новые видео-карты обеспечивают расширенные возможности программируемости шейдеров: огромное количество инструкций, динамическое ветвление и т.д.

 

Вступление

Для начала немного теории. На рисунке показана упрощенная модель графического конвейера, которая условно делится на две части: обработка геометрии и обработка фрагментов (пикселей).

3bbVnmL.jpeg

Рисунок 1. Обработка геометрии.

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

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

3lh5MxY.jpeg

Растеризатор делит треугольник на фрагменты (пиксели), для которых интерполируются текстурные координаты и цвет. Затем для каждого фрагмента происходит выполнение следующих операций: проверка принадлежности пикселя, наложение текстур (заданные для фрагмента координаты текстуры определяют интерполированный цвет из элементов текстурного изображения — текселей, значение этого цвета комбинируется с цветом фрагмента), применение эффектов тумана, альфа-тест, тест шаблона (stencil-test), тест глубины, смешивание, дизеринг и логические операции. После обработки всех этих методов полученный фрагмент помещается в буфер кадра, который впоследствии выводиться на экран.

В каждую из этих частей графического конвейера можно вставить свой определенный шейдер. Существует два различных шейдера: вершинный шейдер (vertex shader) и пиксельный шейдер (pixel shader).

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

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

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

Пиксельные шейдеры выполняются для каждого фрагмента в фазе растеризации треугольников. Фрагмент (или пиксель) — точка, с оконными координатами, полученная растеризатором после выполнения над ней ряда операций. Проще говоря, результирующая точка буфере кадра, совокупность этих точек потом формирует изображение. Пиксельные шейдеры оперирует над фрагментами до заключительных стадий, т.е. до тестов глубины, альфы и stencil. Кстати, возможно в будущем и этими операциями будет управлять шейдер, кто знает… Пиксельный шейдер получает интерполированные данные (цвет, текстурные координаты) из вершинного шейдера.

Обзор HLSL

Один из наиболее серьёзных компонентов в DirectX, предоставляющий программирование шейдеров, является язык высокого уровня HLSL(High-Level Shading Language), разработанный Microsoft,который уже упоминался в статье Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Зачем нужен HLSL? Область применения языка программирования графики высокого уровня весьма широка. Такой язык облегчает разработку графических эффектов и создание впечатляющих графических приложений. Кроме того, использование HLSL повышает читаемость кода шейдера и снижает время разработки тяжелого алгоритма. Приведем пример фрагментного шейдера на asm–языке:

ps_2_0

def c0, 1, 1, 0.5, 0.5

mov r0, c0

mov oC0, r0

и тоже самое на HLSL:

float4 main(): COLOR0

{ return float4(1.0,1.0,0.5,0.5); }

 

Можно сделать вывод, что HLSL очень удобен и понятен.

 

Описание языка HLSL

Типы данных

HLSL поддерживает разнообразные типы данных: от простых скаляров до комплексных типов — векторов и матриц.

Скалярные типы:

Тип Значение
bool  true или false
int  32-bit signed integer
half  16-bit floating point value
float  32-bit floating point value
double  64-bit floating point value

 

Не все графические процессоры в настоящее время поддерживают определенные типы данных (кроме float, конечно же). Например, целые числа могут эмулироваться через тип float.

Векторные типы:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 — означает вектор размерностью size, и скалярным типом type.

 

Можно объявлять еще так:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 – означает N компонентный вектор с данным типом type. Например:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

или тоже самое можно объявить так:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

или

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

Матрицы:

Подобно скалярам и векторам, матрицы могут иметь все скалярные типы.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 — матрица с типом type и размерностью M на N.

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 — матрица с типом type и размерностью M на N.

 

Можно контролировать ориентацию:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

Доступ к элементам и строкам матрицы:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

Переменные:

Переменная может быть объявлена как static или extern. Любая нестатическая переменная (записывается с приставкой extern или без приставки), которая объявлена за шейдером, может быть изменена через API. Статическая переменная используется только шейдером и не управляется API. 

Например:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Переменные a и d должны выставляться через API-функцию Set*ShaderConstant*() и их может изменять шейдер. Переменная b тоже задается через Set*ShaderConstant*(), но шейдер не может изменить ее значение. И переменная c не задается через API, но может быть изменена в шейдере.

Инициализация переменных выполняется также как и на языке C.

Например:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Структуры:

HLSL поддерживает структуры. Например:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Структуры могут быть приведены к/из скалярных величин, векторов, матриц и других структур.

Операторы

Существуют операторы для следующих операций:

Операции Операторы
Арифметические  -, +, *, /, %
Инкремент, декремент  ++, --
Логические  &&, ||, ?:
Унарные  !, -, +
Сравнения  <, >, <=, >=, ==, !=
Назначение  =, -=, +=, *=, /=
Привидение типов  (тип)
Комма  ,
Член структуры  .
Член массива  [индекс]

 

Оператор извлечения остатка деления (%) работает как с целыми числами, так и с числами с плавающей точкой.

Сравнение векторов производится покомпонентно.

Ветвление

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Циклы

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Функции

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

Некоторые математические инструкции, которые можно применять в HLSL:

abs(x) абсолютная величина (per-component).
acos(x) возвращает арккосинус каждого компонента x. Каждый компонент должен быть в диапазоне [-1, 1].
asin(x) возвращает арксинус каждого компонента x. Каждый компонент должен быть в диапазоне [-pi/2, pi/2].
atan(x) возвращает арктангенс каждого компонента x. Каждый компонент должен быть в диапазоне [-pi/2, pi/2].
ceil(x) Возвращает наименьшее целое число, которое больше чем или равно x.
cos(x) Возвращает косинус x.
cosh(x) Возвращает гиперболический косинус x.
ddx(x) Возвращает частную производную x относительно screen-space x-координаты.
ddy(x) Возвращает частную производную x относительно screen-space y-координаты.
degrees(x) Конвертирование x с радианы в градусы.
distance(a, b) Возвращает расстояние между двумя точками a и b.
dot(a, b) Возвращает dot product двух векторов a и b.
floor(x) Возвращает самое большое целое число, которое является меньше чем или равным x.
fwidth(x) Возвращает abs(ddx(x))+abs(ddy(x)).
len(v) Векторная длина.
length(v) Возвращает длину вектора v.
lerp(a, b, s) Возвращает a + s (b - a).
log(x) Возвращает логарифм x.
log10(x) Возвращает десятичный логарифм x.
mul(a, b) Делает матричное умножение между a и b.
normalize(v) Возвращает нормализированный вектор v.
pow(x, y) Возвращает xy.
radians(x) Конвертирует x из градусов в радианы.
reflect(i, n) Возвращает вектор отражения.
refract(i, n, eta) Возвращает вектор преломления.
rsqrt(x) Возвращает 1 / sqrt(x).
sin(x) Возвращает синус x.
sincos(x, out s, out c) Возвращает синус и косинус x.
sinh(x) Возвращает гиперболический синус x
sqrt(x) Возвращает квадратный корень(per-component).
step(a, x) Возвращает (x = a) ? 1 : 0.
tan(x) Возвращает тангенс x
tanh(x) Возвращает гиперболический тангенс x

Использование текстур во фрагментном шейдере.

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

tex1D(s, t) Чтение из одномерной текстуры. s - sampler. t - скаляр.
tex1D(s, t, ddx, ddy) Чтение из одномерной текстуры, с производными. s - sampler.
t, ddx, и ddy - скаляры.
tex1Dproj(s, t) Чтение из одномерной проективной текстуры. s - sampler. t - 4D вектор.
t делиться на t.w перед выполнением функции.
tex1Dbias(s, t) Чтение из одномерной текстуры со смещением, s - sampler, t - 4-х мерный вектор,
Мип-уровень смещается на t.w до того, как производится поиск.
tex2D(s, t) Чтение из двухмерной текстуры. s - sampler. t – 2D вектор.
tex2D(s, t, ddx, ddy) Чтение из двухмерной текстуры, с производными. s - sampler.
t – 2D текстурные координаты. ddx, ddy- 2D вектора.
tex2Dproj(s, t) Чтение из двумерной проективной текстуры. s - sampler. t - 4D вектор.
t делиться на t.w перед выполнением функции.
tex2Dbias(s, t) Чтение из двумерной текстуры со смещением, s - sampler, t - 4-х мерный вектор,
Мип-уровень смещается на t.w до того, как производится поиск.
tex3D(s, t) Чтение из трехмерной текстуры. s - sampler. t – 3D вектор.
tex3D(s, t, ddx, ddy) Чтение из трехмерной текстуры, с производными. s - sampler.
t – 2D текстурные координаты. ddx, ddy - 3D вектора.
tex3Dproj(s, t) Чтение из трехмерной проективной текстуры. s - sampler. t - 4D вектор.
t делиться на t.w перед выполнением функции.
tex3Dbias(s, t) Чтение из трехмерной текстуры со смещением, s - sampler,
t - 4-х мерный вектор, Мип-уровень смещается на t.w до того, как производится поиск.
texCUBE(s, t) Чтение из кубической текстуры. s – sampler,
t – 2D текстурные координаты.
texCUBE(s, t, ddx, ddy) Чтение из кубической текстуры. s - sampler.
t – 3D текстурные координаты. ddx, ddy - 3D вектора.
texCUBEproj(s, t) Чтение из кубической проективной текстуры. s – sampler, t - 4D вектор.
t делиться на t.w перед выполнением функции.
texCUBEbias(s, t) Чтение из кубической текстуры. s – sampler, t - 4D вектор.
Мип-уровень смещается на t.w до того, как производится поиск.

t – текстурные координаты. ddx, ddy – производные.

Есть полезное свойство констант в HLSL это связывание с регистрами констант, например:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

Определяет текстуру в 0 stage. А в API это задается так:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

Входные и исходящие параметры вершинного и фрагментного шейдеров.

Вершинные и фрагментные шейдера имеют два типа входящих данных: varying и uniform.

Uniform — данные, которые постоянны для многократного использования в шейдере. Объявление uniform данных в HLSL можно сделать двумя способами:

1)Объявить данные как extern переменную. Например:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

2)Объявить данные через определитель uniform. Например:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

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

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

POSITIONn Позиция.
BLENDWEIGHTn Весовой коэффициент
BLENDINDICESn Индекс весовой матрицы
NORMALn Нормаль.
PSIZEn Размер точки.
COLORn Цвет.
TEXCOORDn Текстурные координаты.
TANGENTn Тангент.
BINORMALn Бинормаль.
TESSFACTORn Фактор тесселяции.

Использование varying данных во фрагментном шейдере определяет состояние одного фрагмента. Основные входящие семантические типы:

COLORn Цвет.
TEXCOORDn Текстурные координаты.

n – определяет номер семантического типа. Например: TEXCOORD1, NORMAL0.

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

Исходящие данные для вершинного шейдера:

POSITION Позиция.
PSIZE Размер точки.
FOG Коэффициент “туманности” для вершины.
COLORn Цвет.
TEXCOORDn Текстурные координаты.

Исходящие данные для фрагментного шейдера:

COLORn Цвет.
DEPTH Значение глубины.

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

Программирование шейдеров на HLSL

Первый эффект, который мы рассмотрим, называется dizzy (с английского — головокружительный). Смысл эффекта заключается в отображении анимированных спирально-закрученных колец.

nRA0KF7.jpeg

 

 

Алгоритм для вершинного шейдера:
1) Применяем видовую, мировую и проекционную матрицы на позицию вершины.
2) Сохраняем в текстурные координаты (TEXCOORD0) позицию нетрансформированной вершины для фрагментного шейдера. Т.е. входящими данными у нас будут: POSITION, а исходящими - TEXCOORD0.

Алгоритм для фрагментного шейдера:
1) Получаем интерполированные данные (TEXCOORD0 – т.е. наши текстурные координаты).
2) Вычисляем арктангенс между текстурными координатами x,y.
3) Вычисляем радиус между текстурными координатами. По уравнению окружности:
R2=(x-a)2+(y-b)2. Для удобства примем a = 0, b = 0.
4) Возвращаем цвет, по общей формуле sin(угол + радиус). Т.е. некое смещение, которое из-за функции синуса будет закручивать кольца.

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

При подстановке этих параметров конечная формула примет вид:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

Вот фрагментный шейдер этого эффекта:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

И вершинный шейдер:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

В качестве более трудных примеров рассмотрим модели освещения.

Модели освещения.

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

Рассмотрим все подробнее, создадим простую модель освещения — равномерное освещение.

1. Равномерное освещение.

Равномерное освещение (ambient lighting) обеспечивает постоянное начальное освещение для всей сцены. Оно освещает все вершины объектов одинаково, потому что не зависит ни от каких других факторов освещения. Это самый простой и быстрый тип освещения, но при этом дает наименее реалистичный результат. Формула для вычисления этой модели освещения так же очень проста, т.к. там всего одна арифметическая операция — умножение. Для ее вычисления достаточно перемножить цвет материала на интенсивность освещения.

Iambient=ka×Ia

Вершинный шейдер для расчета равномерной модели освещения:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

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

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Здесь тоже нет ничего сложного, просто возвращаем компилятору произведение вектора, на какое то число в данном случае ambient_intensity. Делает он это так:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

Модель равномерного освещения:

Yaea4g8.jpeg

Для того чтобы вычислить бликовый и диффузный компоненты света, необходимо найти три вектора:
• Нормаль N к фрагменту.
• Видовой вектор V — вектор, который направлен на наблюдателя.
• Позицию источника света L.

RuiIeIW.jpeg

Углы между этими векторами составляют интенсивность освещения.

 

2. Диффузная модель освещения.

Диффузная модель освещения (diffuse lighting model) — модель освещения, которая зависит от положения источника освещения и от объектной нормали поверхности. Поскольку излучение света одинаково во всех направлениях, видовой вектор не имеет значения, т.е. v=0. Такой метод требует большего вычисления, так как изменяется для каждой вершины объекта, однако неплохо затеняет объекты и придает им объем. Свет падает, не заполняя всю поверхность одинаковым цветом (как в случае с раномерным освещением), а создается впечатление, что, свет направлен на какую либо поверхность.

yBRbcNt.jpeg

Если вектор позиции источника освещения перпендикулярен поверхности, то никакой матовости не будет наблюдаться, потому что интенсивность света зависит от угла α. Для расчета диффузной модели освещения используется формула (по закону Ламберта):

Idiffuse=kd×Id×(NL)

Подведем итоги – создадим шейдеры для расчета освещения.

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

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

И пиксельный шейдер:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Диффузная модель освещения:

k3LKBA8.jpeg

3.Бликовая модель освещения.

Сложно, представить такую модель освещения не увидев ее. А на самом деле эту модель мы можем увидеть почти везде. Например, чисто отполированную (круговыми движениями) прямую металлическую поверхность, направив на нее источник света, и посмотрев под неким углом, который не перпендикулярен поверхности. В результате мы увидим блики на поверхности, которые существенно увеличивают реалистичность изображения. Эти блики являются отражением источника света от поверхности. В этой модели освещения помимо векторов позиции источника освещения и нормали (как в случае с диффузной моделью освещения) используются еще два вектора: видовой вектор и вектор отражения. Бликовую модель освещения (specular lighting model) предложил Буи-Туонг Фонг.

89J4iVH.jpeg

Угол между видовым вектором и вектором отражения – β. Чем больше угол β, тем ярче бликовое освещение. Поэтому бликовая модель освещения вычисляется по следующей формуле:

Ispecular=ks×Is×(VR)n

где R=reflect (–norm(V), N)
n — коэффициент яркости свечения.

Зависимость яркости свечения от угла β:

e2nKxmG.jpeg

С ростом параметра n отражение становиться все более бликовым и все более концентрируется вдоль направления вектора отражения R.

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

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Необязательно вычислять видовой вектор в шейдере, можно вычислить в программе и занести в вершинный шейдер. Для этого нужно инвертировать видовую матрицу и умножить на вектор D3DXVECTOR4(0.0,0.0,0.0,1.0) — позиция при которой вектор перпендикулярен поверхности (смотрит на нас). Выглядит это так:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

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

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Бликовая модель освещения:

sPRf45M.jpeg

Модификация бликового освещения по Блинну.

Джим Блинн придумал альтернативный способ вычисления бликового освещения, который устраняет дорогие вычисления над вектором отражения. Он ввел промежуточный вектор, который является средним значением между видовым вектором и вектором позиции источника освещения: H=(L+V)/(|L+V|)

Общая формула имеет вид:

Iblin_specular=kb_s×Ib_s×(NH)n

Поэтому фрагментный шейдер уже будет такой:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Ускорение вычисления яркости свечения.

Шлик предложил замену степени n. Пусть скалярное произведение равно D: D=(NH)n, тогда по его способу яркость свечения будет вычисляться следующим образом:
Изображение

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

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Сравнительные графики степенных законов:

PWW3WlA.jpeg

На самом деле физический смысл бликового отражения света намного сложнее, чем предполагается в модели освещения Фонга. В более реалистичной модели Is зависит от длины волны l и от угла падения света (NL). Такая зависимость называется коэффициентом Френеля.

Комбинирование компонентов освещения.

Теперь мы можем сложить три модели освещения (постоянное, диффузное и бликовое), чтобы получить суммарное количество света I, получаемое глазом:
Изображение

Пиксельный шейдер теперь будет такой:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Реалистичное освещение на основе Кука-Торренса

В более реальных моделях освещения основное внимание уделяется на  распределение энергии падающего света. Часть ее поглощается материалом и превращается в тепло, другая часть рассеивается в виде диффузного света, третья часть задает поверхности бликовую освещенность. Поэтому для различных материалов разделение падающего света происходит по-разному, и зависит оно от:
• Функции распределения нормалей
• Затенения и экранирования
• Коэффициента Френеля

Функция распределение нормалей

Эта функция описывает возможное отклонение нормали к поверхности от идеальной нормали N. Чем более эта функция пологая, тем большие отклонения допустимы и тем большей величины пятно отраженного блика. Необходимые нормали расположены вдоль вектора L+V и видимы в направлении V и находятся под углом (HN) к зрителю. Кук и Торренс использовали формулу распределения  Бекмана:

Изображение

где d=HN
m – степень шероховатости объекта. 0.2f – гладкая поверхность, 0.6f – шероховатая. По умолчанию ставят 0.3f.

График распределения Бекмана:

lphyZrh.jpeg

Затенение и экранирование

В модели Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.  учитываются также и такие эффекты, как затенение (shadowing) и экранирование (masking), которые определяют интенсивность бликовой составляющей.

Неэкранированный свет равен:
Изображение

Незатененный свет равен:
Изображение

Тогда финальный множитель G равен:

G=min(1, Gm, Gs)

3pP1uq9.jpeg

 

Коэффициент Френеля

Этот коэффициент определяет долю отраженного света и задается функцией:

Изображение

где f — угол падения, косинус, которого равен c=(NH)
n — показатель преломления материала, g=sqrt(n2+c2–1)

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

F=Rs+(1–Rs)×(1–EN)n
где Rs — бликовое отражение,E — вектор наблюдателя,N — нормаль из карты нормалей.

Такая аппроксимация не учитывает соответственно f и n компоненты, но имеет степень n, с увеличением которой можно добиться не плохих результатов. Шлик использовал n=5.

Комбинирование всех множителей

Торренс и Спэрроу объединили эти множители и вывели формулу для подсчета бликового света:
Изображение

Знаменатель NV введен для регулирования интенсивности света.

MDbPCOW.jpeg

Общая формула для расчета количества света такая:
Изображение

Пришлось формулу немного упростить, так как в оригинале каждый компонент освещения (кроме бликового) умножается еще и на F(0, n). Сделано это из-за использования аппроксимации Шлика.

Пиксельный шейдер этого эффекта:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

И вершинный шейдер:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Specular Bump Mapping.

Ну конечно, без этого никак :), хотелось бы рассказать про этот эффект…

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

5HoJlwr.jpeg

Для реализации этого эффекта нам понадобиться две текстуры: base map и normal map, base map — это diffuse текстура, а normal map текстуру нам придется делать самим (почти самим). Для этого нужно скачать plug-in к Photoshop (На сайте nVidia есть этот plug-in), или любую другую программу которая конвертирует из height map в dot3. Или же воспользоваться программой normal mapper, которая находиться в Radeon DX SDK. Суть bump mapping’a состоит в том, чтобы сделать иллюзию выпуклости какой либо поверхности. Для bump mapping’а понадобиться tangent space. Вместо normal’и полигона мы будем брать normal map (dot3 текстуру), а tangent и binormal нам понадобятся для расчета освещения. Еще одна трудность правильного bump mapping’a (specular bump mapping’a) состоит в том, что нужно рассчитывать tangent space (в математической литературе привычно называть tangent space касательное пространство). В этой системе координат normal будет осью z, binormal — y, и tangent — x.

Так выглядит tangent space для вершины полигона:

3p0UfyO.jpeg

Для расчета нормалей и tangent в D3DX есть следующие функции:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Так что давайте сначала сосчитаем normal и tangent:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Шейдеры…

vertex shader

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

fragment shader

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Теперь о переменных в шейдерах…
m_matMVP  – ModelViewProjection matrix
m_matWorld  – World matrix
m_vLightPos – Light position
m_vViewPos  – View position
m_vAmbient  – Ambient color
m_vDiffuse  – Diffuse color
m_vSpec    - Specular color
m_vHelper.x - Ambient intensity
m_vHelper.y – Diffuse intensity
m_vHelper.z – Specular intensity
m_vHelper.w – Specular exponent

Для работы примера достаточно установить все константы, например, так:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Wood Shader.

Эффект заключается в генерировании древесной поверхности объекта. Вот так выглядит wood shader:

mOChOZb.jpeg

 

wood shader без освещения и с освещением.

Для такого эффекта нам понадобится текстура, обработанная noise фильтром. Пример такой текстуры:

J4xlf2t.jpeg

Древесная поверхность состоит из колец, оси которых постоянно меняются и как бы скачут взад-вперед между полосами. Такой эффект легко можно задать функцией остатка: Rings(R)=frac(R),
где R – радиус колец, который вычисляется между текстурными координатами x и y.

Алгоритм для фрагментного шейдера:

1)Рассчитываем вектор искривления линии (Wooble или Skew), который равен суме текстурных координат и цвета из noise текстуры.
float3 shade = pos + (tex2D(Noise,pos));
Т.е. примерно получится что-то такое: 2IF3ECf.jpeg

2)Вычисляем длину вектора искривления линии:
float dist = length (shade);
В эту часть алгоритма также можно добавить некий параметр num, который задает количество линий: float dist = length(shade)*num;

3)Полученное значение dist осталось занести в основную функцию древесного шейдера: float main_color = frac(dist); И для придания натуральности древесной поверхности помножим main_color на коричневый цвет:
return main_color*float4(0.5,0.3,0.01,0);

Все бы хорошо, но без освещения такой эффект не смотрится. Добавим диффузионную модель освещения. Для этого помножим результирующий свет на формулу расчета диффузной освещенности:
return main_color*float4(0.5,0.3,0.01,0)*((diffuse_color * diffuse_intensity) *dot(normal, lt));

Фрагментный шейдер:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Алгоритм вершинного шейдера составляет точную копию алгоритма вершинного шейдера dizzy эффекта.
Вершинный шейдер:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Оптимизация кода.

1. Не изобретайте колесо! Используйте intrinsic функции. Например:

Неоптимизированный код:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

на выходе:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Используя dot3 вместо DOT3, на выходе получите такое:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

 

2. Всячески избегайте привидения типов и пытайтесь всегда выбрать необходимый тип. Например:

Неоптимизированный код:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

на выходе:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Оптимизированный код:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

на выходе:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

3. Объединяйте скаляры в вектора. Таким образом, можно уменьшить число констант в вашем шейдере. Например:

Неоптимизированный код:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

на выходе:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Оптимизированный код:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

на выходе:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

4. Для ветвлений используйте тип bool как static. Например:

Неоптимизированный код:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

на выходе:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Объявление переменной b как static намного оптимизирует выходной код:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

5. Векторизируйте типы. Например:

Неоптимизированный код:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

на выходе:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Оптимизированный код:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

на выходе:

Войдите или зарегистрируйтесь, чтобы увидеть скрытое содержимое.

Завершение.

Несколько советов:

Рекомендуется скачать Render Monkey™ с сайта ATi Technologies®.
Программу Shader Works от Mad Software.
Подобную программу от NVIDIA® — FXComposer™.
Посетить сайт ShaderX.

P.S. По поводу API, каждый должен понимать, что все подобные эффекты можно сделать и на OpenGL, применяя при этом или GL_VERTEX/FRAGMENT PROGRAM либо на новом высокоуровневом языке GLSL или Cg.

 

Изменено пользователем kuzmich774

Присоединиться к обсуждению

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

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Only 75 emoji are allowed.

×   Ваша ссылка автоматически преображена.   Отображать как простую ссылку

×   Предыдущее содержимое было восстановлено..   Очистить текст в редакторе

×   Вы не можете вставлять картинки напрямую. Загрузите или вставьте их через URL.

  • Ответы 0
  • Создано
  • Последний ответ
  • Просмотры 142

Лучшие авторы в этой теме

  • kuzmich774

    1

Популярные дни

Лучшие авторы в этой теме

Популярные дни

  • Сейчас на странице   0 пользователей

    • Нет пользователей, просматривающих эту страницу
  • Модераторы онлайн

    • СУЛАРИУС
    • alexis
×
×
  • Создать...