Четверг, 21 Ноября 2024, 12:51

Приветствую Вас Гость

Меню сайта
Категории каталога
Создание игр [358]
Статьи об общих понятиях связанных с созданием игр.
Программирование [83]
Гайды по программированию на разных ЯП.
Движки и Гейммейкеры [147]
Статьи о программах для создания игр, уроки и описания.
Софт [43]
Различные программы, в том числе в помощь игроделам.
2D-графика [14]
Уроки по рисованию, растр, пиксель-арт, создание спрайтов и пр.
3D-графика [17]
Уроки по моделированию, ландшафт, модели, текстурирование и пр.
Моддинг игр [5]
Модификация компьютерных игр, создание дополнений, перевод, хакинг.
Игры [167]
Статьи об играх, в том числе и сделанных на гейммейкерах.
Разное [132]
Статьи, которые не вошли в определённые разделы.
Наш опрос
Сколько всего игр вы прошли?
Всего ответов: 4195
Главная » Статьи » Создание игр

Инициализация OpenGL в Windows





Взявшись за написание
OpenGL программы, вы думаете, что написав такую программу можно запустить
её на любом компьютере под управлением любой операционной системы?
Да, к самому коду OpenGL это вполне применимо, но необходимо также
учитывать, что у разных операционных систем свои особенности, а это
значит что для конкретной операционной системы нужно произвести начальные
настройки OpenGL, свойственные только этой операционной системе. У
разных операционных систем свои способы вывода графической информации
на устройства и свои реализации OpenGL.


Данный материал относится
к конкретной операционной системе - Windows. Материал разделён на две
части: программирование с использованием Visual С++ и программирование
с использованием Delphi. В конце каждой части приводятся примеры, которые
можно переписать себе.


Visual C++ под Windows.


Самым главным этапом
в инициализации OpenGL под Windows является заполнение информации о
формате пикселей. Таким образом вы извещаете операционную систему о
том, какую поверхность для вывода OpenGL информации конкретно вы хотите.
Здесь указывается такая информация как глубина цвета, различные флаги
поверхности (двойная или одинарная буферизация, ...), и т.д. Вся эта
информация представляется в специальной структуре, которая называется PIXELFORMATDESCRIPTOR.


Вот эта структура:




typedef
struct tagPIXELFORMATDESCRIPTOR {

WORD nSize;

WORD nVersion;

DWORD dwFlags;

BYTE iPixelType;

BYTE cColorBits;

BYTE cRedBits;

BYTE cRedShift;

BYTE cGreenBits;

BYTE cGreenShift;

BYTE cBlueBits;

BYTE cBlueShift;

BYTE cAlphaBits;

BYTE cAlphaShift;

BYTE cAccumBits;

BYTE cAccumRedBits;

BYTE cAccumGreenBits;

BYTE cAccumBlueBits;

BYTE cAccumAlphaBits;

BYTE cDepthBits;

BYTE cStencilBits;

BYTE cAuxBuffers;

BYTE iLayerType;

BYTE bReserved;

DWORD dwLayerMask;

DWORD dwVisibleMask;


DWORD dwDamageMask;

} PIXELFORMATDESCRIPTOR;



Теперь рассмотрим
все поля структуры поподробнее:



nSize Определяет
размер этой структуры данных. Это значение должно быть установлено
как sizeof(PIXELFORMATDESCRIPTOR).


nVersion Определяет
версию текущей структуры данных. Это значение должно быть установлено
в 1


