www.demoscene.ruenglish version
новостимузыкадемографикаинформацияпрограммыфорум‘подкаст’
 
авторы    статьи    интервью   

Music for games, video game music
Enlight project

Хоровод историй

Лаборатория альтернативной истории

статьи
Radiosity. Алгоритм глобального освещения
 

Создание генератора карт освещённости. Часть 1.

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

Скачать пример на Delphi с исходниками можно ниже (в примере для вывода и создания radiosity lightmaps используется OpenGL).


Посчитаем свет в...

Допустим, что мы хотим посчитать Radiosity-освещение внутри куба. Будем считать, что это такая комната. Простая комната в форме куба: без окон, без дверей. Комната абсолютно пустая.

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


Текстуры.

Чтобы добавить реалистичности нашей комнате, добавим немного текстур. В качестве текстур может быть что угодно: дерево, кирпич, стекло, бетон, обои, металл и т.д. Для простоты сделаем текстуры пола, потолка и стен одинаковыми.


Карты освещённости.

Второй шаг на пути к реалистичному освещению - это карты освещённости (lightmaps). Карты освещённости представляют собой обычные текстуры. В них мы и будем хранить рассчитанный по Radiosity свет.

Таким образом, у нас есть два набора текстур: просто текстуры (кирпич, бетон, дерево) и текстуры lightmap (световые пятна и плавные переходы). Lightmap "накладывается" поверх основной текстуры


Заглянем внутрь комнаты.

Итак, мы подготовили 3D модель нашей комнаты. Выберем размеры текстур lightmap. Для каждой грани куба у нас будет lightmap размером 10 на 10 пикселей:

- пол и потолок: два lightmap-а 10x10;
- стены: четыре lightmap-а 10x10.

Получается, что всего 7 текстур: текстура кирпича и 6 текстур lightmap-ов. Пока lightmap-ы заполнены чёрным цветом. Чем выше разрешение карт освещённости, тем выше качество света.


Маленькие квадратики.

Представим каждый пиксель lightmap-а квадратиком. Чтобы показать это, мысленно разделим пол, стены и потолок сеткой на квадратные элементы. Квадратные элементы называют "патчами" (patch). На рисунке выше зелёным цветом выделен один patch. Lightmap этой стены имеет размеры 10x10. Чтобы закрасить patch зелёным цветом, мы изменили содержимое одного пикселя текстуры lightmap-а (stena[1].data[1,1].green:=255). Это сделано для иллюстрации, поэтому потом сделаем обратно stena[1].data[1,1].green:=0.


Добавим источник света.

В этом упрощенном методе для создания источника света достаточно изменить содержимое любого lightmap-а. Создадим на стене красный источник света. Если хранить карты освещённости пола, стен и потолка в разных текстурах, то они будут выглядеть так:

- текстура lightmap стены с красным источником света.
, , , , - текстуры карт освещённости осталных стен, пола и потолка.

По сути мы встроили в стену красную лампу размером 4x3.


Помещаем камеру в центр патчей.

Рассмотрим упрощенную реализацию метода. Здесь мы применим интересный трюк: попробуем взглянуть на мир с точки зрения patch-а. Цвет патча изменим в зависимости от того, что мы увидим. Эту операцию мы проведём для каждого патча в нашей комнате:


Что видят патчи в реальности.

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

и т.д.. Для каждого патча мы как бы делаем "скриншот" с его точки зрения.


Как сделать lightmap: получение цвета патча.

Цвет патча получается из картинки, которую "видит" патч. Каждому патчу соответствует пиксель в текстуре lightmap. Как же определить цвет, который нужно занести в lightmap?

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


Упрощенный FormFactor.

Формфактор в этом методе реализован весьма просто: в виде пятна:

То, что находится в центре "видения" pathc-а имеет максимальную яркость. А "боковое зрение" patch-а существенно затемняется. Примеры наложения формфактора:

Приведено "видение" патчей, выделенных синим цветом (см. выше). Смешивание цветов происходит так же, как и с lightmap-ами.


Итеративность алгоритма.

Определение освещения в сцене производится в несколько проходов. После первого прохода, который описан выше, многие patch-и в комнате перестанут быть чёрными. Любой patch, цвет которого будет отличен от нуля, становится источником света.

Посмотрите на рисунок выше. По диагонали показано несколько патчей и то, что они "видят". Свет, которые излучают эти патчи, можно будет учесть только во втором проходе. Их "увидят" другие patch-и.


Как всё это работает.

Рассмотрим принцип действия алгоритма. В основе всего лежит функция xrender(v_eye,v_dir,v_up:Tvector):TRGB. На входе в фунцию xrender вы подаёте:

- координату патча (v_eye.x, v_eye.y, v_eye.z);
- направление взгляда из патча (v_dir.x, v_dir.y, v_dir.z);
- и ещё один вектор (v_up.x, v_up.y, v_up.z), который вносит в определённость положения камеры.

