Подготовка
окна
Вы помните,
что подготовку контекста передачи OpenGL надо рассматривать как некий обязательный
ритуал, в котором порядок действий определен. В этой процедуре выделяют следующие
шаги:
- установка стиля окна;
- обработка сообщения WM_ERASEBACKGROUND
и отказ от стирания фона;
- установка pixel-формата;
- создание контекста устройства
(HDC) и контекста передачи (HGLRC);
- специфическая обработка
сообщения WM_SIZE;
- обработка сообщения WM_PAINT;
- освобождение контекстов
при закрытии окна.
Как было отмечено
ранее, окнам, которые в своей клиентской области используют контекст передачи
OpenGL, при создании следует задать биты стиля WS_CLIPCHILDREN и ws_CLiPSiBLiNGS.
Сделайте это внутри существующего тела функции PreCreateWindow класса cocview,
добавив нужные биты стиля к тем, что устанавливаются в заготовке:
BOOL
COGView::PreCreateWindow(CREATESTRUCT& cs)
{
//======
Добавляем биты стиля, нужные OpenGL
cs.style
|= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
return
CView::PreCreateWindow(cs);
}
Вы помните,
что окно OpenGL не должно позволять Windows стирать свой фон, так как данная
операция сильно тормозит работу конвейера. В связи с этим введите в функцию
обработки WM_ERASEBKGND код, сообщающий системе, что сообщение уже обработано:
BOOL
COGView::OnEraseBkgnd(CDC* pDC)
{
return
TRUE;
}
Окно OpenGL
имеет свой собственный формат пикселов. Нам следует выбрать и установить подходящий
формат экранной поверхности в контексте устройства HDC, а затем создать контекст
передачи изображения (HGLRC). Для описания формата пикселов экранной поверхности
используется структура PIXELFORMATDESCRIPTOR. Выбор формата зависит от возможностей
карты и намерений разработчика. Мы зададим в полях этой структуры такие настройки:
- глубину цвета — 24;
- тип буферизации — двойной;
- схему образования цвета
RGBA;
- количество бит для буфера
глубины — 32;
- поддержку регулировки
прозрачностью и другие специфические настройки выключим.
В функцию OnCreate
введите код подготовки окна OpenGL. Работа здесь ведется со структурой PIXELFORMATDESCRIPTOR.
Кроме того, в ней создается контекст m_hRC и устанавливается в качестве текущего:
int
COGView::OnCreate(LPCREATESTROCT
IpCreateStruct)
{
if
(CView::OnCreate(IpCreateStruct) == -1)
return
-1;
PIXELFORMATDESCRIPTOR
pfd = // Описатель формата
{
sizeof(PIXELFORMATDESCRIPTOR),
// Размер структуры
1,
// Номер версии
PFD_DRAW_TO_WINDOW
| // Поддержка GDI
PFD_SUPPORT_OPENGL
| // Поддержка OpenGL
PFD_DOUBLEBUFFER,
// Двойная буферизация
PFD_TYPE_RGBA,
// Формат RGBA, не палитра
24,
// Количество плоскостей
//в
каждом буфере цвета
24,
0, // Для компонента Red
24,
0, // Для компонента Green
24,
0, // Для компонента Blue
24,
0, // Для компонента Alpha
0,
// Количество плоскостей
//
буфера Accumulation
0,
// То же для компонента Red
0,
// для компонента Green
0,
// для компонента Blue
0,
// для компонента Alpha
32,
// Глубина 2-буфера
0,
// Глубина буфера Stencil
0,
// Глубина буфера Auxiliary
0,
// Теперь игнорируется
0,
// Количество плоскостей
0,
// Теперь игнорируется
0,
// Цвет прозрачной маски
0
// Теперь игнорируется };
//======
Добываем дежурный контекст
m_hdc
= ::GetDC(GetSafeHwnd());
//======
Просим выбрать ближайший совместимый формат
int
iD = ChoosePixelForraat(m_hdc, spfd);
if
( !iD )
{
MessageBoxC'ChoosePixelFormat:
:Error") ;
return
-1;
}
//======
Пытаемся установить этот формат
if
( ISetPixelFormat (m_hdc, iD, Spfd) )
{
MessageBox("SetPixelFormat::Error");
return
-1;
}
//======
Пытаемся создать контекст передачи OpenGL
if
( !(m_hRC = wglCreateContext (m_hdc)))
{
MessageBox("wglCreateContext::Error");
return
-1;
}
//======
Пытаемся выбрать его в качестве текущего
if
( IwglMakeCurrent (m_hdc, m_hRC))
{
MessageBox("wglMakeCurrent::Error");
return
-1;
//======
Теперь можно посылать команды OpenGL
glEnable(GL_LIGHTING);
// Будет освещение
//======
Будет только один источник света
glEnable(GL_LIGHTO);
//======
Необходимо учитывать глубину (ось Z)
glEnable(GL_DEPTH_TEST);
//======
Необходимо учитывать цвет материала поверхности
glEnable(GL_COLOR_MATERIAL);
//======
Устанавливаем цвет фона .
SetBkColor
() ;
//======
Создаем изображение и запоминаем в списке
DrawScene
() ;
return
0;
}
Контекст передачи
(rendering context) создается функцией wglCreateContext с учетом выбранного
формата пикселов. Так осуществляется связь OpenGL с Windows. Создание контекста
требует, чтобы обычный контекст существовал и был явно указан в параметре wglCreateContext.
HGLRC использует тот же формат пикселов, что и НОС. Мы должны объявить контекст
передачи в качестве текущего (current) и лишь после этого можем делать вызовы
команд OpenGL, которые производят включение некоторых тумблеров в машине состояний
OpenGL. Вызов функции DrawScene, создающей и запоминающей изображение, завершает
обработку сообщения. Таким образом, сцена рассчитывается до того, как приходит
сообщение о перерисовке WM_PAINT. Удалять контекст передачи надо после отсоединения
его от потока. Это делается в момент, когда закрывается окно представления.
Введите в тело заготовки OnDestroy следующие коды:
void
COGView::OnDestroy(void)
{
//======
Останавливаем таймер анимации
KillTimer(1);
//======
Отсоединяем контекст от потока
wglMakeCurrent(0,
0); //====== Удаляем контекст
if
(m_hRC)
{
wglDeleteContext(m_hRC);
m_hRC
= 0;
}
CView::OnDestroy()
;
}
Так же как
и в консольном проекте OpenGL, обработчик сообщения WM_SIZE должен заниматься
установкой прямоугольника просмотра (giviewport) и мы, так же как и раньше,
зададим его равным всей клиентской области окна. -Напомним, что конвейер OpenGL
использует эту установку для того, чтобы поместить изображение в центр окна
и растянуть или сжать его пропорционально размерам окна. Кроме того, в обработке
onSize с помощью матрицы проецирования (GL_PROJECTION) задается тип проекции
трехмерного изображения на плоское окно. Мы выбираем центральный или перспективный
тип проецирования и задаем при этом угол зрения равным m_AngleView. В конструкторе
ему было присвоено значение в 45 градусов:
void
COGView::OnSize(UINT
nType, int ex, int cy)
{
//======
Вызов родительской версии
CView::OnSize(nType,
ex, cy) ;
//======
Вычисление диспропорций окна
double
dAspect = cx<=cy ? double(cy)/ex : double(ex)/cy;
glMatrixMode
(GL_PROJECTION) ;
glLoadldentity()
;
//======
Установка режима перспективной проекции
gluPerspective
(m_AngleView, dAspect, 0.01, 10000.);
//======
Установка прямоугольника просмотра
glViewport(0,
0, сх, су);
}