Реализация теней с помощью библиотеки OpenGL

А. Титоренко. - М.: Компьютер, ЮНИТИ, 1998. - | С. 31.

РЕАЛИЗАЦИЯ ТЕНЕЙ С ПОМОЩЬЮ БИБЛИОТЕКИ OPENGL

С.В. Журавлев, М.В. Михайлюк, М.А. Торгашев

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

В данной статье рассматривается решение задачи формирования теней с использованием библиотеки OpenGL. OpenGL - это программный интерфейс 3D графики (application program interface of 3D graphics, 3D API) или, иначе, библиотека графических функций, предназначенная для получения реалистичных изображений [1]. Возможность построения теней не реализована напрямую в OpenGL, однако, существуют различные способы их визуализации с помощью этой библиотеки. В статье дается обзор возможных методов построения теней, их использование применительно к OpenGL, рассматриваются их преимущества и недостатки, а также приводится возможная реализация одного из них.

Статья ориентирована на специалистов, знающих основные понятия компьютерной графики и библиотеки OpenGL.

Обзор базовых методов построения теней

Для начала, кратко рассмотрим физическую природу теней. Тени состоят из двух частей, полной (абсолютной) тени и полутени. Полная тень -это область объекта, принимающего тень (далее «затеняемого» объекта), которая невидима из любой части источника освещения. Полутень - это та область, которая может принимать некоторое количество света. Полутени формируют переходный участок между полной тенью и освещенными участками объекта; они варьируются как функция расположения источника освещения и объекта, отбрасывающего тень (далее «затеняющего» объекта).

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

"Проективные тени "

Относительно простой в реализации метод, использующий проекционные преобразования. Затеняющий объект просто проецируется на некоторую плоскость, связанную с затеняемым объектом, а затем визуализируется как отдельный примитив. Вычисление тени включает два этапа: вычисление матрицы проекции, а затем визуализация спроецированного объекта с нужным цветом. Очевидно, вычислительная сложность метода напрямую зависит от сложности затеняющего объекта.

Достоинства:

• Простота реализации;

• Может быть использован на любой платформе, поскольку использует только стандартные функции OpenGL.

Недостатки и ограничения:

• Необходимость обрезания тени по контуру затеняемого объекта для исключения висящих теней. В ряде задач, однако, такая проблема не стоит, например для автомобильных или авиационных симуляторов тень по поверхности земли обрезать не нужно (ясно, что в данном случае ситуация с висящей тенью невозможна). Также иногда в разрешении проблемы может помочь Z-буфер, например, тень, падающая на угол комнаты, образованной несколькими плоскостями, будет формироваться правильно. В остальных случаях используются дополнительные методики, в частности отсечение с помощью буфера трафарета;

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

• При использовании Z-буферизации без дополнительных ухищрений тени в ряде случаев могут частично уходить в объект. Это происходит, если фрагменты затеняемого объекта и тени получают одинаковые, либо близкие друг другу значения Z-координаты (глубины), что приводит к невозможности их отображения в правильном порядке (проблема Z-aliasing-a). Данная проблема решается обычно двумя способами: использовани-

ем принудительного смещения в Z-буфере (реализуется с помощью расширения OpenGL -glPolygonOffsetEXT), либо отключением Z-буферизации и применением буфера трафарета.

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

Метод «теневых объемов»

Этот метод обрабатывает тень, отбрасываемую объектом, как полигональный объем, то есть объемное тело, заданное многоугольниками. «Теневой объем» формируется из лучей, испускаемых источником освещения, далее проходящих через контур затеняющего объекта (со стороны источника света) и продолжающихся за пределы сцены. Заданный таким образом, теневой объем является усеченной пирамидой. Для определения того, какие части объектов сцены будут находиться в теневом объеме, используется буфер трафарета (stencil buffer, далее S-буфер). Метод относится к классу многопроходных:

• сначала вся сцена выводится с атрибутами рисования в тени (выключен источник света);

• затем рисуется теневой объем (только в S-буфер), при этом операции в S-буфере задаются таким образом, что после данного шага ненулевое значение получают затеняемые пикселы;