Эта функция производит отрисовку сцены в маленькое невидимое окошечко. С этого окошечка делается "скриншот". Из этого скриншота WxH пикслелей (W - ширина, H - высота) создаётся пиксель, значение цвета которого вы получаете на выходе функции:
- значение цвета пикселя (R,G,B).


Как посмотреть из патча.

1) взять координату текстуры lightmap-а U,V;
2) воспользоваться формулами;
3) из формул получится положение этой точки в пространстве (X,Y,Z);


Формулы перехода от U,V к X,Y,Z.

Данные формулы расположены в процедуре RadiosityStep. Принцип перехода от текстурных координат U,V к пространственным X,Y,Z весьма прост. Для прямоугольных полигонов с вершинами в точках P1, P2, P3, P4 переход осуществляется по формуле (1):

V = P1+A*DU+B*DV, где
V - вектор в пространстве (X,Y,Z);
P1 - точка в пространстве, соответствующая текстурной координате (U,V)=(0,0);
A - вектор в пространстве, соответствующий текстурному направлению по U;
B - вектор в пространстве, соответствующий текстурному направлению по V;
DU, DV - скалярные шаги по направлениям U и V.

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

A = P2 - P1;
B = P4 - P1;

DU = длина(A) / Umax;
DV = длина(B) / Vmax;

где Umax и Vmax - размеры текстуры lightmap например, Umax=10, Vmax=10);

По формуле (1) мы окажемся в узлах сетки (между патчами). Чтобы положение камеры оказалось в центре патчей, необходимо сместиться на половину размера патча по U и по V:

V = V + A * (DU/2) + B * (DV/2);


Картинки итераций работы алгоритма.

Приведём несколько примеров из более сложной сцены, где присутствует перегородка. Обратите внимание, что здесь не используются текстуры стен, а есть только текстуры lightmap-ов. Поэтому исходные цвета источника света lightmap-ов могут быть полностью "засвечены" переотраженным светом.


Связь между patch-ами и пикселями lightmap-ов.

Patch - это точка текстуры lightmap-а, растянутая на квадратик. Пусть у вас все lightmap-ы имеют размер 10x10. Рассмотрим полигон размером 578x578. Тогда размер квадратика (patch) будет равен 57.8x57.8. Несложно проверить, что 10 патчей, соответствующие одной строке текстуры lightmap-а, как раз займут длину 57.8 * 10 = 578. Таким образом, patch - это пиксель lightmap, имеющий площадь. По-другому можно сказать так: patch - это пиксель lightmap, растянутый на квадратик.


Последовательность действий.

Для всех текстур lightmap в нашей сцене выполняются следующие действия:

1) Берём пиксель lightmap, который хотим узнать.
2) Помещаем в patch этого пикселя камеру.
3) Рендерим всю сцену и получаем "скриншот".
4) Скриншот у нас большой (например, 32x32), а его нужно уместить в одном!!! пикселе.
5) Чтобы уместить картинку "видения" patch-а, мы подвергаем его преобразованию FormFactor и уменьшаем размеры с 32x32 до 1x1 (подсчитываем среднее значение).

После того, как мы пройдёмся по всем патчам, будет закончен первый проход. Но одного прохода недостаточно!!! (смотри картинки ниже). Необходимо делать несколько проходов, так как именно за счёт многопроходности и достигается эффект Radiosity. Всё больше и больше patch-ей получают какой-либо цвет. Благодаря этому свет проникает даже за препятсвия и получаются мягкие тени (смотри картинки ниже: от светящейся стены свет попадает даже за перегородку!).


Пример Cornell Box

Выше был описан самый простой метод создания radiosity карт освещённости. Скриншоты из программы с различным положением источника света:


Источник света находится на потолке.


Источник света расположен на полу.

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


Окно программы.


Меню программы.

С помощью меню вы можете выбрать положение лампы. Так как вначале все lightmap заполнены чёрным цветом, то в окне видна только лампа. После нажатия на кнопку "create lightmaps" необходимо подождать какое-то время, пока пройдут все этапы алгоритма opengl radiosity (Статья "Radiosity и Photon mapping с использованием OpenGL"). Если в директории вместе с программой имеются заранее расчитанные lightmap, то можно быстро загрузить их с помощью кнопки "load lightmap".

В этой версии используется два набора текстур: основные и lightmap. Цвета источников света можно задавать как предварительным заданием lightmap, так и изменением основных текстур.

Скачать программу можно здесь: glrnew.zip - 185kb.


страница 2 


интро   демо   flash   анимация   3D-графика   арт   синглы   альбомы   статьи   трекеры

Дизайн и программирование: Александр Ильин aka Real/SandS     
Шеф-редактор: Антон Уткин aka Frown/Rcd     
Креатив и интерфейс: Александр Мачуговский aka Manwe/SandS