
2633
.pdf}
Следующая часть кода описывает формат пикселей (Pixel Format): static PIXELFORMATDESCRIPTOR pfd=
//pfd сообщает Windows каким будет вывод на экран каждого
пикселя
{
sizeof(PIXELFORMATDESCRIPTOR),
//Размер дескриптора данного формата пикселей
1, |
// Номер версии |
PFD_DRAW_TO_WINDOW | // Формат для Окна |
|
PFD_SUPPORT_OPENGL | |
// Формат для OpenGL |
PFD_DOUBLEBUFFER, |
// Формат для двойного буфера |
PFD_TYPE_RGBA, |
// Требуется RGBA формат |
bits, |
// Выбирается бит глубины цвета |
0, 0, 0, 0, 0, 0, |
// Игнорирование цветовых битов |
0, |
// Нет буфера прозрачности |
0, |
// Сдвиговый бит игнорируется |
0, |
// Нет буфера накопления |
0, 0, 0, 0, |
// Биты накопления игнорируются |
32, |
// 32 битный Z-буфер |
0, |
// Нет буфера трафарета |
0 |
// Нет вспомогательных буферов |
PFD_MAIN_PLANE, |
// Главный слой рисования |
0, |
// Зарезервировано |
0, 0, 0, |
// Маски слоя игнорируются |
} |
|
Выбирается формат, который поддерживает OpenGL и двойной буфер, в соответствии с RGBA (Red-красный, Green-зелёный, Blueсиний, Alpha Channel-альфа канал (канал прозрачности)). Затем устанавливается 32 битный Z-буфер (буфер глубины). Остальные параметры или не используются, или не важны (кроме буфера трафарета и (медленного) буфера накопления).
Если во время создания окна не возникло ни одной ошибки, происходит попытка получения Контекста Устройства (DC) OpenGL. Если невозможно получить DC, на экран выскочит сообщение об ошибке и программа завершит работу (функция возвратит false):
if(!(hDC = GetDC(hWnd))) // |
Возможно ли получить Контекст |
Устройства? |
|
{ |
|
KillGLWindow(); |
// Восстановить экран |
MessageBox( NULL, "Can't Create A GL Device Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION );
43
return false; // Вернуть false
}
Если получен Контекст Устройства для нашего OpenGL окна происходит попытка найти формат пикселя, который описан выше. Если Windows не может найти подходящий формат, на экран выскочит сообщение об ошибке и программа завершит работу (функция возвратит false):
if(!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Найден ли подходящий формат пикселя?
{ |
|
KillGLWindow(); |
// Восстановить экран |
MessageBox(NULL, "Can't Find A Suitable PixelFormat.", "ERROR", |
|
MB_OK | MB_ICONEXCLAMATION); |
|
return false; |
// Вернуть false |
} |
|
Выбирается формат, который поддерживает OpenGL и двойной буфер, в соответствии с RGBA (Red-красный, Green-зелёный, Blueсиний, Alpha Channel-альфа канал (канал прозрачности)). Затем устанавливается 32 битный Z-буфер (буфер глубины). Остальные параметры или не используются, или не важны (кроме буфера трафарета и (медленного) буфера накопления).
Если Windows нашёл соответствующий формат, происходит его установка. Если же он не может быть установлен, на экран выскочит сообщение об ошибке и программа завершит работу (функция возвратит false):
if(!SetPixelFormat(hDC,PixelFormat,&pfd))
//Возможно ли установить Формат Пикселя?
{
KillGLWindow(); |
// Восстановить экран |
MessageBox(NULL, |
"Can't Set The PixelFormat.", "ERROR", |
MB_OK | MB_ICONEXCLAMATION); |
|
return false; |
// Вернуть false |
} |
|
Если формат был корректно установлен, получается Контекст Рендеринга. Если это не возможно, на экран выскочит сообщение об ошибке и программа завершит работу (функция возвратит false):
if( !( hRC = wglCreateContext( hDC ) ) )
//Возможно ли установить Контекст Рендеринга?
{
KillGLWindow(); |
// Восстановить экран |
MessageBox(NULL, |
"Can't Create A GL Rendering Context.", |
"ERROR", MB_OK | MB_ICONEXCLAMATION);
44
return false; // Вернуть false
}
Если до сих пор не возникало ошибок, т. е. созданы и Контекст Устройства, и Контекст Рендеринга, далее надо сделать Контекст Рендеринга активным. Если этого не получится, на экран выскочит сообщение об ошибке и программа завершит работу (функция возвратит false):
if( !wglMakeCurrent( hDC, hRC ) )
// Попробовать активировать Контекст Рендеринга
{ |
|
KillGLWindow(); |
//Восстановить экран |
MessageBox(NULL, "Can't Activate The GL Rendering Context.", |
|
"ERROR", MB_OK | MB_ICONEXCLAMATION); |
|
return false; |
// Вернуть false |
} |
|
Если всё прошло правильно, и OpenGL окно было создано, показывается окно, устанавливается на передний план (присвоив более высокий приоритет) и затем устанавливается фокус для этого окна, потом вызывается ReSizeGLScene, передавая ширину и высоту экрана
для настройки перспективы для нашего OpenGL экрана: |
|
|||
ShowWindow(hWnd, SW_SHOW); |
// Показать окно |
|
||
SetForegroundWindow(hWnd); |
// |
Слегка |
повысим |
|
приоритет |
|
|
|
|
SetFocus(hWnd); |
// Установить |
фокус клавиатуры |
на наше |
|
окно |
|
|
|
|
ReSizeGLScene(width, height); |
// |
Настройка перспективы |
||
для OpenGL экрана. |
|
|
|
|
Далее выполненяем функции InitGL() для настройки освещения и текстуры, также возможно сделать дополнительные проверки на ошибки в InitGL(), и передать true (всё OK) или false:
if( !InitGL() )
//Инициализация только что созданного окна
{
KillGLWindow(); |
// Восстановить экран |
MessageBox(NULL, |
_T("Initialization Failed."), _T("ERROR"), |
MB_OK | MB_ICONEXCLAMATION); |
|
return false; |
// Вернуть false |
}
Если программа дошла до этого момента, логично предположить, что создание окна закончилось успехом, далее происходит возврат true в WinMain(), что сообщает о том, что не возникло никаких ошибок:
return true;
45
}
То есть программа не завершается, а благополучно продолжает работу.
Далее будет происходить обработка сообщений для окна. LRESULT CALLBACK WndProc(HWND hWnd,
// Дескриптор нужного окна
UINT uMsg, |
// Сообщение для этого окна |
WPARAM wParam, |
// Дополнительная информация |
LPARAM lParam) |
// Дополнительная информация |
{
Когда регистрируется класс окна, указывается функция обработки сообщений.
Код ниже устанавливает uMsg как параметр, с которым будут сравниваться все блоки. uMsg будет хранить название сообщения, с
которым будет работать. |
|
|
|
|
switch (uMsg) |
|
// Проверка сообщения для окна |
||
{ |
|
|
|
|
Если окно минимизировано, переменная active будет равна false, |
||||
если окно активно, переменная active будет равна true: |
|
|||
case WM_ACTIVATE: |
// |
Проверка сообщения |
активности |
|
окна |
|
|
|
|
{ |
|
|
|
|
if( !HIWORD( wParam ) ) |
// Проверить состояние минимизации |
|||
{ |
|
|
|
|
active = true; |
|
// Программа активна |
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
active = false; |
|
// Программа теперь не активна |
||
} |
|
|
|
|
return 0; |
// Возвращаемся в цикл обработки сообщений |
|||
} |
|
|
|
|
Если возникло |
сообщение |
WM_SYSCOMMAND |
(системная |
команда), то сравнивается состояние по параметру wParam. Если wParam – SC_SCREENSAVE или SC_MONITORPOWER, то или запускается скринсейвер (программа сохранения экрана) или монитор пытается перейти в режим сбережения энергии. Возвращая ноль – предотвращается наступлению обоих этих событий:
case WM_SYSCOMMAND: |
// Перехват системной команды |
{ |
|
switch(wParam) |
// Останавка системный вызов |
{ |
|
46
case SC_SCREENSAVE: // Пытается ли запустится скринс.?
case SC_MONITORPOWER: |
// Пытается ли монитор перейти в |
режим сбережения энергии? |
|
return 0; |
// Предотвращаем это |
} |
|
break; |
// Выход |
} |
|
Если uMsg – WM_CLOSE окно будет закрыто. Переменная done будет установлена в true, главный цикл в WinMain() будет остановлен и
программа будет закрыта: |
|
case WM_CLOSE: |
// Получено сообщение о закрытии? |
{ |
|
PostQuitMessage(0); |
// Отправка сообщения о выходе |
return 0; |
// Вернуться назад |
} |
|
Если произошло нажатие кнопки (на клавиатуре) происходит распознавание, какая клавиша это была, считав wParam:
case WM_KEYDOWN: |
// Была ли нажата кнопка? |
{ |
|
keys[wParam] = true; |
// Если так, этой ячейке присваивается |
true |
|
return 0; |
// Возврат |
} |
|
Таким образом, можно считать этот массив позже и найти какая клавиша была нажата. Это позволяет отследить нажатия сразу несколько клавиш одновременно.
Всякий раз, когда изменяются размеры окна uMsg в конечном счёте будет иметь значение WM_SIZE. Происходит считывание LOWORD и HIWORD (младшее и старшее слова) переменной lParam для того, чтобы узнать новые высоту и ширину окна. Происходит передача аргументов функции ReSizeGLScene(). OpenGL сцена перерисуется с новой шириной и высотой:
case WM_SIZE: |
// Изменены размеры OpenGL окна |
{ |
|
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); |
|
// Младшее слово=Width, старшее слово=Height |
|
return 0; |
// Возврат |
} |
|
}
Любое сообщение, которое не проверено, будет передано в качестве фактического параметра функции DefWindowProc для того, чтобы Windows могла его обработать:
47
//пересылаем все необработанные сообщения
DefWindowProc
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
Можно сказать, что это входная точка в Windows приложение, где вызывается операция создания окна:
int WINAPI WinMain(HINSTANCE hInstance,
//Дескриптор приложения
HINSTANCE hPrevInstance,
//Дескриптор родительского приложения
LPSTR lpCmdLine,
//Параметры командной строки
int nCmdShow)
// Состояние отображения окна
{
Далее инициализируются две переменные:
MSG msg; // Структура для хранения сообщения Windows BOOL done = false; // Логическая переменная для выхода из
цикла
Переменная msg будет использоваться для того, чтобы проверить существует ли какое-нибудь ожидающее обработки сообщение. Переменная done при старте будет равна false. Это означает, что программа не закончила своего выполнения. Пока done равно false программа будет продолжать выполнение. Как только done изменит значение с false на true – программа закончит выполнение.
Эта секция часть кода является полностью дополнительной. Она вызовет всплывающее окно, которое спросит, надо ли запустить приложение в полноэкранном режиме. Если пользователь кликнет на кнопке "NO", переменная изменится с true (по умолчанию) на false и программа запустится в оконном режиме:
// Спрашивает пользователя, какой режим экрана он предпочитает if(MessageBox( NULL, "Хотите ли Вы запустить приложение в
полноэкранном режиме?", |
"Запустить в полноэкранном режиме?", |
MB_YESNO | MB_ICONQUESTION) == IDNO) |
|
{ |
|
fullscreen = false;// |
Оконный режим |
}
Далее задается, как будет создано окно: // Создани OpenGL окна
if(!CreateGLWindow("Проект OpenGL окно", 1024, 768, 32, fullscreen))
{
48
return 0; |
// Выйти, если окно не может быть создано |
} |
|
Передается |
заголовок, ширина, высота, глубина цвета и true |
(полноэкранный режим) или false (оконный режим) функции CreateGLWindow. Если окно не будет создано по какой бы то ни было причине, CreateGLWindow вернёт false и наша программа немедленно завершиться (return 0).
Далее стартует новый цикл – пока done равно false цикл будет
повторяться: |
|
while(!done) |
// Цикл продолжается, пока done не равно true |
{ |
|
Сначала проверяется стоит ли в очереди какое-нибудь сообщение, и используя PeekMessage(), это делается без остановки выполнения нашей программы:
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Есть ли в очереди какое-нибудь сообщение?
{
В следующей части кода проверяется, есть ли в очереди
сообщение о выходе: |
|
|
|
if( msg.message == WM_QUIT ) |
// |
Получено сообщение о |
|
выходе? |
|
|
|
{ |
|
|
|
done = true; |
//Если так, done=true |
||
} |
|
|
|
else |
// Если нет, обработка сообщения |
||
{ |
|
|
|
Если текущее |
сообщение WM_QUIT, |
это повод для вызова |
PostQuitMessage( 0 ), установки переменной done в true и завершения программы.
Если сообщение в очереди не сообщение о выходе, преобразовывается сообщение, затем отсылаемт его так, чтобы WndProc() или Windows могли работать с ним:
TranslateMessage( &msg ); |
// Перевод сообщения |
|
DispatchMessage( &msg ); |
// Отсылка сообщения |
|
} |
} |
|
|
|
|
else |
// Если нет сообщений |
{
Если не было сообщений, происходит рисование сцены, то первая строчка кода ниже проверяет активно ли окно, если нажата кнопка ESC, переменная done устанавливается в true, приводя к выходу из программы:
49
// Прорисовка сцены. |
|
if( active ) |
// Активна ли программа? |
{ |
|
if(keys[VK_ESCAPE]) |
// Была ли нажата клавиша ESC? |
{ |
|
done = true; |
// ESC говорит об останове выполнения |
программы |
|
} |
|
else |
// Ообновим экран. |
{
Если программа активна и не нажата ESC визуализируется сцена и
меняется буфер: |
|
|
DrawGLScene(); |
// Рисование сцены |
|
SwapBuffers( hDC ); |
// |
Двойная буферизация |
} |
|
|
}
Используя двойную буферизацию происходит рисование всего того, что происходит на скрытом экране (второй буфер) так, чтобы пользователь не мог видеть этого. Когда буфер изменяется, экран (первый буфер), который видит пользователь, становится скрытым, а скрытый – становится видимым. Таким образом, не видно саму прорисовку сцены, а только результат визуализации.
Следующий код позволяет изменять полноэкранный режим на оконный и обратно, нажимая клавишу F1:
if(keys[VK_F1]) |
// Была ли нажата F1? |
{ |
|
keys[VK_F1] = false; |
// Если так, замена значения ячейки |
массива на false |
|
KillGLWindow(); |
// Разрушение текущего окна |
fullscreen = !fullscreen; |
// Переключение режима |
// Пересоздание OpenGL окна
if( !CreateGLWindow( _T("Проект OpenGL структура"), 1024, 768,
32, fullscreen ) ) |
|
{ |
|
return 0; |
// Выход, если это невозможно |
} |
} |
|
|
} |
|
}
Если переменная done больше не false, программа завершается.
50