dwFlags Множество
битовых флагов, которые определяют свойства буфера пикселей. Вы можете
комбинировать следующие флаги:



  • PFD_DRAW_TO_WINDOW
    Буфер можно рисовать в окне или на поверхности устройства.
  • PFD_DRAW_TO_BITMAP
    Буфер можно рисовать в битовый массив в памяти.
  • PFD_SUPPORT_GDI
    Буфер поддерживает рисование GDI. В текущей реализации этот флаг
    нельзя использовать вместе с PFD_DOUBLEBUFFER.
  • PFD_SUPPORT_OPENGL
    Буфер поддерживает рисование OpenGL.
  • PFD_GENERIC_ACCELERATED
    Формат пикселей поддерживается драйвером устройства который ускоряет
    программную реализацию. Если этот не установлен и флаг PFD_GENERIC_FORMAT
    установлен, тогда формат пикселей поддерживается только программной
    реализацией.
  • PFD_GENERIC_FORMAT
    Формат пикселей поддерживается программной реализацией GDI. Если
    этот флаг не установлен, то формат пикселей поддерживается драйвером
    устройства или аппаратно.
  • PFD_NEED_PALETTE
    Для управления палитрой устройства используются RGBA пиксели. Логическая
    палитра используется, чтобы достичь самых лучших результатов для
    этого типа пикселов. Цвета в палитре должны быть определены соответственно
    в полях: cRedBits, cRedShift, cGreenBits, cGreenShift, cBluebits,
    и cBlueShift. Палитра должна быть создана и инициализирована в контексте
    устройства до вызова функции wglMakeCurrent.
  • PFD_NEED_SYSTEM_PALETTE
    Определяется для оборудования, которое поддерживает только одну аппаратную
    палитру (только в режиме 256 цветов). Для использования ускорения
    в таких устройствах палитра должна быть установлена в фиксированном
    порядке (например 3-3-2) в режиме RGBA или должна совпадать с логической
    палитрой в режиме индексации цветов. Когда этот флаг установлен,
    вы должны вызвать функцию SetSystemPaletteUse в вашей программе для
    принудительного отображения один к одному логической и системной
    палитры. Типично этот флаг сброшен, когда ваше OpenGL оборудование
    поддерживает несколько аппаратных палитр и драйвер устройства может
    распределять резервные аппаратные палитры для OpenGL. Также этот
    флаг не установлен для основного формата пикселей.
  • PFD_DOUBLEBUFFER
    Если этот флаг установлен, то используется двойная буферизация. Как
    уже было сказано, этот флаг нельзя использовать совместно с флагом
    PFD_SUPPORT_GDI.
  • PFD_STEREO Буфер
    стереоскопический. В этом режиме используются два буфера (левый и
    правый) для получения стерео изображения. Этот флаг не поддерживается
    в текущей основной реализации.
  • PFD_SWAP_LAYER_BUFFERS
    Показывает, может ли устройство менять местами отдельные уровневые
    плоскости с форматами пикселей, которые включают верхние и нижние
    плоскости при двойной буферизации. Иначе все уровневые плоскости
    рассматриваются как одна группа. Если этот флаг установлен, то поддерживается
    функция: wglSwapLayerBuffers



iPixelType Этот
флаг определяет тип пикселей. Возможны следующие значения:



  • PFD_TYPE_RGBA
    RGBA пиксели. Каждый пиксель определяется четырьмя компонентами в
    таком порядке: красный, зелёный, синий, альфа.
  • PFD_TYPE_COLORINDEX
    Индексированный пиксели. Цвет каждого пикселя определяется индексом
    в специальной таблице цветов (палитре).


cColorBits Определяет
число битовых плоскостей в каждом буфере цвета. Для режима RGBA определяет
размер буфера цвета, исключая альфа-плоскости. В режиме индексации
цветов определяет буфера индексов.


cRedBits Определяет
количество битовых плоскостей красного цвета в каждом RGBA буфере.


cRedShift Определяет
сдвиг для битовых плоскостей красного цвета в каждом RGBA буфере.


cGreenBits Определяет
количество битовых плоскостей зелёного цвета в каждом RGBA буфере.


cGreenShift Определяет
сдвиг для битовых плоскостей зелёного цвета в каждом RGBA буфере.


cBlueBits Определяет
количество битовых плоскостей синего цвета в каждом RGBA буфере.


cBlueShift Определяет
сдвиг для битовых плоскостей синего цвета в каждом RGBA буфере.


