Затенение по Гуро это метод линейной интерполяции освещенности в пределах одного полигона. Он был изобретен в 1971 и носит имя своего изобретателя. Это простой и эффективный метод придания ощущения изогнутости для ровного полигона. Этот метод также часто используется для сокращения глубины прорисовываемой сцены, путем имитации исчезновения удаленных объектов в тумане.
! Полигоном в трехмерной графике принято считать участок поверхности, имеющий как минимум три вершины лежащие в одной плоскости. Собственно говоря, полигон может иметь неограниченное количество вершин, но в трехмерной компьютерной графике, в подавляющем большинстве случаев разработчики используют именно треугольные полигоны. Во-первых, три вершины всегда однозначно определяют положение плоскости в пространстве, а четвертая вершина может лежать вне пределов плоскости и ее положение придется всегда проверять, что вызовет дополнительные трудности. Во-вторых, из треугольников всегда можно получить любой другой многоугольник. Прим. Переводчика
Слева вы видите изогнутую поверхность (состоящую из множества треугольных полигонов, или попросту - треугольников) отвизуализированную на экран обычным образом, где каждая треугольная грань (полигон) имеет равномерную освещенность(flat shading), справа та же поверхность, но с применением затенения Гуро (Gourad shading). Несомненно, второй вариант выглядит куда более привлекательно, поэтому рассмотрим один из вариантов его реализации.(Обращаю внимание уважаемых читателей, что вариантов реализации метода Гуро существует несколько, ниже приводится, собственный варинт разработанный и опробованный автором.)
В отличие от равномерного заполнения, в случае метода Гуро вы прежде всего обязаны определить и просчитать одним из известных способов приведенный вектор нормали для каждой вершины полигона. Получив нормаль мы можем определить shade (степень затенения или освещенность) в вершинах, а уже затем нам остается только провести интерполяцию освещенности по полигону.
При реализации затенения Гуро, по методу изложенному ниже, необходимо ограничится использованием только треугольных полигонов. Так как иногда, полигоны будут выглядеть не так как хотелось бы, если они имеют более трех вершин с различными оттенками в каждой из них. С треугольными полигонам все будет работать отлично.
Ниже мы рассмотрим подробнее фазу интерполяции освещения по
полигону.
Визуализация полигона и интепроляция
освещенности будет проведена путем scanline converting вдоль координаты
Х.
Будем предполагать, что освещенность в вершинах уже
вычислена и допустив, что вдоль кромок полигона она изменяется
линейно, вычислтиь ее значение в точках A и В, на каждое изменение
каодрдинаты Y будет несложно. Конечно, можно и непосредственно определить
нормаль в данных точках, и тут же вычислить освещенность... - вариатов
несколько, но суть в том, что перед началом дальнейших вычислений мы знаем
освещенность в точках A и В.
Перед тем, как первая горизонтальная линия будет просчитана, сделаем
некоторые приготовления. Рисунок ниже, поможет нам разобраться с тем, что
же происходит на самом деле.
Небольшой полигон, всего в несколько квадратных пикселей, изображен с равномерным заполнением только для простоты. Мы хотим просчитать линию А-В. Но мы имеем дело с пикселизованным экраном, поэтому мы сможем вывести на экран только линию состоящую из трех пикселей С- D. Центр первого пикселя, С, не совпадает с центром точки А, а центр пикселя D - не совпадает с точкой B.
Можно посчитать, что смещения в доли пикселя можно и не учитывать, но это будет справедливо только для больших полигонов, а вот для небольших, - это смещение учитывать надо. Особенно если на полигон будет наложена текстура.
В нашем случае мы произведем эти приготовления:
Сначала определим градиент изменения освещенности (shade) по всей длине линии. Произведем это обычным способом.
Gradient = (Bs - As) / (Bx - Ax);
где:
градиент показывает относительную величину изменения освещенности (shade) в пересчете на единицу изменения координаты X.
Теперь мы определим точное значение (shade) в точке С.
Cs = (1 - frac(Ax)) * Gradient;
величина frac(Ax) определяет смещение e , см. рисунок.
Теперь мы готовы к визуализации одной горизонтальной полосы из состава нашего полигона Гуро. Процесс включает в себя просчет оттенка каждого пикселя в линии и выводе его на устройство визуализации. Это достаточно простой процесс, так как величина изменения оттенка (градиент) линейна на всей протяженности выводимой линии. Вот пример псевдокода:
Shade = Cs
loop X from Cx to Dx
plot pixel at (X,Y) with colour Shade //вывод пикселя
Shade = Shade + Gradient
End of X loop
Как обычно, можно значительно ускорить скорость работы данного алгоритма, если реализовать его на ассемблере. Алгоритм приведенный ниже работает только в 8-битном режиме экрана и приведен исключительно с целью простоты демонстрации. Он далек от совершенства, но все же обладает очень приемлимой скоростью вывода. Для всех остальных режимов работы, можно составить аналогичные алгоритмы.
Метод реализует возможность х86 процессоров трактовать 32 битный регистр как два 16 битных и соответственно 16 битный регистр, как два 8 битных. Данный код использует старший байт регистра как целочисленную часть интерполируемого оттенка, а младший байт для хранения дробной части. Такое решение позволяет выполнить интерполяцию используя только целочисленные операции, освобождая сопроцессор для выполнения других задач.
Показанный внутренний цикл производит просчет только одной пиксельной линии полигона. Он использует 32 битные указатели на область памяти отображающую экран.
Ввиду того, что значение величины освещенности определяется значением с фиксированной точкой (целая и дробные части хранятся в разных регистрах), мы достигаем повышенной точности вычислений не вызывая дополнительных операций с плавающей точкой.
Для демонстрации фрагмента кода определим, что:
Несмотря на то, что затенение Гуро далеко от идеала, оно все же позволяет создавать значительно более реалистичные поверхности. Недостатки затенения Гуро начинают проявляться тогда, когда вы пытаетесь совместить вычисления освещенности с затенением больших по размеру полигонов.
Представьте себе, - вы имеете большой полигон, освещенный единственным источником света расположенным в центре полигона. Освещенность создаваемая источником света в вершинах полигона будет очень малой, ввиду значительной удаленности от источника света. Выведенный на экран полигон будет темным, хотя это не правильно, ведь центр полигона - освещен! Этот недостаток вы можете заметить, например, в игре Descent. Вспышки от выстрелов ярко освещают углы стен, и практически не влияют на изменение освещения, если вы выстреливаете в середину стены или пола.
(И наоборот, большой полигон будет всегда полностью освещен если с его противоположных краев стоит по источнику света, даже несмотря на то, что источники маломощны и середина полигона просто обязаны быть в тени)
Однако, если вы используете большое количество небольших полигонов, совместно с отдаленным источником света, то затенение Гуро может выглядеть очень и очень не плохо. Практически - чем меньше размер полигона, тем точнее и качественней будет сглаживание, тем больше, по качеству, оно будет похоже на затенение по Фонгу.