Итак, это первая статья — как и во всех учебниках, начнем с инициализации программы и OpenGL.
Код мы создадим таким образом, чтобы в любой момент мы могли взять файлы из этого урока и начать на их основе новый проект.

Итак, в проекте будет 3 файла: main.cpp; init.cpp; main.h.

main.h: обьявления функций, структур и глобальных переменных.

#ifndef _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 для выхода из программы.

Исходные коды к уроку

Добавить комментарий