• и, наконец, сцена выводится второй раз с включенным источником света и включенным тестом трафарета с условием его прохождения при равенстве значения в S-буфере нулю.

Более подробное описание метода можно посмотреть, например, в [2]. Отметим только, что процедура неинвариантна относительно положения наблюдателя (при условиях его расположении внутри теневого объема и снаружи, S-буфер инициализируется различными значениями).

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

Преимущества:

• Не предъявляются никакие требования к форме затеняемых объектов;

• При корректной реализации дает весьма реалистичный результат.

Недостатки:

• Требование поддержки S-буфера высокой разрядности. К сожалению, многие графические акселераторы поддерживают лишь 1-битный S-

буфер (либо вовсе не поддерживают), что недостаточно для правильной работы метода;

• Высокая сложность построения теневого объема. В случае сложной формы затеняющего объекта вычисление теневого объема является весьма нетривиальной операцией. Обычно это ограничивает применение метода ситуациями, когда затеняющие объекты имеют простую форму;

• Неинвариантность относительно положения наблюдателя;

• Предъявляются высокие требования к корректности реализации некоторых функций, а именно к обработке смежных многоугольников. Если допускаются неточности, такие как "двойное попадание", подсчет в S-буфере может сбиться, что приводит к чрезвычайно некорректному результату.

«Карты теней»

Метод теневых карт («shadow maps») является методом экранного пространства и использует для построения теней Z-буфер и проекционное наложение текстур. Процедура выглядит следующим образом:

• Сцена выводится только в Z-буфер из положения источника света;

• Содержимое Z-буфера переписывается в текстуру, которая и называется картой тени. Ее элементы хранят значения расстояний видимых элементов сцены до источника света;

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

• Сцена визуализируется с нужным положением наблюдателя, при этом проводится следующая проверка: текстурная координата r, которая в данном случае имеет смысл расстояния текущего элемента до источника света, сравнивается со значением соответствующего текселя карты тени. Если r > значения текселя, значит существует элемент сцены, находящийся ближе к источнику света, чем текущий, и, следовательно, текущий элемент затеняется. В противном случае - он освещен. Технологически маскирование затененных участков реализуется заданием требуемого значения прозрачности (альфа-компоненты) в зависимости от результата сравнения и использованием при этом альфа теста.

Для наглядности все это можно представить так, будто тест глубины происходит относительно положения источника освещения.

Отметим, что данный метод требует наличия функции сравнения координаты r и тексельного значения с последующей установкой альфа-компоненты в 0 или 1 в зависимости от результата.

Данная возможность не входит в перечень функций OpenGL 1.1 и реализована как расширение OpenGL - SGIX_shadow, однако оно поддерживается только на платформе SGI.

Скорость метода не зависит напрямую ни от сложности затеняющего объекта, ни от сложности сцены.

Преимущества:

• Высокая скорость и эффективность;

• Не предъявляются никакие ограничения ни к форме затеняющих, ни к форме затеняемых объектов;

Источник света

Затеняющий объект

Плоскость затенения

Рис.1. Проецирование тени.

Недостатки:

• Метод в настоящее время может быть реализован только на платформе SGI;

• Проблема дискретности, ступенчатости из-за ограниченности размера текстуры приводит к искажениям. Артефакты визуализации проявляются в виде "рваных", ступенчатых границ тени, а также эффекта самозатенения. Для решения данной проблемы применяются множество подходов: усреднение результатов сравнения по ближайшим элементам, использование внеэкранных буферов с повышенным разрешением, "дрожание" теневой карты и другие;

• В ряде случаев, когда источник света окружен объектами, невозможно создать теневую карту, описывающую всю сцену.

Сравнительная характеристика методов

Из приведенных выше описаний методов, их преимуществ и недостатков можно сделать следующие выводы. Третий метод («карты теней») не может быть реализован на наиболее распространенной платформе Win32. Первый из оставшихся методов («проективные тени») наиболее приемлем в случае простой формы затеняемых объектов, второй же («теневые объемы») - в случае простой формы затеняющих объектов. В большинстве задач наиболее приемлемым является первое допущение - чаще всего мы действительно можем допустить, что затеняемый объект может быть приближенно задан небольшим количеством плоско-

