Файл: графическая библиотека opengl.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 09.06.2020

Просмотров: 1711

Скачиваний: 4

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

 66

Глава

 7  

 

Приемы

 

работы

 

с

 OpenGL 

В

 

этой

 

главе

 

мы

 

рассмотрим

как

 

с

 

помощью

 OpenGL 

создавать

 

некоторые

 

интересные

 

визуальные

 

эффекты

непосредственная

 

поддержка

 

которых

 

отсутствует

 

в

 

стандарте

 

библиотеки

7.1. 

Устранение

 

ступенчатости

 

Начнем

 

с

 

задачи

 

устранения

 

ступенчатости

    (

antialiasing

). 

Эффект

 

ступенчатости

  (

aliasing

возникает

 

в

 

результате

 

погрешностей

 

растеризации

 

примитивов

 

в

 

буфере

 

кадра

 

из

-

за

 

конечного

  (

и

как

правило

небольшого

разрешения

 

буфера

Есть

 

несколько

 

подходов

 

к

 

решению

 

данной

 

проблемы

Например

можно

 

применять

 

фильтрацию

 

полученного

 

изображения

Также

 

этот

 

эффект

 

можно

 

устранять

 

на

 

этапе

 

растеризации

сглаживая

 

образ

 

каждого

 

примитива

Здесь

 

мы

 

рассмотрим

 

прием

позволяющий

 

устранять

 

подобные

 

артефакты

 

для

 

всей

 

сцены

 

целиком

Для

 

каждого

 

кадра

 

необходимо

 

нарисовать

 

сцену

 

несколько

 

раз

на

 

каждом

 

проходе

 

немного

 

смещая

 

камеру

 

относительно

 

начального

 

положения

Положения

 

камер

например

могут

 

образовывать

 

окружность

Если

 

сдвиг

 

камеры

 

относительно

 

мал

то

 

погрешности

 

дискретизации

 

проявятся

 

по

-

разному

и

усредняя

 

полученные

 

изображения

мы

 

получим

 

сглаженное

 

изображение

.  

Проще

 

всего

 

сдвигать

 

положение

 

наблюдателя

но

 

перед

 

этим

 

нужно

 

вычислить

 

размер

 

сдвига

 

так

чтобы

 

приведенное

 

к

 

координатам

 

экрана

 

значение

 

не

 

превышало

скажем

половины

 

размера

 

пикселя

.  

Все

 

полученные

 

изображения

 

сохраняем

 

в

 

буфере

-

накопителе

 

с

 

коэффициентом

 1/n, 

где

 n – 

число

 

проходов

 

для

 

каждого

 

кадра

Чем

 

больше

 

таких

 

проходов

 

– 

тем

 

ниже

 

производительность

но

 

лучше

 

результат

for(i=0;i<samples_count;++i)  

/* 

обычно

 samples_count 

лежит

 

в

 

пределах

 

от

 5 

до

 10 */ 

  ShiftCamera(i); 

/* 

сдвигаем

 

камеру

 */ 


background image

 67

  RenderScene(); 

 

  if (i==0) 

   /* 

на

 

первой

 

итерации

 

загружаем

 

изображение

 */ 

   glAccum(GL_LOAD,1/(float)samples_count); 
  else 

   /* 

добавляем

 

к

 

уже

 

существующему

 */ 

   glAccum(GL_ACCUM,1/(float)samples_count); 

/* 

Пишем

 

то

что

 

получилось

назад

 

в

 

исходный

 

буфер

 */ 

glAccum(GL_RETURN,1.0); 

 

Следует

 

отметить

что

 

устранение

 

ступенчатости

 

сразу

 

для

 

всей

 

сцены

как

 

правило

связано

 

с

 

серьезным

 

падением

 

производительности

 

визуализации

так

 

как

 

вся

 

сцена

 

рисуется

 

несколько

 

раз

Современные

 

ускорители

 

обычно

 

аппаратно

 

реализуют

 

другие

 

методы

основанные

 

на

 

так

 

называемом

 

ресамплинге

 

изображений

.  

7.2. 

Построение

 

теней

 

В

 OpenGL 

нет

 

встроенной

 

поддержки

 

построения

 

теней

 

на

 

уровне

 

базовых

 

команд

В

 

значительной

 

степени

 

это

 

объясняется

 

тем

что

 

существует

 

множество

 

алгоритмов

 

их

 

построения

которые

 

могут

 

быть

 

реализованы

 

через

 

функции

 OpenGL. 

Присутствие

 

теней

 

сильно

 

влияет

 

на

   

реалистичность

 

трехмерного

 

изображения

поэтому

 

рассмотрим

 

один

 

из

 

подходов

 

к

 

их

 

построению

Большинство

 

алгоритмов

предназначенных

 

для

 

построения

 

теней

используют

 

модифицированные

 

принципы

 

перспективной

 

проекции

Здесь

 

рассматривается

 

один

 

из

 

самых

 

простых

 

методов

С

 

его

 

помощью

 

можно

 

получать

 

тени

отбрасываемые

 

трехмерным

 

объектом

 

на

 

плоскость

Общий

 

подход

 

таков

для

 

всех

 

точек

 

объекта

 

находится

 

их

 

проекция

 

параллельно

 

вектору

соединяющему

 

данную

 

точку

 

и

 

точку

в

 

которой

 

находится

 

источник

 

света

на

 

некую

 

заданную

 

плоскость

Тем

 

самым

 

получаем

 

новый

 

объект

целиком

 

лежащий

 

в

 

заданной

 

плоскости

Этот

 

объект

 

и

 

является

 

тенью

 

исходного

Рассмотрим

 

математические

 

основы

 

данного

 

метода

Пусть

P

 – 

точка

 

в

 

трехмерном

 

пространстве

которая

 

отбрасывает

 

тень

L

 – 

положение

 

источника

 

света

который

 

освещает

 

данную

 

точку


background image

 68

S

=a(

L

-

P

)-

P

  - 

точка

в

 

которую

 

отбрасывает

 

тень

 

точка

 P, 

где

 a – 

параметр

Предположим

что

 

тень

 

падает

 

на

 

плоскость

 

z=0.

 

В

 

этом

 

случае

 

a=z

p

/(z

l

-z

p

). 

Следовательно

,  

x

= (x

p

z

- z

l

z

p

) / (z

- z

p

), 

y

= (y

p

z

l

-y

l

z

p

) / (z

- z

p

), 

z

= 0 

Введем

 

однородные

 

координаты

x

s

=x

s

'/w

s

y

s

=y

s

'/w

s

z

s

=0 

w

s

'=z

l

-z

p

 

Отсюда

 

координаты

 S 

могут

 

быть

 

получены

 

с

 

использованием

 

умножения

 

матриц

 

следующим

 

образом

 

 

 

 

 

Для

 

того

чтобы

 

алгоритм

 

мог

 

рассчитывать

 

тень

падающую

 

на

 

произвольную

 

плоскость

рассмотрим

 

произвольную

 

точку

 

на

 

линии

 

между

 

и

 

P

представленную

 

в

 

однородных

 

координатах

a

P

+b

L, 

где

 a 

и

 b – 

скалярные

 

параметры

Следующая

 

матрица

 

задает

 

плоскость

 

через

 

координаты

 

ее

 

нормали

=

d

z

y

x

G

n

n

n

=

l

l

l

l

l

s

s

s

s

s

s

z

y

x

z

z

z

y

x

w

y

x

0

0

0

1

0

0

0

0

0

0

0

]

1

[

]

0

[


background image

 69

Точка

в

 

которой

 

луч

проведенный

 

от

 

источника

 

света

 

через

 

данную

 

точку

 

P, 

пересекает

 

плоскость

 G, 

определяется

 

параметрами

 a 

и

 b, 

удовлетворяющими

 

следующему

 

уравнению

(a

P

+b

L)G 

= 0 

Отсюда

 

получаем

: a(

PG) 

+ b(

LG) 

= 0. 

Этому

 

уравнению

 

удовлетворяют

  

a = (

LG), 

b = -(

PG) 

Следовательно

координаты

 

искомой

 

точки

 

= (

LG)P-(PG)L. 

Пользуясь

 

ассоциативностью

 

матричного

 

произведения

получим

  

P

[(

LG

)

GL

],  

где

 

I – 

единичная

 

матрица

Матрица

  (

LG

)

GL 

используется

 

для

 

получения

 

теней

 

на

 

произвольной

 

плоскости

Рассмотрим

 

некоторые

 

аспекты

 

практической

 

реализации

 

данного

 

метода

 

с

 

помощью

 OpenGL. 

Предположим

что

 

матрица

 floorShadow 

была

 

ранее

 

получена

 

нами

 

из

 

формулы

 (

LG

)

GL

Следующий

 

код

 

с

 

ее

 

помощью

 

строит

 

тени

 

для

 

заданной

 

плоскости

/* 

Делаем

 

тени

 

полупрозрачными

 

с

 

использованием

 

смешения

 

цветов

(blending) */ 

glEnable(GL_BLEND); 
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); 
glDisable(GL_LIGHTING);  
glColor4f(0.0, 0.0, 0.0, 0.5); 
glPushMatrix(); 

   /* 

Проецируем

 

тень

 */ 

   glMultMatrixf((GLfloat *) floorShadow); 

   /* 

Визуализируем

 

сцену

 

в

 

проекции

 */ 

   RenderGeometry(); 
glPopMatrix(); 
glEnable(GL_LIGHTING); 
glDisable(GL_BLEND); 

/* 

Визуализируем

 

сцену

 

в

 

обычном

 

режиме

 */ 

RenderGeometry();   

 

Матрица

 floorShadow 

может

 

быть

 

получена

 

из

 

уравнения

 (*) 

с

 

помощью

 

следующей

 

функции

/* 

параметры

:  plane – 

коэффициенты

 

уравнения

 

плоскости

 

  lightpos 

– 

координаты

 

источника

 

света

 


background image

 70

   

возвращает

: matrix – 

результирующая

 

матрица

 

*/ 

void 

shadowmatrix

(GLfloat matrix[4][4], GLfloat plane[4], 

                  GLfloat lightpos[4]) 

  GLfloat dot; 
 
  dot = plane[0] * lightpos[0] + 
        plane[1] * lightpos[1] + 
        plane[2] * lightpos[2] + 
        plane[3] * lightpos[3]; 
 
  matrix[0][0] = dot - lightpos[0] * plane[0]; 
  matrix[1][0] = 0.f - lightpos[0] * plane[1]; 
  matrix[2][0] = 0.f - lightpos[0] * plane[2]; 
  matrix[3][0] = 0.f - lightpos[0] * plane[3]; 
 
  matrix[0][1] = 0.f - lightpos[1] * plane[0]; 
  matrix[1][1] = dot - lightpos[1] * plane[1]; 
  matrix[2][1] = 0.f - lightpos[1] * plane[2]; 
  matrix[3][1] = 0.f - lightpos[1] * plane[3]; 
 
  matrix[0][2] = 0.f - lightpos[2] * plane[0]; 
  matrix[1][2] = 0.f - lightpos[2] * plane[1]; 
  matrix[2][2] = dot - lightpos[2] * plane[2]; 
  matrix[3][2] = 0.f - lightpos[2] * plane[3]; 

 
  matrix[0][3] = 0.f - lightpos[3] * plane[0]; 
  matrix[1][3] = 0.f - lightpos[3] * plane[1]; 
  matrix[2][3] = 0.f - lightpos[3] * plane[2]; 
  matrix[3][3] = dot - lightpos[3] * plane[3]; 

 

Заметим

что

 

тени

построенные

 

таким

 

образом

имеют

 

ряд

 

недостатков

.  

 

Описанный

 

алгоритм

 

предполагает

что

 

плоскости

 

бесконечны

и

 

не

 

отрезает

 

тени

 

по

 

границе

Например

если

 

некоторый

 

объект

 

отбрасывает

 

тень

 

на

 

стол

она

 

не

 

будет

 

отсекаться

 

по

 

границе

и

тем

 

более

, "

заворачиваться

на

 

боковую

 

поверхность

 

стола

 

В

 

некоторых

 

местах

 

тени

 

может

 

наблюдаться

 

эффект

  "

двойного

 

смешения

" (reblending), 

т

.

е

темные

 

пятна

 

в

 

тех

 

участках

где

 

спроецированные

 

треугольники

 

перекрывают

 

друг

 

друга

 

С

 

увеличением

 

числа

 

поверхностей

 

сложность

 

алгоритма

 

резко

 

увеличивается

т

.

к

для

 

каждой

 

поверхности

 

нужно

 

заново