ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 09.06.2020
Просмотров: 1713
Скачиваний: 4
71
строить
всю
сцену
,
даже
если
проблема
отсечения
теней
по
границе
будет
решена
.
Тени
обычно
имеют
размытые
границы
,
а
в
приведенном
алгоритме
они
всегда
имеют
резкие
края
.
Частично
избежать
этого
позволяет
расчет
теней
из
нескольких
источников
света
,
расположенных
рядом
и
последующее
смешение
результатов
.
Имеется
решение
первой
и
второй
проблемы
.
Для
этого
используется
буфер
маски
(
см
.
п
. 6.3)
Итак
,
задача
–
отсечь
вывод
геометрии
(
тени
,
в
данном
случае
)
по
границе
некоторой
произвольной
области
и
избежать
"
двойного
смешения
".
Общий
алгоритм
решения
с
использованием
буфера
маски
таков
:
1.
Очищаем
буфер
маски
значением
0
2.
Отображаем
заданную
область
отсечения
,
устанавливая
значения
в
буфере
маски
в
1
3.
Рисуем
тени
в
тех
областях
,
где
в
буфере
маски
установлены
значения
1.
Если
тест
проходит
,
устанавливаем
в
эти
области
значение
2.
Теперь
рассмотрим
эти
этапы
более
подробно
.
1.
/*
очищаем
буфер
маски
*/
glClearStencil(0x0);
/*
включаем
тест
*/
glEnable(GL_STENCIL_TEST);
2.
/*
условие
всегда
выполнено
и
значение
в
буфере
будет
равно
1*/
glStencilFunc (GL_ALWAYS, 0x1, 0xffffffff);
/*
в
любом
случае
заменяем
значение
в
буфере
маски
*/
glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
/*
выводим
геометрию
,
по
которой
затем
будет
отсечена
тень
*/
RenderPlane();
3.
72
/*
условие
выполнено
и
тест
дает
истину
только
если
значение
в
буфере
маски
равно
1 */
glStencilFunc (GL_EQUAL, 0x1, 0xffffffff);
/*
значение
в
буфере
равно
2,
если
тень
уже
выведена
*/
glStencilOp (GL_KEEP, GL_KEEP, GL_INCR);
/*
выводим
тени
*/
RenderShadow();
Строго
говоря
,
даже
при
применении
маскирования
остаются
некоторые
проблемы
,
связанные
с
работой
z-
буфера
.
В
частности
,
некоторые
участки
теней
могут
стать
невидимыми
.
Для
решения
этой
проблемы
можно
немного
приподнять
тени
над
плоскостью
c
помощью
модификации
уравнения
,
описывающего
плоскость
.
Описание
других
методов
выходит
за
рамки
данного
пособия
.
7.3.
Зеркальные
отражения
В
этом
разделе
мы
рассмотрим
алгоритм
построения
отражений
от
плоских
объектов
.
Такие
отражения
придают
б
ó
льшую
достоверность
построенному
изображению
и
их
относительно
легко
реализовать
.
Алгоритм
использует
интуитивное
представление
полной
сцены
с
зеркалом
как
составленной
из
двух
: «
настоящей
»
и
«
виртуальной
» –
находящейся
за
зеркалом
.
Следовательно
,
процесс
рисования
отражений
состоит
из
двух
частей
: 1)
визуализации
обычной
сцены
и
2)
построения
и
визуализации
виртуальной
.
Для
каждого
объекта
«
настоящей
»
сцены
строится
его
отраженный
двойник
,
который
наблюдатель
и
увидит
в
зеркале
.
Рис
. 9
Зеркальное
отражение
Зеркало
Положение
наблюдателя
Виртуальный
объект
Объект
73
Для
иллюстрации
рассмотрим
комнату
с
зеркалом
на
стене
.
Комната
и
объекты
,
находящиеся
в
ней
,
выглядят
в
зеркале
так
,
как
если
бы
зеркало
было
окном
,
а
за
ним
была
бы
еще
одна
такая
же
комната
с
тем
же
объектами
,
но
симметрично
отраженными
относительно
плоскости
,
проведенной
через
поверхность
зеркала
.
Упрощенный
вариант
алгоритма
создания
плоского
отражения
состоит
из
следующих
шагов
:
1.
Рисуем
сцену
как
обычно
,
но
без
объектов
-
зеркал
.
2.
Используя
буфер
маски
,
ограничиваем
дальнейший
вывод
проекцией
зеркала
на
экран
.
3.
Визуализируем
сцену
,
отраженную
относительно
плоскости
зеркала
.
При
этом
буфер
маски
позволит
ограничить
вывод
формой
проекции
объекта
-
зеркала
.
Эта
последовательность
действий
позволит
получить
убедительный
эффект
отражения
.
Рассмотрим
этапы
более
подробно
:
Сначала
необходимо
нарисовать
сцену
как
обычно
.
Не
будем
останавливаться
на
этом
этапе
подробно
.
Заметим
только
,
что
,
очищая
буферы
OpenGL
непосредственно
перед
рисованием
,
нужно
не
забыть
очистить
буфер
маски
:
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|
GL_STENCIL_BUFFER_BIT);
Во
время
визуализации
сцены
лучше
не
рисовать
объекты
,
которые
затем
станут
зеркальными
.
На
втором
этапе
необходимо
ограничить
дальнейший
вывод
проекцией
зеркального
объекта
на
экран
.
Для
этого
настраиваем
буфер
маски
и
рисуем
зеркало
glEnable(GL_STENCIL_TEST);
/*
условие
всегда
выполнено
и
значение
в
буфере
будет
равно
1*/
glStencilFunc(GL_ALWAYS, 1, 0);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
RenderMirrorObject();
74
В
результате
мы
получили
:
в
буфере
кадра
–
корректно
нарисованная
сцена
,
за
исключением
области
зеркала
;
в
области
зеркала
(
там
,
где
мы
хотим
видеть
отражение
)
значение
буфера
маски
равно
1.
На
третьем
этапе
нужно
нарисовать
сцену
,
отраженную
относительно
плоскости
зеркального
объекта
.
Сначала
настраиваем
матрицу
отражения
.
Матрица
отражения
должна
зеркально
отражать
всю
геометрию
относительно
плоскости
,
в
которой
лежит
объект
-
зеркало
.
Ее
можно
получить
,
например
,
с
помощью
такой
функции
(
попробуйте
получить
эту
матрицу
самостоятельно
в
качестве
упражнения
):
void
reflectionmatrix
(GLfloat reflection_matrix[4][4],
GLfloat plane_point[3],
Glfloat plane_normal[3])
{
GLfloat* p;
GLfloat* v;
float pv;
GLfloat* p = (Glfloat*)plane_point;
Glfloat* v = (Glfloat*)plane_normal;
float pv = p[0]*v[0]+p[1]*v[1]+p[2]*v[2];
reflection_matrix[0][0] = 1 - 2 * v[0] * v[0];
reflection_matrix[1][0] = - 2 * v[0] * v[1];
reflection_matrix[2][0] = - 2 * v[0] * v[2];
reflection_matrix[3][0] = 2 * pv * v[0];
reflection_matrix[0][1] = - 2 * v[0] * v[1];
reflection_matrix[1][1] = 1- 2 * v[1] * v[1];
reflection_matrix[2][1] = - 2 * v[1] * v[2];
reflection_matrix[3][1] = 2 * pv * v[1];
reflection_matrix[0][2] = - 2 * v[0] * v[2];
reflection_matrix[1][2] = - 2 * v[1] * v[2];
reflection_matrix[2][2] = 1 - 2 * v[2] * v[2];
reflection_matrix[3][2] = 2 * pv * v[2];
reflection_matrix[0][3] = 0;
reflection_matrix[1][3] = 0;
75
reflection_matrix[2][3] = 0;
reflection_matrix[3][3] = 1;
}
Настраиваем
буфер
маски
на
рисование
только
в
областях
,
где
значения
буфера
равно
1:
/*
условие
выполнено
и
тест
дает
истину
только
если
значение
в
буфере
маски
равно
1 */
glStencilFunc (GL_EQUAL, 0x1, 0xffffffff);
/*
ничего
не
меняем
в
буфере
*/
glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
и
рисуем
сцену
еще
раз
(
без
зеркальных
объектов
)
glPushMatrix();
glMultMatrixf((float *)reflection_matrix);
RenderScene();
glPopMatrix();
Наконец
,
отключаем
маскирование
glDisable(GL_STENCIL_TEST);
После
этого
можно
опционально
еще
раз
вывести
зеркальный
объект
,
например
,
с
альфа
-
смешением
–
для
создания
эффекта
замутнения
зеркала
и
т
.
д
.
Обратите
внимание
,
что
описанный
метод
корректно
работает
,
только
если
за
зеркальным
объектом
нет
других
объектов
сцены
.
Поэтому
существует
несколько
модификаций
этого
алгоритма
,
отличающихся
последовательностью
действий
и
имеющих
разные
ограничения
на
геометрию
.
Контрольные
вопросы
1.
В
результате
чего
возникает
эффект
ступенчатости
изображения
?
Опишите
алгоритм
устранения
ступенчатости
.
2.
Почему
в
OpenGL
нет
встроенной
поддержки
построения
теней
?
3.
Кратко
опишите
предложенный
метод
визуализации
зеркальных
объектов
.
Почему
он
не
работает
,
если
за
зеркалом
находятся
другие
объекты
сцены
?
Что
будет
отражаться
в
этом
случае
?
Подумайте
,
как
обойти
это
ограничение
?