Elite Games - Свобода среди звезд!

Уроки для программистов - Bump mapping

BUMP MAPPING



Итак, что это такое?

Скорее всего, о bump mapping`е вы слышали. Он позволяет улучшить качество картинки, создавая иллюзию того, что на объекте находится множество мелких неровностей/царапин и т.п.

Этот эффект может быть выполнен несколькими способами, но в современных играх используются несколько особенных. Bump mapping, который есть в 3д пакетах использует карту высот (серая текстура), это не в последнюю очередь сделано для удобства пользователя…

Но с real-time техникой чуть по-другому.

NORMAL MAPPING

Вообще 3D API, такие как OpenGL и DirectX, используют при расчете освещения нормали (вектора перпендикулярные полигонам) для каждой вершины. Идея normal mapping`а заключается в том, чтобы задавать эти нормали не только для каждой вершины, но и для каждого пикселя.



Нормали кодируют в цвет и выходит normal map – текстура ядовито-фиолетового цвета. Рассчитать освещение попиксельно позволяют пиксельные шейдеры.

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

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

На вход идет:
- позиция камеры в локальной системе координат модели, которая рисуется
- позиция источника света в локальной системе координат модели, которая рисуется


1)Вычислить вектор источника света
2)Нормализировать его
3)Трансформировать вектор источника света в пространство касательных.
4)Вычислить вектор камеры
5)Нормализировать его
6)Просчитать H вектор (смотрите модели освещения Blinn`а; H — half-angle vector)
7)Трансформировать H вектор в пространство касательных.


На выход идет:
- вектор источника света в пространстве касательных
- H вектор в пространстве касательных

Алгоритм для пиксельного шейдера:

На вход идет:
- вектор источника света в пространстве касательных
- H вектор в пространстве касательных

- Color — цвет

- Ambient, который считается по формуле: Ambient = li.Ambient * ma.Ambient + li.Ambient * ma.WordlAmbient (ma – текущий материал, li – текущий источник света)

- Diffuse, который считается по формуле: Diffuse= li.Diffuse*ma.Diffuse

- Specular, который считается по формуле: Specular = li. Specular *ma. Specular

- Shininess материала (интенсивность Specular)


ВНИМАНИЕ! Это для модели освещения, которая принята в OpenGL.




1) Нормализировать вектор источника света в пространстве касательных
2) Нормализировать Н вектор в пространстве касательных



ПРИМЕЧАНИЕ. Да, да, я знаю Н вектор и вектор источника света мы уже нормализировали в вершинном шейдере.. но в пиксельном шейдере идет уже не тоот вектор – он интерполируется по полигону для каждого пикселя, так что нормализировать его нужно.



3) Прочитать из normal текстуры и превести к норме по формуле перевода цвета в вектор
4) Просчитать dot product (скалярное произведение) вектора нормали из normal текстуры и вектора источника света в пространстве касательных
5) Просчитать dot product (скалярное произведение) вектора нормали из normal текстуры и Н вектора в пространстве касательных
6) Просчитать цвет с помощью diffuse (входного параметра)
7) Прочитать цвет из Diffuse текстуры
8) Просчитать Specular с учетом Shininess
9) Просчитать финальный результат diffuse * specular в границах [0..1]


РЕАЛИЗАЦИЯ НА OPENGL

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


!!ARBvp1.0
OPTION ARB_position_invariant; говорим, что изменять вершины не будем

PARAM light = program.local[0];
PARAM eye = program.local[1];
PARAM radius = program.local[2];
PARAM half = { 0.5, 0.5, 0.5, 0.5 };

ATTRIB normal = vertex.normal;
ATTRIB tangent = vertex.texcoord[1];
ATTRIB binormal = vertex.texcoord[2];

TEMP L, H, V, i;

OUTPUT Tdecal = result.texcoord[0];
OUTPUT Lt = result.texcoord[1];
OUTPUT Ht = result.texcoord[2];

1)Вычислить вектор источника света
ADD L, light, -vertex.position;
2)Нормализировать его
DP3 i, L, L;
RSQ i.x, i.x;
MUL L, L, i.x;
MOV L.w, 1.0;


3)Трансформировать вектор источника света в пространство касательных.
DP3 Lt.x, L, tangent;
DP3 Lt.y, L, binormal;
DP3 Lt.z, L, normal;
MOV Lt.w, 1.0;

4)Вычислить вектор камеры
ADD V, eye, -vertex.position;

5)Нормализировать его
DP3 i, V, V;
RSQ i.x, i.x;
MUL V, V, i.x;
MOV V.w, 1.0;


6)Просчитать H вектор (смотрите модели освещения Blinn`а; H — half-angle vector)
ADD H, L, V; то есть H=(L+V)*0.5
MUL H, H, half;

