Главная » Статьи » Графика (OpenGL)

Инициализация OpenGL в Windows.
«Инициализация OpenGL в Windows»

Наконец-то у меня выдалось пара выходных, и я могу заняться дальнейшим обустройством своей странички, а самое главное начать помогать тем, кто хочет научиться создавать собственные компьютерные 3D-игры(я кстати тоже...:)), либо просто интересуется данной темой, не важно почему, главное, что это кому-то интересно!!!

Итак, приступим. Сегодня мы просто создадим простое мастдайное окошко, где великий и могучий OpenGL будет рисовать простой "проволочный" каркас шарика... В последствии, будем надеяться, мы сможем в этом окошке наблюдать нечто покруче,(может даже покруче Дум3 :)).
Многим этот урок покажется бессмысленным, но как я недавно смог убедиться, у некоторых все еще с этим возникают проблемы, несмотря на многочисленные сетевые ресурсы, иногда очень толковые.
Да, чуть не забыл самое главное! Кодить мы будем на С++ (MS VS6.0), графическая библиотека которую будем использовать - это OpenGL!!! Исходный код максимально прокомментирован и поэтому вопросов, по идее, возникнуть не должно. По себе знаю, чем больше комментариев, тем понятнее. Ну а если все-таки вопросы возникнут, - все нужные ссылки внизу, по возможности отвечу...

НАЧАЛИ КОДИТЬ: Первым делом создадим пустой проект приложения Win32(никаких MFC), и включим в него все необходимые заголовочные файлы, без которых нам не удастся откомпилировать нашу программу...

#include //Присутствует во всех приложениях Win32
#include //Наша OpenGL библиотека
#include //Библиотека утилит OpenGL
#include //И еще одна, с ее помощью можно рисовать стандартные объекты (сферы, кубы, и др.)


Следуюие строчки подключают статические библиотеки OpenGL.lib, Glu32.lib b Glaux.lib которые также необходимы для успешной компиляции проекта...

#pragma comment (lib, "OpenGL32.lib")
#pragma comment (lib, "Glu32.lib")
#pragma comment (lib, "Glaux.lib")


Вместо этих строчек можно зайти в меню Project -> Settings -> закладка Link -> поле Object/library modules: добавить в конец строки через пробел (OpenGL32.lib Glu32.lib Glaux.lib).
Далее объявляем необходимые переменные...

static GLint resW = 640, resH = 480; // Размер окна
bool isFullscr = false; // Полноэкранный режим (по умолчанию - нет)
static GLdouble cntr; // Счетчик. Отвечает за вращение шарика
BOOL keys[256]; // Массив для процедуры обработки клавиатуры

HGLRC hGLRC; // Контекст рендеринга OpenGL
HDC hDC; // Контекст устройства Windows

static GLfloat light1_position[] = {0.0, 0.0, 3.0, 1.0}; //Позиция источника света


Надеюсь все предельно ясно.
Далее напишем функцию инициализации OpenGL, в которой мы инициализируем стандартное освещение, установим метод закраски полигонов, и выполним еще несколько функций, о которых мы более подробно поговорим в следующих уроках, а пока из всей этой функции следует обратить внимание на одну единственную функцию: glEnable (GL_DEPTH_TEST); эта функция включает тест глубины, это нужно для того, чтобы при рисовании нескольких объектов, те объекты которые спереди (ближе к точке обзора) перекрывали те, которые находятся за ними, иначе сами представьте что случится... например чайник стоит за ящиком, но тем не менее рисуется спереди :) прикольно, но нам это не нужно...

