|
|
| Список статей |
|
Полный список наших статей по Delphi. Пока статей немного, мы решили разместить их
спиком. Со временем, очень этого хочется, будут создаваться подразделы с возможностью
оставлять комментарий.
|
|
| OpenGL. Создание класса TOpGL |
Дата публикации: 5 марта 2005г.
OpenGL (Open Graphics Library) – библиотека для работы с 3D графикой.
Стандарт OpenGL появился в 1992 году благодаря компании Silicon Graphics и сейчас переживает
годы своего самого бурного развития. Сейчас так же можно услышать о такой библиотеки как DirectX,
который является библиотекой Windows. Мне кажется, для начала ознакомления с основами 3D графики
лучшим решением для этого будет OpenGL.
Эта статья предназначена для начального ознакомления с основами работы на OpenGL.
Все то, что рассмотрено в этой статье является начальной информацией, которая публиковалась во
многих статьях. Я не могу не остановиться на основах. По ходу выхода статей будем рассматривать возможности OpenGL,
и в частности, как его применять в своих проектах (создание поверхностей, что может быть использовано в отображении
много параметрических функций; игры, движения и т.д.).
Я бы посоветовал бы научиться создавать собственные классы. Это позволит вам упростить вашу работу.
Так что, если вы не научились создавать классы, то параллельно с 3D графикой я постараюсь вам это объяснить.
Первое наперво, в области interface опишем класс:
type
TOpGL = class(TObject)
private
protected
public
constructor Create(Handle: HDC);
destructor Destroy;
published
end;
...
|
Т.е. здесь у нас класс TOpGL является наследником TObject.
private - здесь описываются только внутренние переменные и
процедуры, которым можно получить доступ только изнутри класса.
public – описываются переменные и процедуры, которым может
пользоваться и пользователь.
Остальные пока мы рассматривать не будем.
Подключим модуль, который входит в состав библиотеки Delphi.
Если подключил, знай себе твори чудеса, только бы приходили идеи и бегали пальцы по клаве… Это я немного отвлекся:)
В этом модуле описаны все функции OpenGL из библиотеки Windows opengl32.dll и glu.dll.
Что же делать дальше? Необходимо установить формат пикселей. Я для этой цели использую следующую функцию:
public
...
function bSetPixelFormat(DC: HDC): boolean;
...
|
Если теперь нажать комбинацию клавиш CTRL+SHIFT+C (нажми… жми… не бойся).
Понравилось? Вообще-то, должно было появиться это:
function TOpGL.bSetPixelFormat(DC: HDC): boolean;
begin
end;
|
Теперь туда допиши код:
function TOpGL.bSetPixelFormat(DC: HDC): boolean;
var
pfd: PIXELFORMATDESCRIPTOR;
ppfd: PPIXELFORMATDESCRIPTOR;
pixelformat: integer;
begin
ppfd := @pfd;
ppfd.nSize := sizeof(PIXELFORMATDESCRIPTOR);
ppfd.nVersion := 1;
ppfd.dwFlags := PFD_DRAW_TO_WINDOW xor
PFD_SUPPORT_OPENGL xor
PFD_DOUBLEBUFFER;
ppfd.dwLayerMask := PFD_MAIN_PLANE;
ppfd.iPixelType := PFD_TYPE_RGBA;
ppfd.cColorBits := 16;
ppfd.cDepthBits := 16;
ppfd.cAccumBits := 0;
ppfd.cStencilBits := 0;
pixelformat := ChoosePixelFormat(dc, ppfd);
if pixelformat = 0 then
begin
MessageBox(0, 'ChoosePixelFormat failed', 'Error', MB_OK);
Result := False;
Exit;
end;
if not SetPixelFormat(dc, pixelformat, ppfd) then
begin
MessageBox(0, 'SetPixelFormat failed', 'Error', MB_OK);
Result := False;
Exit;
end;
Result := True;
end;
|
Всё просто... Попытаюсь по возможности объяснить назначение данной функции
(знание которого, по-моему мнению, нет необходимости, просто написал и забыл...).
Здесь:
cColorBits – глубина цвета
cDepthBits – размер буфера глубины (Z-Buffer)
cStencilBits – размер буфера трафарета (мы его пока не используем).
iPixelType – формат указания цвета. Может принимать значения PFD_TYPE_RGBA
(цвет указывается четырьмя параметрами RGBA - красный, зленный, синий и альфа) и
PFD_TYPE_COLORINDEX (цвет указывается индексом в палитре).
Функция ChoosePixelFormat подбирает формат пикселей, максимально
удовлетворяющий нашим требованиям, и возвращает его дескриптор, а SetPixelFormat
устанавливает его в контексте устройства (dc).
Необходимая функция для инициализации ЖЛки имеется. Нам нужно инициализировать
OpenGL, напишем соответствующую процедуру (вот там и будем использовать bSetPixelFormat)
Опишем указатель на дескриптор устройства (ghDC) и handle окна (Handle) в private.
Попробуйте инициализацию следующего вида, не очень сложный вариант...
procedure TOpGL.InitGL;
const
pos: TGLArrayf4 = (3, 3, 3, 1);
dir: TGLArrayf3 = (-1, -1, -3);
var
ghRC: HGLRC;
begin
if not bSetPixelFormat(FghDC) then //1
Exit;
ghRC := wglCreateContext(FghDC); //2
wglMakeCurrent(FghDC, ghRC); //3
glClearColor(0.0, 0.0, 0.0, 0.0); //4
glEnable(GL_COLOR_MATERIAL); //5
glEnable(GL_DEPTH_TEST); //6
glEnable(GL_LIGHTING); //7
glEnable(GL_LIGHT0); //8
glLightfv(GL_LIGHT0, GL_POSITION, @pos); //9
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, @dir); //10
end;
|
Эту процедуру, уходя вперед скажу, будем использовать в этапе создания класса, т.е. в процедуре Create.
Процедура есть, но что там и как работает:
1 – В начале делаем попытку задать формат пикселя;
2 – Создаем контекст воспроизведения в текущем устройстве;
3 – Устанавливается текущий контекст воспроизведения
4 – Устанавливаем цвет который будет использоваться для очистки области вывода, у этой процедуры – 4 параметра, что соответствует RGBA;
5 – разрешили давать нашим объектам какой-то цвет;
6 – разрешили тест глубины;
7 – разрешили освещение;
8 – включили лампочку 0.
9 – устанавливаем позицию лампы;
10 – устанавливаем направление лампы, которые заданы в виде матрицы.
procedure TOpGL.ResetGL(Left, Top, ClientWidth, ClientHeight: Integer);
begin
glViewport(Left, Top, Left + ClientWidth,
Top + ClientHeight); //1
glMatrixMode(GL_PROJECTION); //2
glLoadIdentity(); //3
glOrtho(-5,5, -5,5, 2,12); //4
gluLookAt(0,0,5, 0,0,0, 0,1,0); //5
glMatrixMode(GL_MODELVIEW); //6
glLoadIdentity();
end;
|
Здесь выполняются следующие действия:
1 – задаем область вывода изображения OpenGL (обычно это вся форма);
2 – устанавливаем режим матрицы видового преобразования, т.е. такая установка нужна, если мы меняем тип проецирования, положение или направление камеры;
3 – сбрасываем все изменения осуществлявшимися (еле выговорил:-)) матрицей;
4 – устанавливаем режим ортогонального (прямоугольного) проецирования, т.е. изображение будет рисоваться как в изометрии, 6 параметров типа GLdouble (или просто double): left, right, bottom, top, near, far определяют координаты соответственно левой, правой, нижней, верхней, ближней и дальней плоскостей отсечения, т.е. всё, что окажется за этими пределами, рисоваться не будет. На самом деле эта процедура просто устанавливает масштабы координатных осей. Для того чтобы установить перспективное проецирование, используются процедуры glFrustum и gluPerspective, которые дают более реальное проецирование, где чем дальше объект, тем он меньше;
5 – устанавливаем параметры камеры: первая тройка – её координаты, вторая – вектор направления, третья – направление оси Y;
6 – устанавливаем матрицу видового преобразования.
procedure TOpGL.Draw;
var
quadObj: GLUquadricObj;
begin
glClear(GL_DEPTH_BUFFER_BIT xor GL_COLOR_BUFFER_BIT); //1
quadObj := gluNewQuadric;
//2
gluQuadricDrawStyle(quadObj, GLU_FILL); //3
glColor3f(1, 0, 0); //4
gluSphere(quadObj, 2, 10, 10); //5
glRotatef(3, 0.1, 1, 0.3); //6
gluDeleteQuadric(quadObj); //7
SwapBuffers(FghDC); //8
end;
|
1 – очищаем буфер цвета и буфер глубины, так же можно использовать GL_STENCIL_BUFFER_BIT (буфер трафарета);
2 – Создаём объект типа GLUquadricObj из библиотеки glu32.dll и инициализируем его функцией gluNewQuadric;
3 – устанавливаем стиль фигуры, стиль может быть GLU_FILL, GLU_LINE или GLU_POINT;
4 – установим текущий цвет в 3-мя компонентами типа GLfloat (бывают окончания d – Gldouble, b – GLbyte и т.д.);
5 – создали из quadObj сферу, где три последних параметра – это радиус и количество разбиений поперёк и вдоль оси Z соответственно;
6 – поворачиваем сферу (точнее систему координат) на угол 3 вокруг вектора (0.1, 1, 0.3);
7 – освобождаем память занимаемый объектом quadObj;
8 – выведем все на экран.
Для того чтобы наш класс был функционально законченным, допишем процедуры создания и удаления класса:
constructor TOpGL.Create(Handle: HDC);
begin
FHabdle:= Handle;
FghDC := GetDC(FHabdle);
InitGL;
end;
destructor TOpGL.Destroy;
begin
if FghDC<>0 then
wglMakeCurrent(FghDC, 0);
if ghDC<>0 then
ReleaseDC(FHabdle, FghDC);
end;
|
Посмотрите что мы должны были получить:
unit GL;
interface
uses
Windows, OpenGL;
type
TOpGL = class(TObject)
private
FghDC: HDC;
FHabdle: HDC;
public
constructor Create(DC: HDC);
destructor Destroy;
function bSetPixelFormat(DC: HDC): boolean;
procedure InitGL;
procedure ResetGL(Left, Top, ClientWidth, ClientHeight: integer);
procedure Draw;
end;
implementation
{ TOpGL }
function TOpGL.bSetPixelFormat(DC: HDC): boolean;
var
pfd: PIXELFORMATDESCRIPTOR;
ppfd: PPIXELFORMATDESCRIPTOR;
pixelformat: integer;
begin
ppfd := @pfd;
ppfd.nSize := sizeof(PIXELFORMATDESCRIPTOR);
ppfd.nVersion := 1;
ppfd.dwFlags := PFD_DRAW_TO_WINDOW xor
PFD_SUPPORT_OPENGL xor
PFD_DOUBLEBUFFER;
ppfd.dwLayerMask := PFD_MAIN_PLANE;
ppfd.iPixelType := PFD_TYPE_RGBA;
ppfd.cColorBits := 16;
ppfd.cDepthBits := 16;
ppfd.cAccumBits := 0;
ppfd.cStencilBits := 0;
pixelformat := ChoosePixelFormat(dc, ppfd);
if pixelformat = 0 then
begin
MessageBox(0, 'ChoosePixelFormat failed', 'Error', MB_OK);
Result := False;
Exit;
end;
if not SetPixelFormat(dc, pixelformat, ppfd) then
begin
MessageBox(0, 'SetPixelFormat failed', 'Error', MB_OK);
Result := False;
Exit;
end;
Result := True;
end;
constructor TOpGL.Create(Handle: HDC);
begin
FHabdle:= Handle;
FghDC := GetDC(FHabdle);
InitGL;
end;
destructor TOpGL.Destroy;
begin
if FghDC<>0 then
wglMakeCurrent(FghDC, 0);
if ghDC<>0 then
ReleaseDC(FHabdle, FghDC);
end;
procedure TOpGL.Draw;
var
quadObj: GLUquadricObj;
begin
glClear(GL_DEPTH_BUFFER_BIT xor GL_COLOR_BUFFER_BIT); //1
quadObj := gluNewQuadric;
gluQuadricDrawStyle(quadObj, GLU_FILL);
glColor3f(1, 0, 0);
gluSphere(quadObj, 2, 10, 10);
glRotatef(3, 0.1, 1, 0.3);
gluDeleteQuadric(quadObj);
end;
procedure TOpGL.InitGL;
const
pos: TGLArrayf4 = (3, 3, 3, 1);
dir: TGLArrayf3 = (-1, -1, -3);
var
ghRC: HGLRC;
begin
if not bSetPixelFormat(FghDC) then
Exit;
ghRC := wglCreateContext(FghDC);
wglMakeCurrent(FghDC, ghRC);
glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_POSITION, @pos);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, @dir);
end;
procedure TOpGL.ResetGL(Left, Top, ClientWidth, ClientHeight: Integer);
begin
glViewport(Left, Top, Left + ClientWidth,
Top + ClientHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-5,5, -5,5, 2,12);
gluLookAt(0,0,5, 0,0,0, 0,1,0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
end;
end.
|
|
4. Использование класса TOpGL |
Рассмотрим, как мы можем использовать наш класс. Добавьте в проект наш модуль, нажмите на панели «быструю» кнопку:
Для того чтобы модуль Unit1.pas мог его использовать, в меню нажмите:
В коде можно увидеть:
implementation
uses
GL;
{$R *.dfm}
|
Проинициализируйте форму таким образом, чтобы при создании формы создавался объект типа нашего класса.
procedure TForm1.FormCreate(Sender: TObject);
begin
Op := TOpGL.Create(Form1.Handle);
end;
|
«Бросьте» на форму таймер Timer1: TTimer. Настройте его – поставьте интервал времени равной 50 мс.
Нажав 2 раза, на нем назначьте обработчик событий OnTimer:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Op.Draw;
end;
|
Т.е. каждые 50 мс наше приложение попытается вывести изображение на форму.
Событие формы:
procedure TForm1.FormResize(Sender: TObject);
begin
Op.ResetGL(0, 0, Width, Height);
end;
|
Будет срабатывать при изменении размера формы.
Хорошим тоном программирования считается, если вы будете следить
за памятью при, когда ваша программа завершает свою работу. При этом необходимо удалять все
созданные вами объекты, такие как, в нашем случае, Op.
procedure TForm1.FormDestroy(Sender: TObject);
begin
Op.Free;
end;
|
Полный исходный код:
unit Unit1;
interface
uses
Windows, Classes, Forms, ExtCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
GL;
{$R *.dfm}
var
Op: TOpGL;
procedure TForm1.FormCreate(Sender: TObject);
begin
Op := TOpGL.Create(Form1.Handle);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Op.Draw;
end;
procedure TForm1.FormResize(Sender: TObject);
begin
Op.ResetGL(0, 0, Width, Height);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Op.Free;
end;
end.
|
Смотрим на полученное...
Этот урок был что-то вроде ознакомления с основами 3D графики посредством OpenGL.
Следующем уроке более детально рассмотрим движение экрана, что может быть использован в играх.
Дальше будем улучшать наш класс...
Скачать проект: OpGL.zip (8.83 К)
|
|
|
|