7)Трансформировать H вектор в пространство касательных.
DP3 Ht.x, H, tangent;
DP3 Ht.y, H, binormal;
DP3 Ht.z, H, normal;
MOV Ht.w, 1.0;

MOV Tdecal, vertex.texcoord[0];

END



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


!!ARBfp1.0

PARAM color = program.local[0];
PARAM exponent = program.local[1];
PARAM diff = program.local[2];
PARAM spec = program.local[3];
PARAM values = { 0.5, 1, 2, 8 };

ATTRIB Tdecal = fragment.texcoord[0];
ATTRIB Lt = fragment.texcoord[1];
ATTRIB Ht = fragment.texcoord[2];

TEMP tmp, N, Ln, Hn, diffuse, specular, NdotL, NdotH, decal;



1) Нормализировать вектор источника света в пространстве касательных
DP3 tmp, Lt, Lt;
RSQ tmp.x, tmp.x;
MUL Ln, Lt, tmp.x;

2) Нормализировать Н вектор в пространстве касательных
DP3 tmp, Ht, Ht;
RSQ tmp.x, tmp.x;
MUL Hn, Ht, tmp.x;

3) Прочитать из normal текстуры и превести к норме по формуле перевода цвета в вектор
TEX N, Tdecal, texture[1], 2D;
MAD N, N, values.zzzz, -values.yyyy;

4) Просчитать dot product (скалярное произведение) вектора нормали из normal текстуры и вектора источника света в пространстве касательных
DP3_SAT NdotL, N, Ln;
5) Просчитать dot product (скалярное произведение) вектора нормали из normal текстуры и Н вектора в пространстве касательных
DP3_SAT NdotH, N, Hn;

6) Просчитать цвет с помощью diffuse (входного параметра)
ADD_SAT NdotL, NdotL, diff;

7) Прочитать цвет из Diffuse текстуры
TEX decal, Tdecal, texture[0], 2D;
MUL_SAT diffuse, NdotL, decal;
MUL_SAT diffuse, diffuse, color;

8) Просчитать Specular с учетом Shininess
POW specular.x, NdotH.x, exponent.x;
MUL_SAT specular, specular.xxxx, spec;

9) Просчитать финальный результат diffuse * specular в границах [0..1]
ADD_SAT result.color, diffuse,specular;
END


Существует несколько модификаций классического normal mapping`а:
- gloss mapping – когда specular читается из gloss текстуры. (регулирует степень отражения, очень полезно для имитации ржавчины)
- parallax mapping – перекрывающийся бамп (Oblivion, Stalker). Добавляется карта высот и по ней делается зависимая выборка
- horizont mapping – самозатеняющийся бамп
- POM – самый красивый и самый тяжелый вариант. Подробности тут:
www.ati.com/developer/i3d2006/I3D2006-Tatarchuk-POM.pdf

Материалы и утилиты по данному вопросу:
http://www.delphi3d.net/articles/viewarticle.php?article=bumpmapping.htm
http://www.delphi3d.net/articles/viewarticle.php?article=phong.htm
NVMeshMender в DXSDK или тут: http://download.developer.nvidia.com/developer/SDK/Individual_Samples/samples.html#MeshMenderSimpleExample
Jurec
К началу раздела | Наверх страницы Сообщить об ошибке
Уроки для программистов - Bump mapping
Все документы раздела: Для тех, кто хочет писать игры | Движок на OpenGL | Создание игр в Game Maker | Bump mapping | Использование Direct Input | XNA framework |


Дизайн Elite Games V5 beta.18
EGM Elite Games Manager v5.17 02.05.2010