стей, при этом не катастрофически ухудшив визуальное качество. Дополнительные доводы в пользу метода «проективных» теней - его простота, достаточная скорость и нетребовательность к качеству и полноте реализации OpenGL. Метод не требует поддержки каких либо расширений библиотеки, из нестандартных возможностей требуется лишь 1-битный S-буфер (для обрезания тени по границе), в то время как для метода «теневых объемов» необходим как минимум 8-битный S-буфер.

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

Метод проективныгх теней с использованием буфера трафарета

Напомним, что в данном методе тень формируется как проекция объекта, отбрасывающего тень (далее «затеняющего» объекта) на плоскость «затенения». Плоскость выбирается таким образом, чтобы она являлась более или менее точным приближением поверхности объекта, принимающего тень (далее «затеняемого» объекта). Иллюстрация - на рис 1.

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

Пусть мы имеем сцену, состоящую из объектов, источников света и средств наблюдения (камер). При этом для каждого объекта задан флаг, означающий необходимость затенять данный объект, а для каждого источника света - флаг, означающий необходимость строить тени от данного источника. Кроме того, каждому затеняемому объекту поставлены в соответствие три точки, задающие плоскость затенения. И, наконец, пусть имеется список объектов, отбрасывающих тень. Тогда имеется вся необходимая информация для работы метода. Сразу отметим, что мы не будем строить полутени.

Рассмотрим подробно ключевые аспекты, а затем приведем полную схему метода.

Вычисление матрицы проецирования тени

Рассмотрим процедуру вычисления матрицы проецирования на одну плоскость затенения от одного источника света.

Входные данные: координаты трех точек на плоскости затенения - Р1, Р2 и Р3; координаты источника освещения - LP0, LP1, LP2 и LP3, положим коэффициент LP3 равным 0 для направленного источника света с параллельным направлением лучей, и 1 - для точечного.

Выход: матрица проецирования тени T.

Матрица будет иметь различный вид для точечного и направленного источников света. В первом случае мы получаем ортографическое проецирование, а во втором - перспективное (см. рис. 2).

1. Вычисление коэффициентов A,B,C,D уравнения плоскости затенения вида Ax+By+Cz+D=0.

Здесь A, В и С имеют смысл координат нормали к плоскости, а D - смещения плоскости от начала координат. Для нахождения коэффициентов A,B,C мы вычисляем векторное произведение векторов P2-P1 и P3-P2 и нормируем результат, после чего D находится подстановкой в уравнение плоскости одной из точек плоскости, например P1.

• вычисляем вектор V1= Р1 - Р2;

• вычисляем вектор V2= Р3 - Р1;

• находим нормаль к плоскости как векторное произведение: n =[V1,V2];

• нормируем результат для получения вектора единичной длины: (А,В,С) = n / ilnll;

• вычисляем коэффициент D подстановкой в уравнение плоскости точки P1: D = -(А * Р10) + (В * Р11) + (С * Р12);

2. Проверка того, находится ли источник света в положительной полуплоскости плоскости затенения. Очевидно, что при невыполнении данного условия тень строить не нужно.

Вычислим величину dot, модуль которого имеет смысл расстояния источника света до плоскости: dot = А*ЬР0 + B*LP1 + C*LP2 + D. Если dot < 0 - выход.

3. Вычисление коэффициентов матрицы T проецирования тени. Формулы для коэффициентов выводятся вначале для простейшего случая -проецирование на плоскость z=0, после чего переход к общему случаю осуществляется выполнением преобразований поворота и сдвига исходной системы координат к системе координат, связанной с плоскостью затенения. Окончательные формулы имеют следующий вид:

T00 =dot-LP0*А T10 =-1Р0*В T20 =-LP0*С

T30 =-LP0*D

T01 =^1*А T11 =dot-LP1*В T21 =-LP1 *С

T31 =-LP1*D

T02 =-LP2*А T12 =-LP2*В T22 =dot-LP2*С

T32 =-LP2*D

T03 = -LP3 *А T13 = -LP3 *В T23 = -LP3 *С

T33 =dot-LP3*D.

Отсечение теней и расстановка теней и объектов по глубине с использованием буфера трафарета

