|
Основы 1: Библиотека OpenGL
DirectX, OpenGL и видеокарта.
Я думаю, многие из читателей играют в компьютерные игры с великолепной трехмерной графикой или хоть раз смотрели потрясающие графические демонстрационные программы (демки).
Известно, что многие из них использую графические библиотеки, такие как OpenGL и DirectX.
Но что собой представляют эти библиотеки?
Полагаю, вы не раз видели при установки DirectX богатый набор файлов (d3d9.dll, ddraw.dll, dinput.dll, dsound.dll, и др.).
Они содержат в себе основные компоненты DirectX: Direct3D (3D), DirectDraw (2D), DirectInput (ввод/вывод), DirectSound (звук) и др., встраивающиеся в систему через COM-интерфейс.
Библиотека OpenGL представляет собой всего один файл, opengl32.dll, расположенный в системной директории.
Это уже обычная DLL с огромным набором низкоуровневых функций.
Этот файл поставляется вместе с Windows.
Но как же тогда обновляются или совершенствуются возможности библиотеки, если файл не изменяется?
DirectX, напротив, обновляется чуть ли не каждый месяц.
Дело в том, что DirectX и OpenGL, в основном, не общаются с видеокартой напрямую, а используют в качестве надежного посредника ее драйвер.
Вот, например, схема взаимодействия приложения и монитора компьютера с видеокартой ATI Radeon:

Отсюда видно, что DirectX и OpenGL являются аппартно-независимыми, т.к. общаются с драйверами, которые у каждой видеокарты свои (и, естественно, являются аппартно-зависимыми).
Однако, OpenGL не зависит также и от операционной системы (что нельзя сказать о DirectX) и содержится в Windows, Linux, MacOS и др.
Независимость и простой API интерфейс позволяют OpenGL стать интересным предметом изучения трехмерной графики для новичков и профессионалов.
А система расширений OpenGL позволяет использовать эту библиотеку для создания сложнейших сцен с великолепной графикой.
На основе OpenGL были написаны такие игры, как Quake 1,2,3,4 и Doom 3, а также почти все демки (для интересующихся: Scene.Org).
Подключение OpenGL
Итак, как было уже сказано, OpenGL в первую очередь представляет собой набор функций (API), которые можно использовать в своих программах.
В этой и во всех следующих статьях будет использоваться язык программирования Delphi, который, как считает автор, наиболее легкий для понимания.
Несмотря на это, к примерам на Delphi будут прилагаться примеры на С++ (с и без использования вспомогательной библиотеки GLUT), которые будут одинаково успешно компилироваться как на Windows, так и на Linux.
Автор нашел, по крайней мере, 3 способа подключения функций OpenGL к программе:
1. Во время компиляции программы. Этим способом в Delphi 7 функции просто объявлялись внешними, содержащимися в opengl32.dll.
Язык Delphi
procedure glBegin(mode: GLenum); stdcall;
external 'opengl32.dll';
procedure glEnd; stdcall;
external 'opengl32.dll';
procedure glVertex2f(x,y: GLfloat); stdcall;
external 'opengl32.dll';
Адреса функций автоматически загружались при загрузке программы.
Это удобно, если не используются расширения OpenGL.
Иначе необходимо вручную загружать адреса.
2. Во время загрузки программы. В таком случае необходимо в отдельной функции загрузить адреса всех функций OpenGL (а также расширений, если необходимо) и вызвать ее в самом начале программы до вызова хотя бы одной из этих функций.
Язык Delphi
program Load;
uses
Windows;
var
glBegin: procedure(mode: GLEnum); stdcall;
glEnd: procedure(); stdcall;
glVertex2f: procedure(x, y: GLfloat); stdcall;
//...
procedure LoadOpenGL;
var
lib: HModule;
begin
lib:=LoadLibrary('opengl32');
glBegin:=GetProcAddress(lib,'glBegin');
glEnd:=GetProcAddress(lib,'glEnd');
glVertex2f:=GetProcAddress(lib,'glVertex2f');
//...
end;
begin
LoadOpenGL;
//...
end.
Для того, чтобы загружать адреса функций расширений OpenGL, необходимо использовать функцию wglGetProcAddress:
Язык Delphi
glActiveTextureARB:=wglGetProcAddress('glActiveTextureARB');
3. Во время вызова функции. Это уже более хитрый способ - загрузка адреса функции при ее собственном вызове.
Такой способ автор обнаружил в заголовочной файле для OpenGL 2.0, взятого с сайта OpenGL.Org.
Язык Delphi
program Load;
uses
Windows;
var
glBegin: procedure(mode: TGLenum); stdcall;
glEnd: procedure(); stdcall;
glVertex2f: procedure(x: TGLfloat; y: TGLfloat); stdcall;
//...
procedure STUB_glBegin(mode: TGLenum); stdcall;
begin
glBegin := wglGetProcAddress('glBegin');
glBegin(mode);
end;
procedure STUB_glEnd; stdcall;
begin
glEnd := wglGetProcAddress('glEnd');
glEnd;
end;
procedure STUB_glVertex2f(x: TGLfloat; y: TGLfloat); stdcall;
begin
glVertex2f := wglGetProcAddress('glVertex2f');
glVertex2f(x,y);
end;
//...
procedure InitSTUBs;
begin
glBegin:=STUB_glBegin;
glEnd:=STUB_glEnd;
glVertex2f:=STUB_glVertex2f;
end;
begin
InitSTUBs;
//...
end.
Таким образом адрес функции загружается тогда, когда она первый раз используется.
Здесь можно сделать еще одну полезную вешь: если по каким-либо причинам адрес функции не загружен (может быть такая функция отсутствует), то в "STUB"-функции можно это учесть и не вызывать основную функцию.
Этим можно обойти ошибку неверного доступа к памяти (access violation).
Язык Delphi
procedure STUB_glBegin(mode: TGLenum); stdcall;
var
func: pointer;
begin
func := wglGetProcAddress('glBegin');
if Assigned(func) then
begin
glBegin:=func;
glBegin(mode);
end;
end;
Далее мы просто будем использовать заголовочный файл для OpenGL 2.0 - dglOpenGL.pas.
Скачать его можно здесь.
| |