Аппроксимации Фонга или затенение по Фонгу (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) вы получите значение яркости нашего пикселя.
Здесь мы не будем рассматривать алгоритмы и какие-либо фрагменты
псевдокода. Те из вас, кто в состоянии воспроизвести это - не нуждаются в
псевдокоде. Ну а для тех, кто действительно хочет использовать затенение
по Фонгу в своих программах, можно предложить рассмотреть упрощенный
вариант реализации этого затенения.
Базовый алгоритм затенения по Фонгу - требует больших вычислительных ресурсов и поэтому очень медлен. Однако, эффект от применения этого затенения очень привлекателен, поэтому сам алгоритм неоднократно подвергался различным оптимизациям. Оптимизировать можно только базовый, главный алгоритм, и как не сокращай количество операций с фиксированной точкой или сколько не создавай заранее просчитанных таблиц, все равно в приложениях реального времени аппроксимации по Фонгу являются существенным "тормозом".
Один из вариантов упрощения, активно обсуждаемый в кругах трехмерной графики, это способ заключающийся в интерполяции угла между нормалью и направлением света, вместо базовой интерполяции самого вектора нормали по всему полигону. Это отличная идея, с одним лишь минусом - фактически она не дает ничего. По существу это воспроизводство затенения Гуро (т.к. интерполируется скалярная величина), и поэтому и пользы от него мало. (Возможно этот факт можно оспорить и потом долго дискутировать, но это мнение самого автора статьи. Прим. переводчика)
Однако, этот способ дал толчок для развития мысли в правильном направлении, и было предложено интерполировать не один лишь угол, а сразу два. Это будет очень напоминать интерполяцию вектора. Далее, вместо проделывания целого ряда тяжелейших вычислений с этими двумя углами, можно рассмотреть эти углы как соответствие координатам на заранее просчитанной карте (текстуре) затенения.
Это значительно упростит задачу просчета в реальном времени. И мы можем забыть о неуклюжем затенении Гуро и наслаждаться всеми преимуществами затенения по Фонгу на нашем обычном домашнем компьютере. Затенение по Фонгу, теперь становиться обычным, линейным текстурированием, которое благодаря Майклу Абраш (Michael Abrash) может реализовываться очень и очень быстро.
Карта для затенения по Фонгу (Phong Map) представляет собой заранее просчитанный наобор яркостей для всех возможных нормалей.
Как ни странно карта тени это очень несложная текстура. Удобно
использовать текстуру размерностью 256х256 пикселей. Вот она. Вы можете ее
просто взять и использовать. Более того, вы запросто сможете создать свою
собственную. Например, вы можете применить текстуру с яркими кольцами для
придания внешнего вида хромированного объекта.
Итак, для каждой из вершин подготовленного к визуализации полигона, вы
должны просчитать координаты соответствующей позиции на карте затенения.
Смотрите на рисунок справа. Для этого полигона определим два вектора,
находящихся под прямым углом друг к другу и к нормали полигона. Назовем их
V и H. Эти два
вектора будут определять координаты u, v карты затенения (текстуры).
Вспоминаем, что у текстур в 3х мерной графике своя система координат- (u,
v).
Теперь расположим полигон в пространстве таким образом, что нам можно представить вектор света к нашей вершине. Это будет вектор L.
Наша цель получить координаты u и v исходя из значений V, H, L.
Алгоритм очень прост. Если карта затенения будет размерностью 256х256 с точным центром то:
Просчитайте это для каждой вершины, а затем "натяните" карту затенения на полигон - вы получите плавно затененный полигон в соответствии с внешним освещением.
Можно еще более упростить и ускорить процесс вычислений, если допустить, что источник света находится в той же точке, где и камера. В этом случае мы можем игнорировать сами векторы V и H, а вместо них использовать координаты x и y компоненты вектора нормали. Умножим на 128 и прибавим 127. Все!
Визуальное качество наложения эффекта Phong Shading будет зависеть от разрмерности текстуры (карты) затенения. Это означает, что при ограниченной размерности, блики на полигоне могут быть заметно пикселизированны. Если объект движущийся, то это можно и не заметить вовсе. Если вы примените рельефное текстурирование (bump mapping) то эффект будет едва заметным.
Недостатком данного метода будет так же проявление эффекта паразитного источника света. Первый будет там, где и должен быть , а вот второй появится с противоположной стороны объекта.