GLvoid InitGL() // Вызвать после создания окна GL
{
// LoadGLTextures();
GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
glClearColor (0.5, 0.5, 0.5, 0.0);
//*********** Инициализация освещения *********************
GLfloat light_position[] = {0.0, 0.0, 10.0, 1.0};
glLightf (GL_LIGHT0, GL_SPOT_EXPONENT, 20.0);
glLightfv (GL_LIGHT0, GL_POSITION, light_position);
glLightf (GL_LIGHT0, GL_SPECULAR, 128);
glEnable (GL_LIGHTING);
glEnable (GL_LIGHT0);
//**********************************************************

glShadeModel (GL_SMOOTH); // Метод закраски: СГЛАЖЕНЫЙ
glEnable (GL_DEPTH_TEST); // Включаем тест глубины
// ВКЛ. АВТОМАТИЧЕСКИЙ ПЕРЕСЧЕТ НОРМАЛЕЙ
glEnable (GL_AUTO_NORMAL);
glEnable (GL_NORMALIZE);
// ВКЛ. ОТСЕЧЕНИЕ ЗАДНИХ ГРАНЕЙ
glEnable (GL_CULL_FACE); // Включаем отсечение граней
glCullFace (GL_BACK); // Указываем, что отсекать будем задние грани
}


Теперь началось самое интересное... Функция перерисовки, обновления, как хотите ее называйте, а выполняет она следующее:
устанавливает размер порта просмотра(это то место где мы будем рисовать)который не всегда может быть равен размеру окна widowsa, но в нашем случае их размеры равны. Затем устанавливаем и настраиваем матрицу проекции с помощью ф-ции gluPerspective которая не является стандартной для OpenGL а включена в отдельную библиотеку утилит OpenGL, об этом можно догадаться по трем первым буквам ф-ции(glu - gl utility). Далее мы устанавливаем матрицу модели-вида, и практически все готово для того, чтобы рисовать...

void reshape(int width,int height)
{
glViewport(0,0,width,height); // Устанавливаем размер порта просмотра
glMatrixMode( GL_PROJECTION ); // Переключаемся на матрицу проекции
glLoadIdentity(); // Сбрасываем матрицу проекции путем установки единичной матрицы
gluPerspective (50.0, (GLfloat)width/(GLfloat)height, 1.0, 100.0); // настраиваем нашу камеру
// gluPerspective (угол_обзора, отношение_ширины_к_высоте_порта_просмотра, ближняя_плоскость_отсечения, дальняя_плоскость_отсечения);
// gluLookAt( 0,0,3, 0,0,0, 0,1,0 ); // попробуйте...:)
glMatrixMode( GL_MODELVIEW ); // Переключаемся на матрицу модели-вида
glLoadIdentity(); // Сбрасываем матрицу модели-вида путем установки единичной матрицы
}


И сама функция рисования, в которой нас пока интересуют первые 2е строчки. Первая говорит OpenGL, что нужно очистить буфер цвета и буфер глубины. Эти операции выполняются перед рисованием каждого кадра! Очищая буфер цвета, мы стираем то, что уже нарисовали и отобразили на экране, и затем рисуем следующий кадр. Обновляя буфер глубины мы следим за тем, чтоб на экране рисовалось то что мы видим, и не рисовалось то, что от нас скрыто(ого как загнул)...

void display()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очищаем буфера
glLoadIdentity(); // Грузим единичную матрицу

glPushMatrix();
cntr +=0.1;
glTranslatef (0.0, 0.0, -5.0);
glRotated(cntr, 0.0f, 1.0f, 0.0f);

auxWireSphere(1.0);

if (cntr >= 360.0f) cntr =0.0f;
glPopMatrix();

glFlush(); // Досрочное завершение рисования (ф-я необязательна)
}


НУ ВОТ... Наконец мы добрались до самой изюминки данного урока! Это две функции, которые отвечают за создание окна, его вид, наличие отсутствие кнопочек, контекстного меню, получение и обработку сообщений(команд) операционной системы и т.д. и т.п.
Функция WindowFunc отвечает за инициализацию формата пикселей, и обработку сообщений(команд) операционной системы. Вот ее код...

LRESULT CALLBACK WindowFunc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
RECT Screen;

