Итак, вы решили написать программу, которая осуществляет рендерирг средствами OpenGL. Что для этого нужно? Сначала нужно получить контекст окна приложения, в которое будет осуществляться рендеринг, создать контекст рендеринга OpenGL, затем выбрать созданный контекст рендеринга.
Разберем всё более детально. Для подготовки OpenGL к работе нужно проделать следующие операции.
- Получить контекст окна, в которое будет осуществляться рендеринг
- Установить предполагаемый формат пикселя, для рисования в окне средствами OpenGL
- Выбрать наиболее подходящий формат пикселя из форматов, поддерживаемых операционной системой
- Установить выбранный формат пикселя
- Создать контекст рендерига OpenGL
- Сделать текущим контекст рендерига OpenGL
Перейдем от теории к практике. Перед вами пример модуля, который содержит все необходимые методы для создания программы, использующей функции OpenGL, с подробными комментариями.
unit GL_Utils;
interface
uses
Windows, OpenGL;
type
// Структура сохраняет, контекст окна,
// контекст рендеринга OpenGL и дескриптор окна
TContextData = record
DC: HDC; // Контекст окна, в которое осуществляется рендеринг
RC: HGLRC; // Контекст рендеринга OpenGL
Handle: THandle; // Дескриптор окна, в которое осуществляется рендеринг
end;
procedure CreateContext(const Handle: THandle; var ContextData: TContextData);
procedure DestroyContext(var ContextData: TContextData);
implementation
// Процедура устанавливает предполагаемый формат пикселей
procedure SetPFD(var PFD: TPixelFormatDescriptor);
begin
FillChar(PFD, SizeOf(PFD), 0); // Обнуляем PFD
with PFD do
begin
nSize := SizeOf(PFD); // Устанавливаем размер структуры
nVersion := 1; // Устанавливаем версию
// Устанавливаем флаги буфера пикселей
dwFlags := PFD_DRAW_TO_WINDOW or PFD_DOUBLEBUFFER or PFD_SUPPORT_OPENGL;
iPixelType := PFD_TYPE_RGBA; // Устанавливаем тип пикселей
cColorBits := 24; // Устанавливаем количество битовых плоскостей в буфере цвета
cDepthBits := 32; // Устанавливаем размер буфера глубины
iLayerType := PFD_MAIN_PLANE; // Устанавливаем тип слоя
end;
end;
// Процедура создаёт контекст рендеринга OpenGL и
// устанавливает контекст окна
procedure CreateContext(const Handle: THandle; var ContextData: TContextData);
var
PFD: TPixelFormatDescriptor; // Описание формата пикселей
IPixelFormat: Integer; // Индекс формата пикселей
begin
// Если хотябы один из контекстов или дескриптор окна не равен нулю, завершаем процесс
with ContextData do
if (DC <> 0) or (RC <> 0) or (ContextData.Handle <> 0) then Exit;
ContextData.Handle := Handle; // Сохраняем дескриптор окна
ContextData.DC := GetDC(Handle); // Получаем контекст окна
if ContextData.DC = 0 then Exit; // Если контекст не получен - завершаем процесс
SetPFD(PFD); // Устанавливаем предполагаемый формат пикселей
// Получаем наиболее подходящий формат пикселей
IPixelFormat := ChoosePixelFormat(ContextData.DC, @PFD);
// Если индекс формата пикселей не получен, завершаем процесс
if IPixelFormat = 0 then Exit;
// Устанавливаем полученный формат пикселей
SetPixelFormat(ContextData.DC, IPixelFormat, @PFD);
ContextData.RC := wglCreateContext(ContextData.DC); // Создаем контекст рендеринга OpenGL
if ContextData.RC = 0 then Exit; // Если контекст не создан, завершаем процесс
// Делаем текущим созданный контекст рендеринга
wglMakeCurrent(ContextData.DC, ContextData.RC);
end;
// Процедура удаляет контекст рендеринга OpenGL и
// освобождает контекст окна
procedure DestroyContext(var ContextData: TContextData);
begin
with ContextData do
begin
// Если хотя бы один из контекстов либо дескриптор окна равен нулю, завершаем процесс
if (DC = 0) or (RC = 0) or (Handle = 0) then Exit;
// Сбрасываем текущий контекст рендеринга
wglMakeCurrent(0, 0);
// Удаляем контекст рендеринга
wglDeleteContext(RC);
// Освобождаем контекст окна
ReleaseDC(Handle, DC);
end;
end;
end.
|
Модуль содержит две основные процедуры: CreateContext и DestroyContext.
Разберем процедуру CreateContext, которая создает контекст рендеринга OpenGL и устанавливает контекст окна. Процедура сначала проверяет контексты и дескриптор окна (они должны быть установлены в ноль).
with ContextData do if (DC <> 0) or (RC <> 0) or (ContextData.Handle <> 0) then Exit;
|
Переменная ContextData – это запись TContextData, созданная мной для удобства работы с контекстами.
TContextData = record
DC: HDC; // Контекст окна, в которое осуществляется рендеринг
RC: HGLRC; // Контекст рендеринга OpenGL
Handle: THandle; // Дескриптор окна, в которое осуществляется рендеринг
end;
|
Далее сохраняем дескриптор окна.
ContextData.Handle := Handle;
|
Затем получаем контекст окна с помощью функции Win API.
ContextData.DC := GetDC(Handle);
|
Проверяем контекст.
if ContextData.DC = 0 then Exit;
|
Если контекст равен нулю – завершаем процедуру.
Далее устанавливаем предполагаемый формат пикселя TPixelFormatDescriptor. Рассмотрим эту структуру подробно. Структура TPixelFormatDescriptor – это запись tagPIXELFORMATDESCRIPTOR, которая описана в модуле Windows. Описания полей этой структуры приведены в таблице ниже.
nSize
|
Размер структуры (обычно устанавливается с помощью функции SizeOf модуля System (nSize := SizeOf(TPixelFormatDescriptor)))
|
nVersion
|
Версия (всегда устанавливаем в 1)
|
dwFlags
|
Флаги буфера пикселей
|
iPixelType
|
Тип данных пикселей
|
cColorBits
|
Количество битовых плоскостей в каждом буфере цвета
|
cRedBits
|
Количество битовых плоскостей красного в каждом цветовом буфере RGBA
|
cRedShift
|
Сдвиг битовых плоскостей красного в каждом цветовом буфере RGBA
|
cGreenBits
|
Количество битовых плоскостей зеленого в каждом цветовом буфере RGBA
|
cGreenShift
|
Сдвиг битовых плоскостей зеленого в каждом цветовом буфере RGBA
|
cBlueBits
|
Количество битовых плоскостей синего в каждом цветовом буфере RGBA
|
cBlueShift
|
Сдвиг битовых плоскостей синего в каждом цветовом буфере RGBA
|
cAlphaBits
|
Количество битовых плоскостей альфа канала в каждом цветовом буфере RGBA
|
cAlphaShift
|
Сдвиг битовых плоскостей альфа канала в каждом цветовом буфере RGBA
|
cAccumBits
|
Общее количество битовых плоскостей в буфере накопления
|
cAccumRedBits
|
Количество битовых плоскостей красного в буфере накопления
|
cAccumGreenBits
|
Количество битовых плоскостей зеленого в буфере накопления
|
cAccumBlueBits
|
Количество битовых плоскостей синего в буфере накопления
|
cAccumAlphaBits
|
Количество битовых плоскостей альфа канала в буфере накопления
|
cDepthBits
|
Размер буфера глубины
|
cStencilBits
|
Размер буфера трафарета
|
cAuxBuffers
|
Количество дополнительных буферов (дополнительные буферы не поддерживаются)
|
iLayerType
|
Тип слоя
|
bReserved
|
Не используется (всегда устанавливаем в 0)
|
dwLayerMask
|
Маска слоя (игнорируется)
|
dwVisibleMask
|
Видимая маска
|
dwDamageMask
|
Не используется (всегда устанавливаем в 0)
|
Чтобы установить формат пикселя вызываем процедуру SetPFD, которая и сохраняет все нужные параметры в структуру TPixelFormatDescriptor.
procedure SetPFD(var PFD: TPixelFormatDescriptor);
begin
FillChar(PFD, SizeOf(PFD), 0); // Обнуляем PFD
with PFD do
begin
nSize := SizeOf(PFD); // Устанавливаем размер структуры
nVersion := 1; // Устанавливаем версию
// Устанавливаем флаги буфера пикселей
dwFlags := PFD_DRAW_TO_WINDOW or PFD_DOUBLEBUFFER or PFD_SUPPORT_OPENGL;
iPixelType := PFD_TYPE_RGBA; // Устанавливаем тип пикселей
cColorBits := 24; // Устанавливаем количество битовых плоскостей в буфере цвета
cDepthBits := 32; // Устанавливаем размер буфера глубины
iLayerType := PFD_MAIN_PLANE; // Устанавливаем тип слоя
end;
end;
|
Для того, чтобы не присваивать нули каждому параметру TPixelFormatDescriptor, заполняем нулями всю запись с помощью процедуры FillChar, модуля System.
FillChar(PFD, SizeOf(PFD), 0);
|
После очистки структуры PFD, можно приступить к установке нужных (запрашиваемых) значений пиксельного формата. Поле nSize, которое должно содержать размер записи TPixelFormatDescriptor заполняем с помощью функции SizeOf.
Далее устанавливаем версию структуры.
Затем устанавливаем параметр dwFlags. Это битовая комбинация следующих флагов.
PFD_DRAW_TO_WINDOW
|
Разрешает вывод в окно
|
PFD_DOUBLEBUFFER
|
Разрешает двойную буферизацию
|
PFD_SUPPORT_OPENGL
|
Разрешает рисование с помощью OpenGL
|
Устанавливаем другие параметры.
iPixelType := PFD_TYPE_RGBA; // Тип пикселей
cColorBits := 24; // Количество битовых плоскостей в буфере цвета
cDepthBits := 32; // Размер буфера глубины
iLayerType := PFD_MAIN_PLANE; // Тип слоя
|
Все остальные параметры оставляем равными нулю.
Теперь, когда контекст окна получен и структура TPixelFormatDescriptor заполнена, можно приступить к установке поддерживаемого формата пикселей, который наиболее близок к запрашиваемому PFD. Делается это с помощью функции ChoosePixelFormat.
IPixelFormat := ChoosePixelFormat(ContextData.DC, @PFD);
|
Переменная IPixelFormat сохраняет индекс формата пикселя, который возвращает функция. Если индекс формата не получен – завершаем процесс.
if IPixelFormat = 0 then Exit;
|
Когда формат пикселя установлен, можно приступить к созданию контекста рендеринга OpenGL.
ContextData.RC := wglCreateContext(ContextData.DC);
|
Результат функции сохраняем в запись ContextData.
И в завершении делаем текущим созданный контекст рендеринга с помощью функции wglMakeCurrent:
wglMakeCurrent(ContextData.DC, ContextData.RC);
|
Теперь все готово для рисования инструментами OpenGL. Но об этом я расскажу на следующих занятиях. А сейчас продолжим рассматривать наш модуль.
Перед завершения программы нужно удалить контекст рендеринга OpenGL и освободить контекст окна. Для этого нужно вызвать процедуру DestroyContext. Прежде чем выполнить необходимые действия процедура проверяет контексты и дескриптор окна.
if (DC = 0) or (RC = 0) or (Handle = 0) then Exit;
|
Если хоть один из контекстов равен нулю – процесс завершается.
Далее сбрасываем текущий контекст рендеринга.
Затем удаляем его.
И в завершении освобождаем контекст окна.
Теперь можно испытать наш модуль в действии.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, OpenGL, GL_Utils;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
ContextData: TContextData; // Сохраняет контексты и дескриптор окна
procedure TForm1.FormCreate(Sender: TObject);
begin
// Заполняем нулями структуру TContextData
ZeroMemory(@ContextData, SizeOf(ContextData));
// Создаём контекст рендеринга OpenGL и
// устанавливаем контекст окна
CreateContext(Handle, ContextData);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// Удаляем контекст рэндэринга OpenGL и
// освобождаем контекст окна
DestroyContext(ContextData);
end;
end.
|
Откройте Делфи создайте новый проект, в главном модуле напишите код этого примера и сохраните проект в отдельную папку (он вам понадобится для следующих занятий). Затем создайте модуль и скопируйте в него содержимое примера модуля GL_Utils и сохраните его под именем GL_Utils.pas в папку с проектом или в папку с вашими модулями. Если папка, в которую вы сохранили модуль не подключена к среде разработки Делфи - подключите её (как подключить смотрим здесь). Рекомендую выполнить эти действия, так, как данный модуль понадобится вам в дальнейшем и у вас не будет проблем с линковкой модуля к проекту.
Вот, собственно и все на сегодня. В следующих уроках мы рассмотрим рендеринг средствами OpenGL.
Скачать исходники
|