Итак, это первая статья — как и во всех учебниках, начнем с инициализации программы и OpenGL.
Код мы создадим таким образом, чтобы в любой момент мы могли взять файлы из этого урока и начать на их основе новый проект.
Итак, в проекте будет 3 файла: main.cpp; init.cpp; main.h.
main.h: обьявления функций, структур и глобальных переменных.
#define _MAIN_H// Хидеры, необходимые для работы программы
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <glgl.h>
#include <glglu.h>// Обьявим глобальные переменные, ширину, высоту и глубину цвета экрана
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define SCREEN_DEPTH 16// Глобальные параметры окна; будут доступны из других файлов:
extern HWND g_hWnd;
extern RECT g_rRect;
extern HDC g_hDC;
extern HGLRC g_hRC;
extern HINSTANCE g_hInstance;// Прототип главной функции программы — WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprev, PSTR cmdline, int ishow);
// Прототип функции обработки сообщений
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
// Функция — главный цикл программы
WPARAM MainLoop();
// Функция, создающая окно
HWND CreateMyWindow(LPSTR strWindowName, int width, int height, DWORD dwStyle, bool bFullScreen, HINSTANCE hInstance);
// Функция, устанавливающая формат пиксела
bool bSetupPixelFormat(HDC hdc);
// Прототип функции, устанавливающей размеры окна OpenGL
void SizeOpenGLScreen(int width, int height);
// Функция, инициализирующая OpenGL
void InitializeOpenGL(int width, int height);
// Общая инициализация
void Init(HWND hWnd);
// Функция, которая собственно рисует сцену
void RenderScene();
// Де-инициализация
void DeInit();
#endif
Теперь создадим файлик init.cpp, реализующий большинство этих прототипов.
Нам почти не придется менять его в дальнейшем.
init.cpp: основные функции программы; инициализация и деинициализация
#include «main.h»///////////////////////////////////////////////////////////////
//
// Функция, создающая наше окошко
//
///////////////////////////////////////////////////////////////
HWND CreateMyWindow(LPSTR strWindowName, int width, int height, DWORD dwStyle, bool bFullScreen, HINSTANCE hInstance)
{
HWND hWnd; // дескриптор окна
WNDCLASS wndclass; // класс окна
memset(&wndclass, 0, sizeof(WNDCLASS)); // Резервируем память под класс окна
wndclass.style = CS_HREDRAW | CS_VREDRAW; // Стандартные параметра
wndclass.lpfnWndProc = WinProc; // Передаём указатель на функцию обработки сообщений
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Иконка
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); // Курсор
wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW+1); // Окно будет белым
wndclass.lpszClassName = «Wingman`s 3dLessons»; // Имя класса
RegisterClass(&wndclass); //регистрируем классdwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // Стиль окнаg_hInstance = hInstance;// Устанавливаем заданные размеры окна.
RECT rWindow;
rWindow.left = 0; // Левая сторона — 0
rWindow.right = width; // Правая сторона — 800
rWindow.top = 0; // Верх — 0
rWindow.bottom = height; // Низ — 800
AdjustWindowRect( &rWindow, dwStyle, false); // Применяем заданные размеры
// Создаём окно
hWnd = CreateWindow(«Wingman`s 3dLessons», strWindowName, dwStyle, 0, 0,
rWindow.right — rWindow.left, rWindow.bottom — rWindow.top,
NULL, NULL, hInstance, NULL);
if(!hWnd) return NULL; // Есди не получилось — умираем…
ShowWindow(hWnd, SW_SHOWNORMAL); // Показать окно
UpdateWindow(hWnd); // И нарисовать его
SetFocus(hWnd); // Фокусирует клавиатуру на наше окно
return hWnd;
}
///////////////////////////////////////////////////////////////
//
// Функция, устанавливающая формат пиксела
//
///////////////////////////////////////////////////////////////
bool bSetupPixelFormat(HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd; // Дескриптор формата пиксела
int pixelformat;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); // Устанавливаем размер структуры
pfd.nVersion = 1; // Всегда ставим = 1
// Передаём нужные флаги OpenGL
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.dwLayerMask = PFD_MAIN_PLANE; // Стандартная маска (один хрен игнорируется)
pfd.iPixelType = PFD_TYPE_RGBA; // Нам нужны RGB and Alpha типа пикселей
pfd.cColorBits = SCREEN_DEPTH; // Используем наши #define для цветовой глубины
pfd.cDepthBits = SCREEN_DEPTH; // Это игнорируется для RGBA, но все равно передадим
pfd.cAccumBits = 0;
pfd.cStencilBits = 0;
// Ф-я ищет формат пиксела, наиболее подходящий заданным требованиям, выход при неудаче
if ( (pixelformat = ChoosePixelFormat(hdc, &pfd)) == FALSE )
{
MessageBox(NULL, «ChoosePixelFormat failed», «Error», MB_OK);
return FALSE;
}
// Устанавливаем указанный формат пиксела
if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE)
{
MessageBox(NULL, «SetPixelFormat failed», «Error», MB_OK);
return FALSE;
}
return TRUE;
}
///////////////////////////////////////////////////////////////
//
// Ф-я устанавливает размер окна OpenGL
//
///////////////////////////////////////////////////////////////
void SizeOpenGLScreen(int width, int height)
{
if (height==0) // Предотвратим деление на 0
height=1;
// Сделаем наше окно OpenGL размером с главное окно программы. При желании можно сделать
// вьюпорт меньше окна.
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION); // Выберем матрицу проекции
glLoadIdentity(); // И сбросим её
// Вычислим перспективу нашего окна
// Параметры:
// (угол взгляда, отношение ширины и высоты,
// Ближайшее расстояние обьекта до камеры, при котором он виден,
// и дальнейшее расстояние, при котороом происходит отрисовка
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height, .5f ,150.0f);
// Важно: дальняя дистанция должна быть больше единицы, если вы не хотите получить
// прикольные артефакты, работая с освещением. Мало кто знает об этом, но при
// использовании света с ближней дистанцией камеры меньше 1, на полигонах будут
// видны неуместные блики и флеши.
glMatrixMode(GL_MODELVIEW); // Выберем матрицу моделей
glLoadIdentity(); // И сбросим её
}
///////////////////////////////////////////////////////////////
//
// Ф-я инициализирует OpenGL
//
///////////////////////////////////////////////////////////////
void InitializeOpenGL(int width, int height)
{
g_hDC = GetDC(g_hWnd); // Устанавливаем глобальный дескриптор окна
if (!bSetupPixelFormat(g_hDC)) // Устанавливаем формат пиксела
PostQuitMessage (0); // И выходим при ошибке
g_hRC = wglCreateContext(g_hDC); // Контекст рендеринга для hdc
wglMakeCurrent(g_hDC, g_hRC); // Делаем контекст текущим
glEnable(GL_TEXTURE_2D); // Включаем текстуры
glEnable(GL_DEPTH_TEST); // И тест глубины
// И устанавливаем размер вьюпорта:
SizeOpenGLScreen(width, height);
}
///////////////////////////////////////////////////////////////
//
// Ф-я де-Инициализирует OpenGL
//
///////////////////////////////////////////////////////////////
void DeInit()
{
if (g_hRC)
{
wglMakeCurrent(NULL, NULL); // Освобождает память, занятую для рендера
wglDeleteContext(g_hRC); // Удаляет контекст рендеринга OpenGL
}
if (g_hDC)
ReleaseDC(g_hWnd, g_hDC); // Убирает HDC из памяти
UnregisterClass(«Wingman`s 3dLessons», g_hInstance); // Освобождаем класс окна
PostQuitMessage (0); // Выходим
}
///////////////////////////////////////////////////////////////
//
// Функция регистрирует и создает окно
//
///////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{
HWND hWnd;
// Создает окно с помощью нашей функции, в которую передаём:
// Имя, Ширину, Высоту, любые флаги для окна, хотим ли мы фулскрин, и hInstance
hWnd = CreateMyWindow(«Wingman`s 3dLessons», SCREEN_WIDTH, SCREEN_HEIGHT, 0, false, hInstance);
// Выходим при ошибке
if(hWnd == NULL) return TRUE;
// Инициализируем OpenGL
Init(hWnd);
// Запускаем игровой цикл
return MainLoop();
}
Ну и теперь последний файл:
main.cpp: Главный файл, в котором и выполняется программа
// OpelGL
#pragma comment(lib, «opengl32.lib»)
#pragma comment(lib, «glu32.lib»)
#pragma comment(lib, «glaux.lib»)// Наш главный хидер:
#include «main.h»// Необходимые дескрипторы:
HWND g_hWnd;
RECT g_rRect;
HDC g_hDC;
HGLRC g_hRC;
HINSTANCE g_hInstance;///////////////////////////////////////////////////////////////
//
// Функция вызывается каждый кадр и рендерит сцену
//
///////////////////////////////////////////////////////////////void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Сейчас рисуется просто черный экран, в дальнейшем будет
// код рендеринга
SwapBuffers(g_hDC);
}
///////////////////////////////////////////////////////////////
//
// Инициализирует игровое окно
//
///////////////////////////////////////////////////////////////
void Init(HWND hWnd)
{
g_hWnd = hWnd;
GetClientRect(g_hWnd, &g_rRect);
InitializeOpenGL(g_rRect.right, g_rRect.bottom);
}
///////////////////////////////////////////////////////////////
//
// Главный цикл
//
///////////////////////////////////////////////////////////////
WPARAM MainLoop()
{
MSG msg;
while(1)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
RenderScene();
}
}
DeInit();
return(msg.wParam);
}
///////////////////////////////////////////////////////////////
//
// Обработка сообщений windows
//
///////////////////////////////////////////////////////////////
LRESULT CALLBACK WinProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LONG lRet = 0;
PAINTSTRUCT ps;
switch (uMsg)
{
case WM_SIZE: // Если изменён размер окна
SizeOpenGLScreen(LOWORD(lParam),HIWORD(lParam));// LoWord=Width, HiWord=Height
GetClientRect(hWnd, &g_rRect); // получаем window rectangle
break;
case WM_PAINT: // Если нужно перерисовать сцену
BeginPaint(hWnd, &ps); // Иниц. paint struct
EndPaint(hWnd, &ps); // EndPaint, подчищаем
break;
case WM_KEYDOWN: // Это сообщение означает, что нажата клавиша на клавиатуре.
// Сама клавиша передаётся в параметре wParam
switch(wParam)
{
case VK_ESCAPE: // Если нажат ESCAPE
PostQuitMessage(0); // Выходим
break;
}
break;
case WM_CLOSE: // Если окно было закрыто
PostQuitMessage(0); // Выходим
break;
default: // Return по умолчанию
lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
break;
}
return lRet;
}
Этот урок совсем простой: была описана инициализация программы и OpenGL.
В программе ничего не делается — отрисовывается чёрный экран, и ожидается нажатие
клавиши ESCAPE для выхода из программы.