Буфер трафарета (stencil buffer, далее S-буфер) - многофункциональный буфер OpenGL - подобен буферам глубины и цвета, но, в отличие от них, его значения имеют специфичный для конкретного приложения смысл. Работу S-буфера определя-

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

Идея использования 8-буфера в данном случае такова: при визуализации затеняемого объекта мы записываем в буфер трафарета значение 1, если проходит тест глубины. Таким образом, мы получаем в буфере как бы шаблон объекта. Затем перед выводом теней (которые фактически являются проекциями затеняющих объектов), мы включаем тест трафарета, задав условием его прохождения равенство значения пикселя в 8-буфере единице, и, отключаем Z-тест. В результате, тени рисуются только в тех областях, где был выведен затеняемый объект. Таким образом, обеспечивается обрезание тени по границе и правильная их расстановка по глубине: тень выводится поверх затеняющего объекта.

Пошаговый алгоритм работы с 8-буфером при рисовании тени от одного источника света и одного затеняющего объекта:

• разрешаем операции с 8-буфером;

• очищаем 8-буфер значением 0;

• задаем безусловное прохождение теста трафарета в любом случае и операцию замены значения в 8-буфере на 1, если проходит тест глубины;

• рисуем затеняемый объект;

• запрещаем операции с Z-буфером

• устанавливаем в качестве условия прохождения теста трафарета равенство значения в 8-буфере единице и задаем операцию уменьшения значения на 1 в случае прохождения теста;

• рисуем тень

• разрешаем операции с Z-буфером и запрещаем операции с 8-буфером.

Использование операции смешивания цветов для управления цветом тени

Непосредственное рисование Использование функции

смешивания цветов Рис.3. Управление цветом тени

Прежде всего, необходимо отметить, что при выводе тени необходимо отключать освещение. Поскольку тень формируется проецированием затеняющего объекта на плоскость и, нормали подвергаются преобразованиям проецирования, мы не можем управлять освещением тени. Таким образом, единственными параметрами для управления видом тени остаются ее цвет и прозрачность. При непосредственной визуализации без операции смешивания мы получаем «глухую» тень заданного цвета (рис. 3), который не меняется в зависимости от цвета затеняемого объекта (тень будет, например, серой и на поверхности с желтым материалом, и на поверхности с синим материалом, и на объекте с текстурой). Более приемлемой видится модуляция (перемножение) цвета тени и цвета затеняемого объекта. В таком случае компоненты цвета тени имеют смысл коэффициентов ослабления интенсивности соответствующих цветовых компонент, скажем, если цвет тени задан RGB тройкой - (0.5,0.5,0.5), то на затеняемых участках интенсивность ослабляется вдвое.

В библиотеке OpenGL существует операция смешивания цвета (blending), уравнение которого контролируется двумя параметрами: sfactor - на что умножается входящее значение цвета и dfactor - на что умножается значение цвета, имеющееся в буфере кадра:

C = Csrc sfactor + Cdst dfactor Для получения операции модуляции необходимо положить sfactor = Cdst, dfactor = 0 (задается командой OpenGL glBlendFunc(GL_SRC_COLOR, GL_ZERO)) либо sfactor = 0, dfactor = Csrc (команда OpenGL glBlendFunc (GL_ZERO, GL_SRC _COLOR)).

Тот же результат, однако, можно получить, используя функцию альфа-смешивания: C = Csrc alpha + Cdst (1-alpha) (команда OpenGL - glBlendFunc

мы хотим получить коэффициент ослабления тени k, то alpha = 1-k.

Поскольку функция альфа смешивания является наиболее распространенной, и, следовательно, поддерживается практически всеми драйверами, то целесообразнее использовать именно ее.

Иллюстрация применения смешивания цветов приведена на рис.3. Слева изображена «глухая» тень, получаемая при непосредственной визуализации, а справа - тень при использовании функции смешивания.

Теперь приведем полный пошаговый алгоритм метода.

Полная схема метода

Итак, пошаговая процедура построения теней выглядит следующим образом:

В цикле вывода объектов проверяем "теневой" флаг: если объект принимает тень, тогда:

• очищаем S-буфер: glClear(GL_STENCIL_BUFFER_BIT);

• разрешаем рисование в S-буфер: glEnable (GL_STENCIL_TEST);

Источник света с Точечный источник света

параллельным ходом лучей

Рис. 2. Получение тени от направленного и точечного источников света.

(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ ALPHA)), задавая при этом черный цвет тени и требуемую прозрачность: Сгс = (0,0,0,alpha). Если