GLuint PixelFormat;
static PIXELFORMATDESCRIPTOR pfd=
{
sizeof(PIXELFORMATDESCRIPTOR), //Размер структуры
1, // Номер версии
PFD_DRAW_TO_WINDOW | // Рисовать будем в окно
PFD_SUPPORT_OPENGL | // Установим поддержку OpenGL
PFD_DOUBLEBUFFER, // Выберем двойной буфер
PFD_TYPE_RGBA, // Режим цветности - RGBA
32, // Выберем 32 битную глубину цвета
0, 0, 0, 0, 0, 0, // Игнорирование цветовых битов
0, // Нет буфера прозрачности
0, // Игнорируем сдвиговый бит
0,//---------------------// Нет буфера аккумуляции
1, 1, 1, 1, // Биты аккумуляции игнорируются
32, // 32 битный Z-буфер (буфер глубины)
0, // Нет буфера трафарета
0, // Нет вспомогательных буферов
PFD_MAIN_PLANE, // Рисовать в главную плоскость(слой)
0, // Резерв
0, 0, 0 // Маски слоя игнорируются
};

switch(message)
{
// сообщение WM_CREATE приходит
// один раз при создании окна
case WM_CREATE:
hDC = GetDC(hWnd); // Получить контекст устройства для окна

PixelFormat = ChoosePixelFormat(hDC, &pfd);// Найти ближайшее совпадение для нашего формата пикселов
if (!PixelFormat) // Если не удалось то...
{
// Выводим сообщение об ошибке
MessageBox (0, "Не удается найти подходящий PixelFormat!", "Error", MB_OK | MB_ICONERROR);
SendMessage(hWnd, WM_DESTROY, wParam, lParam);// Это сообщение говорит, что программа должна завершится
break; // Предотвращение повтора кода
}

SetPixelFormat (hDC, PixelFormat, &pfd); // Теперь нужно установить наш формат пикселов
if (!SetPixelFormat (hDC, PixelFormat, &pfd)) // Если не удалось то...
{
// Выводим сообщение об ошибке
MessageBox (0, "Не удается установить PixelFormat!","Ошибка!!!", MB_OK | MB_ICONERROR);
// Уничтожаем окно!
SendMessage(hWnd, WM_DESTROY, wParam, lParam);
break;
}

hGLRC = wglCreateContext (hDC); //Захватывает Контекст Рендеринга и сохраняет его в переменной hGLRC
if (!hGLRC) // Захватить не удалось...
{
// Выводим сообщение об ошибке
MessageBox (0, "Не удается создать контекст рендеринга OpenGL!","Ошибка!!!", MB_OK | MB_ICONERROR);
// Уничтожаем окно!
SendMessage(hWnd, WM_DESTROY, wParam, lParam);
break;
}

wglMakeCurrent(hDC, hGLRC); // делаем его текущим
if (!wglMakeCurrent(hDC, hGLRC)) // Если не получилось...
{
MessageBox (0, "Не удается активировать контекст рендеринга GLRC!", "Ошибка!!!", MB_OK | MB_ICONERROR);
// Уничтожаем окно!
SendMessage(hWnd, WM_DESTROY, wParam, lParam);
break;
}
// Все... окошко создано...
InitGL (); // Инициализируем наш GL
break;

// это сообщение приходит при уничтожении окна
case WM_DESTROY: // Уничтожает окно
case WM_CLOSE: //-|-|-|-|-|-|-
ChangeDisplaySettings (NULL, 0);// Восстанавливает первоначальное разрешение(видеорежим)
if(hDC) wglMakeCurrent(hDC, NULL); // Освобождаем контекст рендеринга...
if(hGLRC) wglDeleteContext(hGLRC); // ...и удаляем его
if(hWnd) ReleaseDC(hWnd, hDC); // Рвем с ним всякие связи.
PostQuitMessage(0); // Убиваем окно!
break;

case WM_SIZE:
GetClientRect (hWnd, &Screen);// Создает область рисования и возвращает высоту и ширину окна
reshape(Screen.right, Screen.bottom );
break;

case WM_SYSKEYDOWN:
case WM_KEYDOWN: // Если клавиша нажата то...
keys[wParam] = TRUE; // В массив keys[key number] заносится TRUE
break;

case WM_SYSKEYUP:
case WM_KEYUP: // Если клавиша отпущена то...
keys[wParam] = FALSE; // В массив keys[key number] заносится FALSE
break;

default: // Реакция на все остальные сообщения ОС...
return DefWindowProc(hWnd,message,wParam,lParam); // ...по умолчанию
}
// если где-то что-то не так...
return 0; // ...возвращаем ошибку
}