cAlphaBits Определяет
количество битовых плоскостей альфа в каждом RGBA буфере. Альфа плоскости
не поддерживаются.


cAlphaShift Определяет
сдвиг для битовых плоскостей альфа в каждом RGBA буфере. Альфа плоскости
не поддерживаются.


cAccumBits Определяет
общее число битовых плоскостей в буфере аккумулятора.


cAccumRedBits Определяет
число битовых плоскостей красного цвета в буфере аккумулятора.


cAccumGreenBits Определяет
число битовых плоскостей зелёного цвета в буфере аккумулятора.


cAccumBlueBits Определяет
число битовых плоскостей синего цвета в буфере аккумулятора.


cAccumAlphaBits Определяет
число битовых плоскостей альфа в буфере аккумулятора.


cDepthBits Определяет
размер буфера глубины (ось Z).


cStencilBits Определяет
размер буфера трафарета.


cAuxBuffers Определяет
число вспомогательных буферов. Вспомогательные буферы не поддерживаются.


iLayerType Принимает
одно из следующих значений: PFD_MAIN_PLANE, PFD_OVERLAY_PLANE, PFD_UNDERLAY_PLANE.


bReserved Определяет
число плоскостей переднего и заднего плана. Биты 0..3 определяют до
15 плоскостей переднего плана, а биты 4..7 - до 15 плоскостей заднего
плана.


dwLayerMask Игнорируется.
Раньше этот флаг использовался, но не долго.


dwVisibleMask Определяет
цвет или индекс прозрачности задней плоскости.


dwDamageMask Игнорируется.
Раньше этот флаг использовался, но не долго.


Итак, после того
как наша структура заполнена, мы приступаем к самой инициализации.
Для начала нам нужно передать на рассмотрение системе, выбранный нами
формат пикселей. После того как система просмотрит его, она выберет
наиболее совпадающий формат с тем, который поддерживается в контексте
устройства. Вот эта функция:




int
ChoosePixelFormat(HDC hdc, CONST PIXELFORMATDESCRIPTOR *ppfd);



Как я уже сказал,
функция просматривает в контексте устройства - hdc наиболее подходящий
формат пикселей и выбирает его. ppfd - это указатель на нашу структуру.


В случае успешного
завершения функция возвращает индекс формата пикселей (значение начиная
с 1) или возвращает 0 - в случае ошибки.


Как только система
выбрала формат пикселей, нам нужно установить его в контексте устройства.
Для этого существует следующая функция:



BOOL
SetPixelFormat(HDC hdc, int iPixelFormat, CONST PIXELFORMATDESCRIPTOR
*ppfd);



Параметры функции:
hdc - контекст устройства, для которого устанавливается данный формат
пикселей, iPixelFormat - индекс, который получен при помощи предыдущей
функции, ppfd - указатель на структуру PIXELFORMATDESCRIPTOR, которая
содержит логическую спецификацию формата пикселей и эта структура никак
не действует на функцию SetPixelFormat .


В случае успеха функция
возвратит TRUE, в случае неудачи - FALSE.



Существуют ещё две
функции, они используются реже чем две предыдущие. Обе предоставляют
информацию о формате пикселей для контекста устройства.



int
GetPixelFormat(HDC hdc);



Функция возвращает
текущий формат пикселей для контекста устройства hdc, т.е. возвращает
значение больше 0, если произошла ошибка функция возвратит 0.



int
DescribePixelFormat(HDC hdc, int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR
ppfd);



Эта функция позволяет
получить информацию о формате пикселей, заданного индексом iPixelFormat
для контекста устройства hdc. Полученную информацию функция записывает
в ppfd - это структура типа PIXELFORMATDESCRIPTOR, из которой вы уже
сможете смотреть конкретную информацию о формате пикселей. Ещё один
параметр - nBytes, это размер структуры в байтах, на которую ссылается
ppfd, его можно определить например так: sizeof(PIXELFORMATDESCRIPTOR);