Далее корректно разрушается OpenGL окно, чтобы всё было освобождено и производится выход из программы:
// Shutdown |
|
KillGLWindow(); |
// Разрушение окна |
return ( msg.wParam ); |
// Выход из программы |
}
В результате получаем OpenGL окно (рис.1), которое следует использовать для выполнения всех последующих программ.
Рис. 1. OpenGL окно
Контрольные вопросы
1.Что такое OpenGL?
2.В чем заключается основной принцип работы OpenGL?
3.Для каких целей предназначена библиотека GLU?
4.Поясните такие основные понятия компьютерной графики, как: пиксель, рендеринг и битовая плоскость.
5.Как в OpenGL осуществляется организация и управление
окном?
6.Поясните следующие понятия: Контекст Рендеринга и Контекст Устройства.
7.Поясните смысл переменной fullscreen.
8.Как реализовать возможность изменения размеров OpenGL сцены всякий раз, когда будут изменены размеры окна?
9.Как и зачем настраивают экран для перспективного вида?
10.Как установить цвет для очистки экрана, включить глубину буфера, плавное сглаживание цветов?
11.Для чего предназначен буфер глубины?
12.Какую задачу выполняет KillGLWindow?
51
13.Что такое Message Box?
14.Как и для чего необходимо предусмотреть возможность корректным образом уничтожить окно и открыть другое без получения сообщения об ошибке?
15.Как задать значения координат левого верхнего и правого нижнего угла прямоугольника (окна)?
16.Как дать возможность пользователю осуществлять запуска программы в полноэкранном или в оконном режимах?
17.Как задается массив, используемый для операций с клавиатурой?
18.Что означает NULL в функции MessageBox() ?
19.Перечислите параметры, в которых нуждается
CreateWindowEx() при создании окна.
20.Объясните различия между функциями вызова сообщений
PeekMessage() и GetMessage().
21.В какой секции кода происходит создание окна и проверка создано ли оно должным образом?
22.Какая секции кода описывает Формат Пикселей (Pixel
Format).
2.2. Прорисовка примитивов
Целью программы, приведенной в качестве примера, является создания фигур – треугольник и квадрат при помощи функций
GL_TRIANGLES и GL_QUADS.
Для создания приложения используется предыдущий код, только добавляется часть в функцию DrawGLScene.
Следует заменить функцию DrawGLScene следующим кодом, или просто добавить те строки, которые там отсутствуют:
GLvoid DrawGLScene(GLvoid)
{
//Очистка экрана и буфера глубины glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); // Сброс просмотра
Вызов функции gLoadIdentity() устанавливает начало системы координат в центр экрана, причем ось X идет слева направо, ось Y вверх и вниз, а ось Z к и от наблюдателя. Центр OpenGL экрана находится в точке 0, 0, 0. Координаты, расположенные слева, снизу и вглубь от него, имеют отрицательное значение, расположенные справа, сверху и по направлению к наблюдателю – положительное.
52