Рис. 4. Иллюстрация работы метода на _конкретной сцене_

• задаем запись в S-буфер значения 1 при прохождении теста глубины: glStencilOp(GL_KEEP,GL_KEEP,GL_REP LACE);

glStencilFunc(GL_ALWAYS,1,1);

• рисуем текущий объект;

• отключаем тест глубины и освещение, включаем смешивание цветов: glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glEnable(GL_BLENDING);

• задаем функцию альфа смешивания и определяем цвет тени (пусть коэффициент ослабления тени равен 0.5): glB lendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_

8КС_ЛЬРИЛ); glColor4f(0.0f,0.0f,0.0f,0.5f);

• задаем в качестве условия прохождения теста трафарета равенство единице и операцию уменьшения на 1 при прохождении теста:

1епсП0р(0Ь_КЕЕР, ОЬ_БЕ СЯ,ОЬ_БЕ СЯ); g1StenciIFunc(GL_EQUЛL, 1,1);

• цикл по источникам света;

• проверяем флаг источника: если необходимо строить тени от него, то:

• вычисляем матрицу проецирования тени Т для текущего источника;

• сохраняем в стеке текущую матрицу: g1PushMatrix();

• домножаем текущую матрицу на теневую матрицу Т: g1Mu1tMatrixf(T);

• рисуем в цикле все объекты, отбрасывающие тень;

• восстанавливаем матрицу из стека: g1PopMatrix();

• выключаем режим смешивания цветов и тест трафарета, включаем освещение и тест глубины:

gIDisab1e(GL_BLEND);

gIDisab1e(GL_STENCIL_TEST); gIEnab1e(GL_LIGИTING);

gIEnab1e(GL_DEPTИ_TEST);

Заключение

Описанный в статье метод построения теней был реализован в среде разработки Visual C++ и включен как модуль в создаваемую авторами систему визуализации трехмерных сцен [3,4].

Применение метода на реальных сценах показало достаточно высокую скорость визуализации и приемлемое визуальное качество. Пример работы системы визуализации [3] с включенной опцией рисования теней приведен на рис. 4.

Авторы статьи выражают благодарность профессору Решетникову В.Н. за помощь в подготовке статьи.

Список литературы

1. Тихомиров Ю. OpenGL - Программирование трехмерной графики. СПб.: BHV - Санкт-Петербург, 1998. - 256 с.

2. Siggraph'98 Course: " Advanced Graphics Programming Techniques Using OpenGL», chapter 9.4 «Creating shadows".

3. Михайлюк М.В., Скворцов А.В, Торгашев М.А. Система визуализация моделей 3D Studio с использованием библиотеки OpenGL // Труды научно-технического семинара «Технические средства и технологии для построения тренажеров», 1998.

4. Михайлюк М.В., Решетников В.Н. Визуализация трехмерных сцен в реальном режиме времени // Программные продукты и системы, 1999. -№1.

ИССЛЕДОВАТЕЛЬСКОЕ ПРОЕКТИРОВАНИЕ СЛОЖНЫХ ОБЪЕКТОВ: ОТ МОДЕЛЕЙ ЖИЗНЕННЫХ ЦИКЛОВ К С-ТЕХНОЛОГИЯМ

Ю.Р. Валькман

Предлагаемая вниманию читателей статья продолжает изложение результатов исследований, опубликованных, в частности, в [1-6].

Жизненные циклы сложных изделий: определение, цели построения и использования

Развитием средств, методов, технологий его создания и эксплуатации, с одной стороны, происходит все большая детализация этапов жизненных циклов (ЖЦ), в частности его конструирования, с другой - расширение ЖЦ, учет все больших объемов информации, знаний, данных о его изготовлении и функционировании при его проектировании.