При успешном завершении
функция возвращает максимальный доступный индекс формата пикселей.
Если возникает ошибка, функция возвратит 0.


Чтобы вам стало понятнее,
как на практике всё это применять, я написал небольшую программку,
которая демонстрирует инициализацию OpenGL в Windows.


Посмотрите на следующий
код, он демонстрирует заполнение структуры PIXELFORMATDESCRIPTOR ,
запрашивает информацию в системе и устанавливает для контекста устройства
формат пикселей. В данном примере инициализация OpenGL производится
в конструкторе главного окна, т.е. сначала создаётся главное окно программы
и сразу за ним устанавливается OpenGL.



CMainWin::CMainWin()

{


//
Тут создаётся главное окно программы

Create(NULL, "OpenGL MFC Sample");



PIXELFORMATDESCRIPTOR pfd;


memset(&pfd,
0, sizeof(PIXELFORMATDESCRIPTOR));


pfd.nSize
= sizeof(PIXELFORMATDESCRIPTOR);

pfd.nVersion = 1;

pfd.dwFlags = PFD_DRAW_TO_WINDOW |

PFD_SUPPORT_OPENGL |

PFD_DOUBLEBUFFER;

pfd.iPixelType = PFD_TYPE_RGBA;

pfd.cColorBits = 24;

pfd.cRedBits = 0;

pfd.cRedShift = 0;

pfd.cGreenBits = 0;

pfd.cGreenShift = 0;

pfd.cBlueBits = 0;

pfd.cBlueShift = 0;


pfd.cAlphaBits = 0;

pfd.cAlphaShift = 0;

pfd.cAccumBits = 0;

pfd.cAccumRedBits = 0;

pfd.cAccumGreenBits = 0;

pfd.cAccumBlueBits = 0;

pfd.cAccumAlphaBits = 0;

pfd.cDepthBits = 32;

pfd.cStencilBits = 0;

pfd.cAuxBuffers = 0;

pfd.iLayerType = PFD_MAIN_PLANE;

pfd.bReserved = 0;

pfd.dwLayerMask = 0;

pfd.dwVisibleMask = 0;

pfd.dwDamageMask = 0;




// Создаём новый контекст устройства

m_pDC = new CClientDC(this);

HDC hdc = m_pDC->GetSafeHdc();




// Выбираем подходящий формат пикселей

int pixelFormat = ChoosePixelFormat(hdc, &pfd);

if (pixelFormat == 0)


{

MessageBox("Error in Pixel format choosing!");

return;

}


//
Устанавливаем теперь этот формат пикселей

BOOL bresult = SetPixelFormat(hdc, pixelFormat, &pfd);

if (!bresult)

{

MessageBox("Error in Pixel format registering!");

return;

}



// Создаём новый контекст воспроизведения
для OpenGL

rc = wglCreateContext(hdc);

if (!rc)

{

MessageBox("Error in OpenGL context create!");

return;

}

// Делаем созданный контекст текущим.

wglMakeCurrent(hdc, rc);


//
Дальше идут уже команды OpenGL.

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);


glViewport(0,
0, 400, 300);


glMatrixMode(GL_PROJECTION);

glLoadIdentity();


gluOrtho2D(-1,
1, -1, 1);

glMatrixMode(GL_MODELVIEW);

}




Вы наверно заметили
в приведённом выше примере, что после того как устанавливается формат
пикселей, дальше идут две функции wglCreateContex и wglMakeCurrent
. Эти функции служат для создания контекста воспроизведения OpenGL.
Что это такое и как его использовать, читайте дальше.


Контекст воспроизведения
OpenGL чем то напоминает контекст устройства Windows, но не является
тем же самым. Для того чтобы рисовать в окне Windows, вы должны сначала
запросить у системы контекст устройства (т.е. окна), который связан
с конкретным устройством, на котором осуществляется рисование (т.е.
окно, но может быть и не окном). Далее нужно создать контекст воспроизведения
OpenGL, который необходимо связать с контекстом устройства в Windows,
а потом выбрать его, т.е. сделать текущим. Зачем делать его текущим?
Дело в том, что можно создать много контекстов воспроизведения, каждый
из них будет связан со своим устройством отображения, но для того чтобы
работать в OpenGL надо выбрать только один (текущий) контекст. Посмотрите
на рисунок и вам станет понятнее:



src="http://gamecreating.3dn.ru/pic/opengl_context.jpg" width=400>


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


В процессе выполнения
OpenGL программы вы можете переключать текущий поток на другие контексты
воспроизведения. Таким образом используя несколько контекстов воспроизведения
можно строить мощные комплексы воспроизведения. Например можно организовать
сеть воспроизведения, где один компьютер будет вырабатывать команды
OpenGL (такой компьютер называется клиентом) и будет переключать эти
потоки команд между несколькими компьютерами (серверами), которые будут
визуализировать поступающую информацию.


Переходим теперь
к практической части. То есть к программированию под Windows.


В распоряжения программиста
предоставлено несколько команд для управления контекстами воспроизведения.
Сначала необходимо создать контекст воспроизведения, делает это функция:



HGLRC
wglCreateContext(HDC hdc);



Эта функция создаёт
новый контекст воспроизведения OpenGL, который подходит для рисования
на устройстве, определённом дескриптором hdc. В случае успешного завершения
функция возвращает дескриптор созданного контекста воспроизведения,
в случае неудачи возвращает значение NULL.


Следующая функция
позволяет выбрать текущий контекст воспроизведения для потока воспроизведения.
Если контекстов создано несколько, то надо выбрать один из них. Функция
выглядит таким образом:



BOOL
wglMakeCurrent(HDC hdc, HGLRC hglrc);



Контекст устройства
задаётся параметром hdc, контекст воспроизведения OpenGL - параметром
hglrc. Если функция выполняется успешно, то она возвратит TRUE, в противном
случае возвратит FALSE.


Есть ещё две функции,
которые носят информационный характер. Первая из них:



HGLRC
wglGetCurrentContext(VOID);



Она возвращает дескриптор
текущего контекста воспроизведения или NULL, если такового не существует.


Другая функция позволяет
получить контекст устройства, который ассоциирован с текущим контекстом
воспроизведения:



HDC
wglGetCurrentDC(VOID);



В случае успешного
завершения (если в потоке существует контекст воспроизведения) функция
возвратит контекст устройства, связанного с ним или NULL, в противном
случае.


И ещё одна функция,
которую в основном вызывают в конце работы с OpenGL:



BOOL
wglDeleteContext(HGLRC hglrc);



Эта функция удаляет
контекст воспроизведения. Необходимо также позаботиться об удалении
контекста устройства Windows, связанного с этим контекстом воспроизведения,
это делается Windows функцией: BOOL DeleteDC(HDC hdc);


В случае успешного
завершения функция wglDeleteContext возвращает TRUE, в противном же
случае - FALSE.


Ну и на последок
маленький кусочек кода, который вызывается при завершении программы,
поможет вам побольше разобраться в практической части.




afx_msg
void CMainWin::OnDestroy()

{

HDC hdc = m_pDC->GetSafeHdc();

// Выбираем контекст воспроизведения

wglMakeCurrent(hdc, rc);

// И удаляем его ...

wglDeleteContext(rc);

CFrameWnd::OnDestroy();

}




В конце этого раздела
приведу пару примеров, которые вы можете скачать в виде ZIP файлов.


href="http://www.vbstreets.ru/lavision/default.asp?include=download.asp&id=opengl_mfc.zip">OpenGL_MFC.zip -
Инициализация OpenGL в Windows средствами MFC

href="http://www.vbstreets.ru/lavision/default.asp?include=download.asp&id=opengl_win32.zip">OpenGL_Win32.zip -
Инициализация OpenGL в Windows средствами Win32, т.е. без MFC.




Delphi под Windows.



