Главная Статьи Ссылки Скачать Скриншоты Юмор Почитать Tools Проекты Обо мне Гостевая Форум |
Одним из очень красивых эффектов в Doom 3 является "сгорание" убитого противника - он сгорает как сигарета.
Посмотрев на этот эффект я понял, что довольно легко реализуется при помощи помощи фрагментных программ, не изменяя при этом геометрию самого объекта.
Рассмотрим простейший вариант реализации данного эффекта.
Для начала сопоставим каждой вершине исходного объекта кроме стандартных параметров дополнительное скалярное значение, отвечающие за распространение огня вдоль объекта.
Также введем аналогичный параметр для самой фрагментной программы. При помощи этого параметра мы будем управлять распространением огня по поверхности объекта.
В результате этого каждой точке растрового представления объекта можно сопоставить величину b, являющуюся разностью проинтерполированного вдоль грани значения и параметра самой программы.
Задачей фрагментной программы будет при b<0 возвращать исходный цвет (например цвет текстуры, соответствующей данному фрагменту). При b>1 фрагмент должен просто отбрасывать, а отрезку [0,1] сопоставляются различные фазы процесса сгорания.
За счет изменения параметра фрагментной программы мы будет перемещать фронт огня по поверхности объекта.
Однако здесь мы сталкиваемся с тем фактом, что в языке фрагментных программ практически полностью отсутствуют условные операции.
Единственными операциями, которые дают возможность программисту использовать условия являются KIL, CMP, MAX, MIN, SGE и SLT.
Команде KIL позволяет легко отбросить те фрагменты, для которых b>1:
ADD b2, one, -b; # break if b > 1 KIL b2.x;
Все остальное ветвление мы будем реализовывать при помощи команд SLT и LRP.
Команда SLT позволяет нам определить нужно ли использовать нормальную текстуру или же цвет сгорания. Для этого достаточно произвести линейную интерполяцию между цветом огня, соответствующему текущему значению bи цветом текстуры. В качестве параметра интерполяция надо использовать значение, возвращаемое командой SLT:
SLT tf.xyz, b, 0; # now tf.x = 1 if burn < 0, otherwise 0 LRP result.color.rgb, tf, color, cf; # choose either color or cf
Для получение цвета, соответствующего процессу сгорания удобно также воспользоваться командой LRP:
LRP cf, b, brightYellow, darkRed; # get burn color
В результате мы приходим к следующей фрагментной программе:
!!ARBfp1.0 # # simple burn fragment shader # on entry: # fragment.texcoord [0] - texture coordinates # # texture [0] - decal map # # program.local [0].x - burn coordinate # ATTRIB t = fragment.texcoord [1]; PARAM burn = program.local [0]; PARAM one = 1; PARAM two = 2; PARAM darkRed = { 0.5, 0, 0, 1 }; PARAM brightYellow = { 1, 1, 0, 1 }; TEMP b, b2, tf, cf, dots, color, h2, l2, temp, diffuse, dist, atten; ADD b, t, -burn; MOV b.y, b.x; MOV b.z, b.x; ADD b2, one, -b; # break if b > 1 KIL b2.x; TEX color, fragment.texcoord [0], texture [0], 2D; # get decal color SLT tf.xyz, b, 0; # now tf.x = 1 if burn < 0, otherwise 0 LRP cf, b, brightYellow, darkRed; # get burn color LRP result.color.rgb, tf, color, cf; MOV result.color.a, 1; END
Однако непосредственное применение данной программы приводит к слишком ровной границе процесса горения, что не всегда красиво выглядит.
Рис 1. Эффект сгорания, полученный при помощи приведенной выше программы.
Простейшим путем сделать границу сгорания неровной является добавление к величине tнекоторого шумового значения. Проще всего это значение задать при помощи двухмерной текстуры, подобной той, что используется для имитации микрорельефа (микрофактурной текстурой, текстурой детализации).
Соответствующая фрагментная программа приводится ниже, в ней шумовая текстура передается в первом текстурном блоке.
!!ARBfp1.0 # # simple burn fragment shader # on entry: # fragment.texcoord [0] - texture coordinates # # texture [0] - decal map # # program.local [0].x - burn coordinate # ATTRIB t = fragment.texcoord [1]; PARAM burn = program.local [0]; PARAM one = 1; PARAM two = 2; PARAM darkRed = { 0.5, 0, 0, 1 }; PARAM brightYellow = { 1, 1, 0, 1 }; TEMP offs, b, b2, tf, cf, dots, color, h2, l2, temp, diffuse, dist, atten, mask; # get burn noise MUL temp, fragment.texcoord [0], 30; TEX offs, temp, texture [1], 2D; MAD offs, offs, 3, -1.5; # apply it ADD b, t, -burn; MOV b.y, b.x; MOV b.z, b.x; ADD b, b, offs; # bias threshold with noise ADD b2, one, -b; # break if b > 1 KIL b2.x; # get decal color TEX color, fragment.texcoord [0], texture [0], 2D; SLT tf.xyz, b, 0; # now tf.x = 1 if burn < 0, otherwise 0 LRP cf, b, brightYellow, darkRed; # get burn color LRP result.color.rgb, tf, color, cf; MOV result.color.a, 1; END
Ниже приводятся изображения, полученные при помощи данной программы:
По этой ссылке можно скачать весь исходный код к этой статье. Также доступны для скачивания откомпилированные версии для M$ Windows, Linux и Mac OS X.