Меню
  ГЛАВНАЯ       
  CALLIPSO       
  ПРОГРАММЫ
  DELPHI            
 Статьи
 Исходники    
 Компоненты
 Справка        
  C + +                
  ASM                 
  WEB                  
  FLASH             
  ТЕХНОЛОГИИ 
  ИНСТИТУТ      
  ВЕСЕЛЬЕ         
  ССЫЛКИ         
  ФОРУМ            
  ГОСТЕВАЯ       
 


Список статей
Полный список наших статей по Delphi. Пока статей немного, мы решили разместить их спиком. Со временем, очень этого хочется, будут создаваться подразделы с возможностью оставлять комментарий.


OpenGL. Создание класса TOpGL

Дата публикации: 5 марта 2005г.


    OpenGL (Open Graphics Library) – библиотека для работы с 3D графикой. Стандарт OpenGL появился в 1992 году благодаря компании Silicon Graphics и сейчас переживает годы своего самого бурного развития. Сейчас так же можно услышать о такой библиотеки как DirectX, который является библиотекой Windows. Мне кажется, для начала ознакомления с основами 3D графики лучшим решением для этого будет OpenGL.
    Эта статья предназначена для начального ознакомления с основами работы на OpenGL. Все то, что рассмотрено в этой статье является начальной информацией, которая публиковалась во многих статьях. Я не могу не остановиться на основах. По ходу выхода статей будем рассматривать возможности OpenGL, и в частности, как его применять в своих проектах (создание поверхностей, что может быть использовано в отображении много параметрических функций; игры, движения и т.д.).

1. Начало работы


     Я бы посоветовал бы научиться создавать собственные классы. Это позволит вам упростить вашу работу. Так что, если вы не научились создавать классы, то параллельно с 3D графикой я постараюсь вам это объяснить. Первое наперво, в области interface опишем класс:

type TOpGL = class(TObject)
  private

  protected

  public
    constructor Create(Handle: HDC);
    destructor Destroy;
  published
end;
...

Т.е. здесь у нас класс TOpGL является наследником TObject.
private - здесь описываются только внутренние переменные и процедуры, которым можно получить доступ только изнутри класса.
public – описываются переменные и процедуры, которым может пользоваться и пользователь.
Остальные пока мы рассматривать не будем.

    Подключим модуль, который входит в состав библиотеки Delphi.

uses
  Windows, OpenGL;

    Если подключил, знай себе твори чудеса, только бы приходили идеи и бегали пальцы по клаве… Это я немного отвлекся:)
В этом модуле описаны все функции 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.


2. Инициализация OpenGL

    Попробуйте инициализацию следующего вида, не очень сложный вариант...

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 – устанавливаем матрицу видового преобразования.

3. Рисуем

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 К)
Дизайн и разработка: Callipso e-mail: None