// Создан ли контекст рисования?
bool IsCreated ( ) ;
// Является ли контекст рисования текущим?
bool IsCurrent ( ) const ;
// Делает контект текущим bool MakeCurrent ( ) ;
196
Приложение Б.
Демонстрационные программы// Вызывается в конце рисования ,
// показ созданного изображения void SwapBuffers ( ) ;
private :
// создан ли контекст bool m_created ;
// окно , для которого контекст
HWND m_wnd;
// контекст устройства
HDC m_dc;
// контекст рисования OpenGL
HGLRC m_glrc ;
} ;
#endif
Программа Б.7. Файл glrc.cpp.
#include
#include " g l r c . h"
#include " a s s e r t . h"
GLRC: :GLRC(HWND wnd)
:
m_created
( f a l s e )
{
a s s e r t ( wnd ) ;
m_wnd = wnd ;
m_dc = : : GetDC( wnd ) ;
a s s e r t ( m_dc ) ;
}
GLRC: : GLRC( )
{
i f ( m_created )
Б.5. Класс для работы с OPENGL в WIN32 197
Destroy ( ) ;
}
void GLRC: : Destroy ( )
{
wglDeleteContext ( m_glrc ) ;
: : ReleaseDC (m_wnd, m_dc ) ;
m_created = f a l s e ;
}
bool GLRC: : MakeCurrent ( )
{
a s s e r t ( m_created ) ;
i f ( IsCurrent ( ) )
return true ;
BOOL r e s = wglMakeCurrent (m_dc, m_glrc ) ;
return ( r e s != FALSE ) ;
}
bool GLRC: : Create ( )
{
a s s e r t ( ! m_created ) ;
int nPixelFormat = 0 ;
DWORD f l a g s ;
f l a g s =
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER;
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof (PIXELFORMATDESCRIPTOR) ,
1 ,
198
Приложение Б. Демонстрационные программы f l a g s ,
PFD_TYPE_RGBA,
24 ,
0 , 0 , 0 , 0 , 0 , 0 ,
1 ,
0 ,
0 ,
0 , 0 , 0 , 0 ,
32 ,
0 ,
0 ,
PFD_MAIN_PLANE,
0 ,
0 , 0 , 0
} ;
pfd . cAlphaBits = 8 ;
nPixelFormat = ChoosePixelFormat ( m_dc, &pfd ) ;
BOOL r e s =
SetPixelFormat ( m_dc, nPixelFormat , &pfd ) ;
i f ( r e s == FALSE)
return f a l s e ;
m_glrc = wglCreateContext ( m_dc ) ;
m_created = true ;
return MakeCurrent ( ) ;
}
void GLRC: : SwapBuffers ( )
{
a s s e r t ( m_created ) ;
: : SwapBuffers (m_dc ) ;
Б.5. Класс для работы с OPENGL в WIN32 199
}
bool GLRC: : IsCurrent ( ) const
{
a s s e r t ( m_created ) ;
return : : wglGetCurrentContext ( ) == m_glrc ;
}
bool GLRC: : IsCreated ( )
{
return m_created ;
}
Приложение В.
Примеры практических заданийВ.1. Cornell Box
Целью задания является создание изображения заданной трехмерной статичной сцены средствами OpenGL с использо- ванием стандартных геометрических примитивов.
Требуется создать изображение сцены Cornell Box. Эта клас- сическая сцена представляет собой комнату кубического вида, с отсутствующей передней стенкой. В комнате находятся геомет- рические предметы различных форм и свойств (кубы, парал- лелепипеды, шары), а также протяженный источник света на потолке. Присутствует также камера с заданными параметрами
(обычно она расположена так, чтобы была видна вся комната).
В одной из лабораторий Корнельского университета такая комната существует в реальности, и ее фотографии сравнива- ются с изображениями, построенными методами трассировки лучей для оценки точности методов. На странице лаборатории
(http://graphics.cornell.edu) можно найти описание геометрии сцены в текстовом формате.
201
202
Приложение В. Примеры практических заданий
Рис. В.1. Пример сцены Cornell Box.
Реализации сцены, приведенной на рисунке В.1, достаточно для выполнения задания, хотя возможно введение новых пред- метов дополнительно к существующим или вместо них. Привет- ствуется использование примитивов библиотек GLUT и GLU.
Внимание! Сцена не должна превращаться в набор разнород- ных предметов. Эстетичность и оригинальность выполненного задания принимается во внимание.
Протяженный источник света на потолке комнаты можно эмулировать несколькими точечными источниками.
За простейшую реализацию сцены ставится 7 баллов.
Реалистичность сцены можно значительно повысить за счет разбиения многоугольников.
Суть этого в том, что в модели осве- щения OpenGL освещенность вычисляется в вершинах много- угольника с учетом направления нормалей в этих вершинах, а затем линейно интерполируется по всей поверхности. Если ис- пользуются относительно большие многоугольники, то, очевид- но, невозможно получить действительно плавные переходы и за-
В.1. CORNELL BOX
203
тенения. Для преодоления этого недостатка можно разбивать большие грани (стены, например) на множество меньших по раз- мерам. Соответственно разброс в направлении нормалей в вер- шинах одного многоугольника не будет столь велик и затенение станет более плавным (1 балл).
Наложение текстур на объекты сцены поощряется 2-мя бал- лами.
Дополнительными баллами оценивается присутствие в сцене теней. Один из простейших алгоритмов наложения теней приве- ден в разделе 8.2. За его реализацию можно получить до 2 бал- лов. Использование более продвинутых алгоритмов (например,
shadow volumes) будет оценено дополнительными баллами.
Реализация устранения ступенчатости (antialiasing) методом,
предложенным в разделе 8.1 или каким-либо другим, оценивает- ся в 2 балла.
За введение в сцену прозрачных объектов и корректный их вывод дается 1 балл. Механизм работы с прозрачными объекта- ми описан в разделе 7.1.
Задание оценивается, исходя из 15 баллов.
В приведенной ниже таблице указано распределение баллов в зависимости от реализованных требований:
Простейший вариант сцены (только освещение) 7 баллов
Разбиение полигонов
+1 балл
Использование текстур
+2 балла
Наложение теней
+2 балла
Устранение ступенчатости
+2 балла
Использование прозрачных объектов
+1 балл
Дополнительные баллы можно получить за хорошую опти- мизацию программы, необычные решения, эстетичность и т.д.
204
Приложение В. Примеры практических заданий
В.2. Виртуальные часы
Целью задания является создание трехмерной интерактив- ной модели аналоговых часов.
Рис. В.2. Пример трехмерных виртуальных часов.
Обязательные требования к программе:
€ Программа должна демонстрировать на экране трехмер- ную модель часов. Часы могут быть любые, от наручных до кремлевских. Проявите в полной мере Вашу фантазию и чувство меры! Постарайтесь сделать как можно более реа- листичную сцену. Поощряется подробная детализация эле- ментов часов.
€ Часы на экране обязательно должны иметь минутную и часовую стрелки. Секундная по желанию, но очень при- ветствуется (иначе трудно будет определить, ходят часы или нет).
В.2. Виртуальные часы
205
€ Время на часах должно совпадать с системным временем компьютера. Часы обязательно должны ходить, т.е. стрел- ки должны двигаться, и скорость их движения не должна зависеть от производительности компьютера, а определять- ся только текущим временем.
€
Сцена должна быть интерактивной, т.е. давать приемле- мую частоту кадров в секунду (>10) при визуализации на машине с аппаратным ускорителем трехмерной графики.
Если программа будет работать медленно, баллы могут быть снижены.
€ Необходимо реализовать вращения часов (или, возможно,
камеры) с помощью мыши (предпочтительно) или клавиа- туры. Можно также предусмотреть режимы с автоматиче- ским вращением.
Пожелания к программе:
€ Поощряется введение дополнительной геометрии. Напри- мер, ремешков, маятников и т.д. Можно сделать часы с ку- кушкой, будильник и т.п.
€ Желательно наличие возможностей для управления про- цессом визуализации. Например, наличие/отсутствие тек- стур, режимы заливки, детализации и т.д.
€ Приветствуется выполнение задания в виде демонстрации,
т.е. c возможностью работы в полноэкранном режиме и немедленным выходом по клавише Escape. Можно напи- сать программу как Screen Saver.
€ Постарайтесь использовать максимум возможностей биб- лиотеки OpenGL. Блики, отражения, спецэффекты за все это обязательно даются дополнительные баллы.
206
Приложение В. Примеры практических заданий
€ Проявите вкус сделайте так, чтобы нравилось прежде всего Вам. Но не увлекайтесь оставайтесь реалистами.
Максимальная оценка 20 баллов. За минимальную реали- зацию требований ставиться 10 баллов. Еще до 10 баллов мож- но получить за использование в работе возможностей OpenGL
(текстур, прозрачности, сферического текстурированяи и пр.),
оригинальных и продвинутых алгоритмов, количество настроек,
а также за эстетичность и красоту сцены.
В.3. Интерактивный ландшафт
Целью данного задания является генерация и вывод с по- мощью OpenGL поверхности ландшафта, а также обеспечение интерактивного передвижения над ней.
Рис. В.3. Пример трехмерного ландшафта.
В.3. Интерактивный ландшафт
207
Обязательная часть задания
Для выполнения обязательной части задания необходимы:
€ генерация трехмерного ландшафта
€ раскраска для придания реалистичности
€ эффект тумана
€ возможность ѕполетаї над ландшафтом (управление)
Более подробное описание:
Генерация ландшафта
Один из вариантов задания поверхности ландшафта зада- ние так называемого ѕполя высотї функции вида z = f(x, y),
которая сопоставляет каждой точке (x, y) плоскости OXY число z
высоту поверхности ландшафта в этой точке. Один из спосо- бов задания функции f табличный, когда функция f представ- ляется матрицей T размера M ЧN, и для целых x и y f = T [x, y],
а для дробных x и y из диапазонов [0..M ?1] и [0..N ?1] соответ- ственно, f вычисляется интерполяцией значений f в ближайших точках плоскости OXY с целыми x и y, а вне указанных диапа- зонов x и y значение функции считается неопределенным.
Допустим, в памяти лежит двухмерный массив со значения- ми матрицы T . Пусть N = M. Если теперь для каждого квадрата
[x, x + 1] Ч [y, y + 1]
, где x и y принадлежат диапазону [0..N ? 2]
построить две грани: ((x, y, T [x, y]), (x+1, y, T [x+1, y]), (x+1, y+
1, T [x+1, y +1]))
и ((x, y, T [x, y]), (x+1, y+1, T [x+1, y+1]), (x, y+
1, T [x, y + 1]))
, то мы
получим трехмерную модель поверхности,
описываемой матрицей Т.
Но каким образом задать массив значений матрицы T ? Один из способов сгенерировать псевдослучайную поверхность с по-
208
Приложение В. Примеры практических заданий мощью фрактального разбиения. Для этого положим размер- ность матрицы T равной 2
N
+ 1
, где N натуральное число.
Зададим некоторые произвольные (псевдослучайные) значения для четырех угловых элементов матрицы . Теперь для каждого из четырех ребер матрицы (это столбцы или строки элемен- тов, соединяющие угловые элементы) вычислим значение эле- мента матрицы T , соответствующего середине ребра. Для этого возьмем среднее арифметическое значений элементов матрицы в вершинах ребра и прибавим к получившемуся значению некото- рое псевдослучайное число, пропорциональное длине ребра. Зна- чение центрального элемента матрицы вычислим аналогично,
только будем брать среднее арифметическое четырех значений элементов матрицы в серединах ее ребер.
Теперь разобьем матрицу на четыре квадратные подматри- цы. Значения их угловых элементов уже определены, и мы мо- жем рекурсивно применить к подматрицам описанную выше процедуру. Будем спускаться рекурсивно по дереву подматриц пока все элементы не будут определены. С помощью подбо- ра коэффициентов генерации псевдослучайной добавки можно регулировать ѕизрезанностьї поверхности. Для реалистичности поверхности важно сделать величину псевдослучайной добавки зависящей от длины текущего ребра с уменьшением размера ребра должно уменьшаться и возможное отклонение высоты его середины от среднего арифметического высот его вершин.
Один из других вариантов использовать изображения в градациях серого для карты высот. (В этом случае ландшафт можно оттекстурировать с помощью соответствующей цветной картинки и линейной генерации текстурных координат).
Внимание: использование NURBS возможно, но не привет- ствуется в силу ограниченности использования NURBS для ре- альных приложений.
В.3. Интерактивный ландшафт
209
Раскраска ландшафта
Чтобы сделать получившуюся модель немного более напоми- нающей ландшафт, ее можно раскрасить. Каждой вершине мож- но сопоставить свой цвет, зависящий от высоты этой вершины.
Например, вершины выше определенного уровня можно покра- сить в белый цвет в попытке сымитировать шапки гор, вершины пониже в коричневый цвет скал, а вершины уровнем еще ни- же в зеленый цвет травы. Значения ѕуровнейї раскраски по- верхности следует подобрать из эстетических соображений.
Освещение ландшафта
Для еще большего реализма и для подчеркивания рельефа осветить модель ландшафта бесконечно удаленным источником света (как бы солнцем).
Цвет вершин можно задавать через glColor совместно с g l C o l o r M a t e r i a l (GL_FRONT, GL_AMBIENT_AND_DIFFUSE) ;
Туман
Чтобы усилить (или хотя бы создать) иллюзию больших раз- меров модели и ее протяженности, можно воспользоваться эф- фектом тумана. Тип тумана (линейный или экспоненциальный)
следует выбрать из индивидуальных эстетических предпочте- ний. Способ создания тумана описан в разделе 5.4.
Управление
Элементарное управление движением камеры по клавиатур- ным ѕстрелочкамї. Нажатие на стрелку ѕвверхї передвиже- ние по направлению взгляда вперед. ѕНазадї по направлению взгляда назад. ѕВлевої, ѕВправої по аналогии, ѕPage Upї, ѕPage
Downї вверх, вниз, соответственно.
210
Приложение В. Примеры практических заданий
В
GLUT'е получать нажатия не алфавит- но-цифровых клавиш можно через функцию glutSpecialFunc(void (*)(int key, int x, int y))
,
где key кон- станта, обозначающая клавишу (см. в glut.h
GLUT_KEY
).
Функция используется аналогично glutKeyboardFunc()
Дополнительная часть
Управление мышью
Движение мыши в горизонтальной плоскости (смещение по оси X) управляет углом поворота направления взгляда в гори- зонтальной плоскости (угол альфа ? [0..2?]). Движение мыши в вертикальной плоскости (смещение по оси Y ) управляет углом поворота направления взгляда в вертикальной плоскости отно- сительно горизонта (угол бета ? [??..?]). Зная оба угла, вектор направления взгляда в мировых координатах вычисляется сле- дующим образом:
direction_z = s i n ( бета ) ;
direction_x = cos ( альфа ) * cos ( бета ) ;
direction_y = s i n ( альфа ) * cos ( бета ) ;
а затем нормализуется.
Вектор направления ѕвбокї вычисляется как векторное про- изведение вектора направления вертикально вверх, то есть век- тора (0, 0, 1) и уже известного вектора направления взгляда.
Вектор направления ѕвверхї вычисляется как векторное про- изведение вектора направления взгляда и вектора направления
ѕвбокї.
Положение камеры в OpenGL можно передать c помощью ко- манды gluLookAt()
. Подсказка: параметр target можно положить равным position + direction
Смещение позиции камеры должно происходить не на фик- сированное расстояние за один кадр, а вычисляться, исходя из
В.3. Интерактивный ландшафт
211
скорости передвижения камеры, и времени, ушедшего на обсчет последнего кадра. Передвижение камеры должно осуществлять- ся в направлении взгляда. Скажем, по левой кнопке мыши
вперед, а по правой назад. Для того, чтобы засечь время,
можно воспользоваться функцией timeGetTime()
, описанной в
ѕmmsystem.hї, и реализованной в библиотеке ѕwinmm.libї (толь- ко для Windows).
#include "mmsystem . h"
void Display ( )
{
int system_time_before_rendering ;
system_time_before_rendering = timeGetTime ( ) ;
RenderFrame ( ) ;
int time_spent_rendering_msec =
timeGetTime ( ) ?system_time_before_rendering ;
}
В GLUT'е для этого есть специальный вызов (аналогично timeGetTime()
):
time = glutGet (GLUT_ELAPSED_TIME) ;
Вода, или нечто на нее похожее
При раскраске ландшафта можно добавить еще один, самый нижний ѕуровеньї уровень воды. Вершины, располагающиеся на этом уровне, можно покрасить в цвет воды предположи- тельно, синий. Для того, чтобы получившиеся ѕводоемыї не вы- глядели продолжением поверхности ландшафта, просто покра- шенным в синий цвет, а
имели плоскую поверхность, при генера- ции поля высот можно установить порог высоты, ниже которого
ѕопускатьсяї вершинам запрещается. Если для элемента матри-
212
Приложение В. Примеры практических заданий цы генерируется значение высоты ниже этого порога, элемент инициализируется пороговым значением.
Объекты
По ландшафту можно раскидать в художественном беспо- рядке от пятидесяти объектов, встречающихся на ландшафте в обычной жизни, например, домов или деревьев. При этом ель считается деревом, а две равнобедренные вытянутые вертикаль- но грани, поставленные на ландшафт крест накрест и покрашен- ные в зеленый цвет, считаются елью.
Отражения в воде
Сделать так, чтобы ландшафт отражался в воде, которая уже должна присутствовать на ландшафте (то есть подразумевается,
что это дополнительное задание является развитием дополни- тельного задания 2). Один из вариантов реализации:
рассчитав текущую матрицу камеры, отразить ее относительно плоскости воды и изображения ландшафта, не выводя при этом грани по- верхности воды. Затем, пользуясь еще не отраженной матрицей камеры, визуализировать грани поверхности воды полупрозрач- ными. Это создаст иллюзию поверхности воды, сквозь которую видно отражение. Затем, опять же с неотраженной матрицей ка- меры, нужно нарисовать сам ландшафт (этот подход является частным случаем описанного в разделе 8.3).
Тени
На этапе раскраски вершин ландшафта (то есть это надо сде- лать один раз, а не каждый кадр) из каждой вершины можно вы- пустить луч, противоположный направлению солнца. Если луч не пересекся с поверхностью ландшафта раскрашивать как
В.3. Интерактивный ландшафт
213
запланировано, если пересекся значит данная вершина ланд- шафта находится в тени, и для нее нужно взять менее интен- сивный цвет. Примечание: реализация теней является задачей повышенной сложности (придется писать нахождение пересече- ний луча с гранями, что в общем случае нетривиально).