Использование OpenGL

3 РАЗРАБОТКА АЛГОРИТМОВ

3.1 Методы освещения

Если попытаться классифицировать все известные на сегодня методы освещения, то в первую очередь их можно разделить на программные и аппаратные. При этом программные методы, в которых всю работу выполняет центральный процессор, использовавшиеся задолго до появления 3D акселераторов, являются наиболее часто используемыми сегодня. Аппаратные методы полностью (или почти полностью) реализуются с помощью видеокарты. Эти методы пока редко используются даже в секторе Hi-End.

3.1.1 Полностью программное освещение

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

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

Карты освещённости (lightmaps), впервые использованные в glQuake, на сегодняшний день широко применяются в 3D играх, т. е. там, где необходимо достичь компромисс между скоростью и качеством.

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

Картинка, содержащая световой блик, называется картой освещённости (lightmap). Карты освещённости можно или смешивать с накладываемой на объект текстурой, или применять уже поверх текстуры (двухпроходный вариант). Если ускоритель поддерживает мультитекстурирование (например, 3dfx Voodoo2 и выше, nVidia RivaTNT/GeForce, S3 Savage, ATI Rage 128 или Radeon), то такое освещение почти не снижает скорости работы.

Карты освещённости перебрасывают всю наиболее тяжёлую работу с процессора на 3D-акселератор. Цена этого решения - активное использование ограниченной локальной (текстурной) памяти видеокарты и/или интенсивный обмен по системной шине.

    Алгоритм достаточно прост:
  1. Сцена выводится только в Z-буфер, без освещения - для того, чтобы карты освещённости накладывались только на нужные плоскости. Неважно, с текстурами или без.
  2. Устанавливается режим Z-буферизации GL_LEQUAL ("меньше или равно"), что вызывает перезапись значений цвета при повторной прорисовке тех же объектов.
  3. Выбирается специальный режим смешивания; по сути - модуляция, glBlend(GL_ZERO, GL_SRC_COLOR).
  4. Настраивается автогенерация текстурных координат (glTexGen...) для правильного позиционирования "блика"; в качестве самих текстур используются карты освещённости. При этом яркость карты освещённости контролируется модулирующим её цветом, glColor3f (рассчитывается программно, исходя из расстояния от источника до вершин). Шаг 4 повторяется для каждой плоскости (см. рис 3.1).
  5. Режим смешивания - сложение, glBlend(GL_ONE, GL_ONE).
  6. Сцена выводится повторно, без освещения, смешиваясь с картами освещённости. Текстуры при этом включены (см. рис. 3.2).

    Качество такого "освещения" сильно зависит от количества и размера карт освещённости. Практика показала, что выгодно для простых объектов использовать карты освещённости, а более сложные, состоящие из многих плоскостей, освещать стандартными средствами OpenGL. Результат - более высокие скорость работы и качество изображения, чем, если бы эти два метода применялись по отдельности.


    Рисунок 3.1 - Сцена без освещения

    Рисунок 3.2 - Карты освещённости

    Рисунок 3.3 - Сцена с освещением

    Несмотря на очевидные преимущества, метод имеет свои недостатки. Если в сцене преобладают сложные объекты, то проецирование на сотни и тысячи плоскостей одновременно окажется даже медленнее, чем математические расчёты в вершинах.

    Затем, алгоритм многопроходный по своей сути. Отключение расчётов уравнения освещения в несколько раз снижает ограничение по геометрии, но затем придётся умножить время расчёта и передачи в акселератор данных на число проходов. Даже при использовании мультитекстурирования, возможно, к многопроходному варианту всё равно придётся прибегнуть.

    3.1.3 Виртуальные источники света

    Можно использовать и стандартные средства освещения OpenGL, учитывая программно-аппаратное ограничение на число источников света. Марк Килгард (Mark J. Kilgard) в своей работе рассмотрел две возможные модификации метода [5]. В обоих случаях все источники помещались в один список, и сортировались согласно их важности, или ("яркости"). Разница состояла только в способе вычисления этой "яркости". В конечном счёте, так или иначе, использовались только самые "важные" источники.

    В первом, самом простом случае, учитывалось только расстояние до объекта, "яркостью" был взятый с обратным знаком квадрат расстояния.

    Второй вариант использует модель Ламберта. Согласно закону Ламберта, диффузная составляющая (GL_DIFFISE) пропорциональна косинусу угла между нормалью к поверхности, и направлением луча света.

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

    Алгоритм может быть дополнительно усовершенствован за счёт учёта энергии источника, т. е. лучше всегда отдавать предпочтение источникам с большей энергией. Амплитудой источника A можно считать значение яркости luma=0.299*R + 0.587*G + 0.114*B. Целесообразно в качестве цвета RGB взять диффузную составляющую источника света

    Второе усовершенствование касается расчёта расстояния до объекта. Это связано с тем, что расчёт расстояния до центра объекта можно использовать только для сферических объектов, в противном случае можно, например, ввести несколько "контрольных точек" и учитывать расстояние до каждой из них.

    3.1.4 Накопление освещённости

    Кроме всех перечисленных трюков, можно использовать стандартные средства освещения OpenGL, а ограничение числа источников преодолевать с помощью буфера накопления (accumulation buffer). Если каждый источник освещает все объекты, алгоритм достаточно прост. Суммарная освещённость поверхности состоит из "фонового освещения" (GL_LIGHT_MODEL_AMBIENT), общего для всей сцены, и отдельных составляющих, вносимых каждым источником. Кроме того, "снизу" сцены обычно выводится фоновая картинка (небо и горизонт), а "поверх" накладываются спецэффекты вроде тумана и т.п. Это тоже надо учесть, чтобы не применить спецэффекты несколько раз к одному и тому же изображению.

      Вот стандартная последовательность действий:
    1. Очистить видеобуфер и Z-буфер.
    2. Вывести сцену с включёнными фоновым светом, спецэффектами, и любыми первыми 8-ю источниками.
    3. Скопировать полученную картинку в буфер накопления - glAccum(GL_LOAD, 1.0F).
    4. Отключить фоновое освещение, и все спецэффекты (туман, фоновую картинку и т.д.)
    5. Очистить видеобуфер и Z-буфер.
    6. Включить следующие 8 источников (или столько, сколько осталось).
    7. Вывести сцену.
    8. Добавить результат к буферу накопления - glAccum(GL_ACCUM, 1.0F).
    9. Повторять шаги 5 - 8 до тех пор, пока не отработают все источники.
    10. Вернуть результат из буфера накопления в видеобуфер: glAccum(GL_RETURN, 1.0F).

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