Программирование
OpenGL на Delphi под Windows сильно напоминает то, что было описано
выше. Всё то же надо проделать, всё те же шаги для инициализации OpenGL.
Только в Delphi это всё выглядит иначе, правильно язык то другой -
pascal! smile


Итак приведу основные
шаги, которые надо проделать для Delphi.



  1. Во первых надо
    нужен специальный файл, который называется OpenGL.pas. Его можно
    скачать на моей страничке. Автор этого файла: Mike Lischke, public@lischke-online.de.
    Файл представляет собой ничто иное как портированную версию OpenGL
    для Delphi. Необходимо подключить этот файл к проекту.
  2. В форме, где вы
    собираетесь использовать OpenGL необходимо в метод обработки события
    TForm.FormCreate(...) вставить команды по инициализации OpenGL, например
    как показано на примере:

    procedure
    TMain.FormCreate(Sender: TObject);

    var

    PixelFmt: Integer;

    pfd: TPixelFormatDescriptor;

    begin


    InitOpenGL;


    DC
    := GetDC(Handle);

    FillChar(pfd, SizeOf(pfd), 0);


    with
    pfd do

    begin

    nSize := sizeof(pfd);

    nVersion := 1;

    dwFlags := PFD_DRAW_TO_WINDOW or

    PFD_SUPPORT_OPENGL or

    PFD_DOUBLEBUFFER;

    iPixelType:= PFD_TYPE_RGBA;

    cColorBits:= 24;

    cDepthBits:= 32;

    iLayerType:= PFD_MAIN_PLANE;

    end;


    PixelFmt
    := ChoosePixelFormat(DC, @pfd);

    SetPixelFormat(DC, PixelFmt, @pfd);

    hrc := wglCreateContext(DC);

    wglMakeCurrent(DC, hrc);


    glClearColor(0.0,
    0.0, 0.0, 1.0);


    glViewport(0,
    0, 400, 300);

    glMatrixMode(GL_PROJECTION);

    glLoadIdentity();


    gluOrtho2D(-1,
    1, -1, 1);

    glMatrixMode(GL_MODELVIEW);


    end;





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

Далее идёт заполнение
структуры PIXELFORMATDESCRIPTOR , а далее уже всё по знакомой схеме,
описанной выше.



  1. Для рисования
    в окне OpenGL необходимо в метод TForm.FormPaint(...) вставить соответствующий
    код.
  2. Ну и наконец при
    завершении приложения необходимо написать следующее:

    procedure
    TMain.FormDestroy(Sender: TObject);

    begin

    wglMakeCurrent(0, 0);

    wglDeleteContext(hrc);

    ReleaseDC(Handle, DC);

    end;





Заключение:


Теперь, когда вы
наконец то завершили инициализацию OpenGL под Windows, можно спокойно
начинать программировать на OpenGL. Хотя если вы не только будете рисовать
в окне, а захотите работать например с клавиатурой или мышкой, то вам
опять OpenGL не поможет и надо будет использовать функции Windows,
но это тема уже другой статьи. Скажу лишь несколько слов о том, что
существуют специальные библиотеки, например GLUT, которые берут управление
инициализацией, окнами, устройствами ввода/вывода на себя и не зависят
от конкретной операционной системы, но это тоже тема другой статьи.

Категория: Создание игр | Добавил: GC-Vic (31 Января 2008)
Просмотров: 21192 | Комментарии: 2 | Рейтинг: 5.0/4 |
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:
Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.

Всего комментариев: 2
+1-
1 Алмаз   (27 Июня 2009 22:00) [Материал]
Вот именно, лучше в начале не заморачиваться на созданиях окон,а применять GLUT.

+1-
2 05142   (05 Мая 2011 20:49) [Материал]
В начале да. Но если движок пишешь? Эта статья для тех кто изучил немного OpenGL и хочет сделать двиг.

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • INSTEAD
  • Axiom Engine
  • Gideros Studio
  • Dark BASIC
  • Metagam
  • NaaLaa
  • Creatry
  • WinPAW
  • Noobster
  • Fyrox
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2024 Рейтинг