WM_CREATE, WM_DESTROY, WM_SIZE и т.д. - это все сообщения, которые ОС посылает нашему окну все время. И таких команд очень много, а мы затронули лишь необходимый минимум. Полный список находится в файле windows.h, будет интересно - загляните.
ИДЕМ ДАЛЬШЕ...
Функция, с которой начинается выполнение любой Win32-программы! Это функция WinMain! Она отвечает за создание окна, его свойства и все, что с ним связано... В общем название функции само за себя говорит!
Давайте на нее поглядим...

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInst,
LPSTR str,int nWinMode)
{
MSG msg; // Структура сообщения Windows
WNDCLASS wc; //Структура класса Windows для установки типа окна
HWND hWnd; //Сохранение дескриптора окна

wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Стиль нашего окна
wc.lpfnWndProc = WindowFunc; // Определяем ф-цию ответственную за обработку сообщений ОС
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL; // иконка нашего окошка (по умолчанию)
wc.hCursor = LoadCursor (NULL, IDC_ARROW); // Курсор который будет отображаться при наведении на наше окошко (стрелка)
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "OpenGLWinClass"; // Определяем название класса окна

if (!RegisterClass(&wc)) // Если класс зарегистрировать не удалось...
{
MessageBox(0, "Ошибка при регистрации класса окна!","Ошибка!!!", MB_OK | MB_ICONERROR);
return FALSE;
}

if(isFullscr) // Если режим полноэкранный - создаем окно с соответствующими параметрами
{
// создаем окно
hWnd = CreateWindow(
"OpenGLWinClass", // название класса окна
"УРОК 01 >OpenGL is COOL", // Заголовок окна
WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, // Стиль (вид) окна
0, 0,
resW, resH, // Размеры окна
NULL, NULL, hInstance, NULL);
}
else // Иначе устанавливаем наше окошко, его вид и свойства
{
// создаем окно
hWnd = CreateWindow(
"OpenGLWinClass", // название класса окна
"УРОК 01 >OpenGL is COOL", // Заголовок окна
WS_OVERLAPPEDWINDOW | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE | WS_SIZEBOX, // Стиль (вид) окна
0, 0,
resW, resH, // Размеры окна
NULL, NULL, hInstance, NULL);
}

if (!hWnd) // если создать окно не удалось...
{
MessageBox(0, "Ошибка при создании окна!","Ошибка!!!", MB_OK | MB_ICONERROR);
return FALSE;
}

if(isFullscr)
{
DEVMODE dmScreenSettings; // Режим работы устройства (видяхи)
memset (&dmScreenSettings, 0, sizeof(DEVMODE));// Выделение памяти для хранения установок
dmScreenSettings.dmSize = sizeof(DEVMODE); // Размер структуры Devmode
dmScreenSettings.dmPelsWidth = resW; // Ширина экрана
dmScreenSettings.dmPelsHeight = resH; // Высота экрана
dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;// Режим Пиксела
ChangeDisplaySettings (&dmScreenSettings, CDS_FULLSCREEN);// Переключение в полный экран
}
//else ChangeDisplaySettings (&dmScreenSettings, CDS_GLOBAL);

ShowWindow(hWnd,nWinMode); // Показать окно
UpdateWindow(hWnd); // Обновить окно
SetFocus(hWnd); // Сделать окно активным

while (1)
{
// Обработка всех сообщений
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
else return TRUE;
}

display(); // Нарисовать сцену
SwapBuffers(hDC); // Переключить буфер экрана

if (keys[VK_ESCAPE]) SendMessage(hWnd, WM_CLOSE, 0, 0);// Если ESC - выйти

}
}


Ну, вот вроде все!


Исходный код здесь(14.5k)
Если что не понятно пишите, постараюсь ответить.
Пожелания замечания, тоже приветствуются.

Виталий Демиденко © 2007


Content-Disposition: form-data; name="sort" 50
Категория: Графика (OpenGL) | Добавил: viledogsoftware (02.05.2007) | Автор: VileDog E
Просмотров: 35818 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]