Особенности моделирования света: Аппроксимации Фонга (Phong Shading)


Аппроксимации Фонга, или затенение по Фонгу (Phong shading), носят название по имени своего изобретателя — Ву Тонг Фонга (Wu Tong Phong). Этот метод дает более точные результаты при затенении полигонов по сравнению с затенением Гуро, рассмотренным выше. Суть этих аппроксимаций заключается в определении нормали к поверхности в каждой вершине полигона и дальнейшей интерполяции вектора по всему полигону поверхности. Далее для каждого пикселя необходимо вычислить значение яркости, основываясь на значениях вектора нормали.

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

Затенение по Фонгу — это достаточно популярный метод для реализации затенения, особенно в программах, применяющих non-realtime rendering, например, 3D Studio. Однако, несмотря на то, что этот метод значительно точнее затенения Гуро, он по-прежнему не является физически точным.

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

Просчет затенения по Фонгу включает в себя:

  • вычисление двух квадратных корней
  • реализацию двух операций деления
  • четырех операций умножения

и это не считая значительного количества операций сложения на каждый пиксел!!! Более того, они не являются целочисленными. Сравните это с единственной операцией сложения на пиксель при затенении Гуро и вы поймете, почему этот метод столь медленный.

Рассмотрим подробнее

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

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

Итак, мы имеем полигон. В каждой вершине полигона мы имеем вектор нормали к поверхности, частью которой полигон является (v1, v2, v3). Эти векторы интерполированы по всей площади полигона, подобно тому, как это делалось для затенения Гуро. В затенении Гуро, однако, мы имели дело только с одним значением -shade (это скалярная величина). Здесь же мы оперируем вектором с тремя значениями Vx, Vy и Vz (три значения, три координаты — определяющие положение вектора в пространстве).

Итак, возьмем пиксель на этом полигоне, красная точка (см. рисунок). Вектор нормали, соответствующий этому пикселю на поверхности — n. Свет падает на полигон вдоль вектора l. Количество света, отраженного от данного пикселя, является функцией, называемой в английском dot product.

Dot product — это скалярное произведение векторов, дающее в результате число, определяющее длину (величину) результирующего вектора. Если мы хотим получить в качестве результата тоже вектор, то это уже будет cross product. Но здесь мы его не рассматриваем. Скалярное произведение вектора n(x,y,z) и вектора l(x,y,z) может производиться по любой из двух формул:

  • n*l = nxlx + nyly + nzlz (1)
  • n*l = |n|*|l| cosq (2), где q (тетта) — угол между векторами.

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

Все просто.

Далее результатом вычисления данной функции будет значение в диапазоне от -1 до 1, где 1 соответствует максимальной яркости. Понятия отрицательного света не существует, поэтому значения меньше 0 следует понимать как 0. Если вы затем умножите данное значение на величину яркости света (brightness), вы получите значение яркости нашего пикселя.

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

Быстрое затенение по Фонгу (Fast Phong shading)

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

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

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

Это значительно упростит задачу просчета в реальном времени. И мы можем забыть о неуклюжем затенении Гуро и наслаждаться всеми преимуществами затенения по Фонгу на нашем обычном домашнем компьютере. Затенение по Фонгу теперь становиться обычным, линейным текстурированием, которое благодаря Майклу Абраш (Michael Abrash) может реализовываться очень и очень быстро.

Карта тени для Phong shading

Карта для затенения по Фонгу (Phong Map) представляет собой заранее просчитанный набор яркостей для всех возможных нормалей.

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

Итак, для каждой из вершин подготовленного к визуализации полигона вы должны просчитать координаты соответствующей позиции на карте затенения. Смотрите на рисунок справа. Для этого полигона определим два вектора, находящихся под прямым углом друг к другу и к нормали полигона. Назовем их V и H. Эти два вектора будут определять координаты u, v карты затенения (текстуры). Вспоминаем, что у текстур в 3х мерной графике своя система координат- (u, v).

Теперь расположим полигон в пространстве таким образом, что нам можно представить вектор света к нашей вершине. Это будет вектор L.

Наша цель — получить координаты u и v исходя из значений V, H, L.

Алгоритм очень прост. Если карта затенения будет размерностью 256х256 с точным центром, то:

  • u = ( V . L ) * 128 + 127
  • v = ( H . L ) * 128 + 127

Просчитайте это для каждой вершины, а затем "натяните" карту затенения на полигон — вы получите плавно затененный полигон в соответствии с внешним освещением.

Можно еще более упростить и ускорить процесс вычислений, если допустить, что источник света находится в той же точке, где и камера. В этом случае мы можем игнорировать сами векторы V и H, а вместо них использовать координаты x и y, компоненты вектора нормали. Умножим на 128 и прибавим 127. Все!

Особенности быстрого затенения по Фонгу

Визуальное качество наложения эффекта Phong Shading будет зависеть от размерности текстуры (карты) затенения. Это означает, что при ограниченной размерности блики на полигоне могут быть заметно пикселизированны. Если объект движущийся, то этого можно и не заметить вовсе. Если вы примените рельефное текстурирование (bump mapping), то эффект будет едва заметным.

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

[ Читайте по этой теме: Затенение Гуро ]

[ Читайте по этой теме: Рельефное тестурирвание ]



Материал для данной статьи любезно предоставил Hugo Elias




Дополнительно

iXBT BRAND 2016

«iXBT Brand 2016» — Выбор читателей в номинации «Процессоры (CPU)»:
Подробнее с условиями участия в розыгрыше можно ознакомиться здесь. Текущие результаты опроса доступны тут.

Нашли ошибку на сайте? Выделите текст и нажмите Shift+Enter

Код для блога бета

Выделите HTML-код в поле, скопируйте его в буфер и вставьте в свой блог.