Войти
ПрограммированиеСтатьиГрафика

Основы программирования OpenGL в Borland С++Builder и Delphi.

Автор:

На кого рассчитана статья. Я рассчитываю на то, что вы знакомы с азами создания приложений в С++Builder или Delphi и совсем не знаете OpenGL.

Введение
Пример

Введение

OpenGL (Open Graphics Library) - популярная библиотека для работы с 3D графикой. Стандарт OpenGL появился в 1992 году благодаря компании Silicon Graphics и сейчас переживает годы своего самого бурного развития.

Чуть-чуть побольше узнать об OpenGL и о том, как с ним работать в VC, можно почитав wat'а: http://www.gamedev.ru/code/articles/?id=4268

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

Эта - первая - статья посвящена в основном инициализации OpenGL.

Инициализация

Первым делом нужно подключить заголовочные файлы:

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
uses OpenGL;

Если вы используете Delphi, то всё необходимое для работы с OpenGL находится в модуле OpenGL.dcu. А если вы используете С++Builder, то подключать придётся несколько файлов:

После подключения заголовочных файлов нужно установить формат пикселей. Я для этой цели использую следующую функцию:

BOOL bSetupPixelFormat(HDC hdc)
{
    PIXELFORMATDESCRIPTOR pfd, *ppfd;
    int pixelformat;
 
    ppfd = &pfd;
 
    ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR);
    ppfd->nVersion = 1;
    ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    ppfd->dwLayerMask = PFD_MAIN_PLANE;
    ppfd->iPixelType = PFD_TYPE_RGBA;
    ppfd->cColorBits = 16;
    ppfd->cDepthBits = 16;
 
    ppfd->cAccumBits = 0;
    ppfd->cStencilBits = 0;
 
    if ((pixelformat = ChoosePixelFormat(hdc, ppfd)) == 0)
    {
        MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK);
        return FALSE;
    }
    if (SetPixelFormat(hdc, pixelformat, ppfd) == FALSE)
    {
        MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
        return FALSE;
    }
    return TRUE;
}

function bSetupPixelFormat(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);
        bSetupPixelFormat:=FALSE;
        exit;
    end;
 
    if SetPixelFormat(dc, pixelformat, ppfd)=false then
    begin
        MessageBox(0, 'SetPixelFormat failed', 'Error', MB_OK);
        bSetupPixelFormat:=FALSE;
        exit;
    end;
 
    bSetupPixelFormat:=TRUE;
end;

Вряд ли вам придётся менять что-нибудь в этой функции, но кое-что о структуре PIXELFORMATDESCRIPTOR сказать надо.
cColorBits - глубина цвета
cDepthBits - размер буфера глубины (Z-Buffer)
cStencilBits - размер буфера трафарета (мы его пока не используем)
iPixelType - формат указания цвета. Может принимать значения PFD_TYPE_RGBA (цвет указывается четырьмя параметрами RGBA - красный, зленный, синий и альфа) и PFD_TYPE_COLORINDEX (цвет указывается индексом в палитре). Как вы видите, я использую RGBA, и вам придётся поступить также, т.к. если вы захотите использовать COLORINDEX, то вам придётся изменить мою функцию: добавить пару флагов и дать начальные значения ещё нескольким переменным.

Более подробную информацию смотрите в справочнике или в MSDN.

Функция ChoosePixelFormat() подбирает формат пикселей, максимально удовлетворяющий нашим требованиям, и возвращает его дескриптор, а SetPixelFormat() устанавливает его в контексте устройства (dc).

После того как в контексте устройства установлен формат пикселей, нужно создать контекст воспроизведения (Rendering Context) для этого в OpenGL определены следующие функции:

HGLRC wglCreateContext(HDC hdc);
BOOL wglMakeCurrent(HDC hdc, HGLRC hglrc);

function wglCreateContext(dc: HDC): HGLRC;
function wglMakeCurrent(dc: HDC; glrc: HGLRC):Boolean;

Наверное, объяснять их значение не стоит :)

Теперь перейдём к форме. В объявлении класса формы в области private добавьте следующее:

HGLRC ghRC;
HDC   ghDC;
void Draw();

ghRC:HGLRC;
ghDC:HDC;
procedure Draw;

ghRC - указатель на контекст воспроизведения (Rendering Context)

ghDC - дескриптор устройства (для нас - просто указатель на окно)

Процедура Draw будет отвечать за рисование.

Далее заполняем FormCreate:

void __fastcall TForm1::FormCreate(TObject *Sender)
{
   ghDC = GetDC(Handle);
   if (!bSetupPixelFormat(ghDC))
      Close();
   ghRC = wglCreateContext(ghDC);
   wglMakeCurrent(ghDC, ghRC);
 
   glClearColor(0.0, 0.0, 0.0, 0.0);
 
   FormResize(Sender);
 
   glEnable(GL_COLOR_MATERIAL);
   glEnable(GL_DEPTH_TEST);
 
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
     float p[4]={3,3,3,1},
           d[3]={-1,-1,-3};
   glLightfv(GL_LIGHT0,GL_POSITION,p);
   glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,d);
}

procedure TForm1.FormCreate(Sender: TObject);
var
   p: TGLArrayf4;
   d: TGLArrayf3;
begin
   ghDC := GetDC(Handle);
   if bSetupPixelFormat(ghDC)=false then
     Close();
   ghRC := wglCreateContext(ghDC);
   wglMakeCurrent(ghDC, ghRC);
 
   glClearColor(0.0, 0.0, 0.0, 0.0);
 
   FormResize(Sender);
 
   glEnable(GL_COLOR_MATERIAL);
   glEnable(GL_DEPTH_TEST);
 
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
     p[0]:=3;
     p[1]:=3;
     p[2]:=3;
     p[3]:=1;
     d[0]:=-1;
     d[1]:=-1;
     d[2]:=-3;
   glLightfv(GL_LIGHT0,GL_POSITION,@p);
   glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,@d);
end;

Вы видите, что тут вызывается FromResize, который мы ещё не описали. Надо это исправить. Поместите туда следующий код:

   glViewport( 0, 0, Width, Height );
   glMatrixMode( GL_PROJECTION );
   glLoadIdentity();
   glOrtho(-5,5, -5,5, 2,12);
   gluLookAt(0,0,5, 0,0,0, 0,1,0);
   glMatrixMode( GL_MODELVIEW );

Теперь, наверное, надо кое-что объяснить.

glClearColor() устанавливает цвет (в нашем случае чёрный), которым будет заполняться экран при очищении. У этой процедуры - 4 параметра, что соответствует RGBA. Вместо нее можно написать glClearIndex(0.0) . Эта процедура устанавливает индекс цвета в палитре.

glViewport() устанавливает область вывода - область, в которую OpenGL будет выводить изображение. В нашем случае - вся форма.

glMatrixMode() устанавливает режим матрицы видового преобразования. Не забивайте ей себе голову, просто запомните, что, если вы меняете тип проецирования, положение или направление камеры, то параметр должен быть GL_PROJECTION. После того, как вы завершили свои изменения, вызовите эту процедуру с параметром GL_MODELVIEW.

glLoadIdentity() заменяет текущую матрицу видового преобразования на единичную.

glOrtho() устанавливает режим ортогонального (прямоугольного) проецирования. Это значит, что изображение будет рисоваться как в изометрии. 6 параметров типа GLdouble (или просто double): left, right, bottom, top, near, far определяют координаты соответственно левой, правой, нижней, верхней, ближней и дальней плоскостей отсечения, т.е. всё, что окажется за этими пределами, рисоваться не будет. На самом деле эта процедура просто устанавливает масштабы координатных осей. Для того чтобы установить перспективное проецирование, используются процедуры glFrustum() и gluPerspective(), но о них - потом.

gluLookAt() устанавливает параметры камеры: первая тройка - её координаты, вторая - вектор направления, третья - направление оси Y.

В OpenGL всё включается и выключается (разрешается и запрещается) процедурами glEnable() и glDisable(). Таким образом, мы разрешили тест глубины (GL_DEPTH_TEST), чтобы изображение было объёмным, разрешили давать нашим объектам какой-то цвет (GL_COLOR_MATERIAL), разрешили освещение (GL_LIGHTING) и включили "лампочку №0" (GL_LIGHT0).

glLightfv() устанавливает свойства "лампочек": позицию и направление света.

После того, как вы завершили работу с OpenGL, нужно освободить занятые ресурсы: освободить контекст, вызвав wglMakeCurrent с параметром ноль для идентификатора контекста OpenGL и разрушить этот контекст функцией wglDeleteContext. Кроме того нужно удалить дескриптор ghDC. Так как обычно работу с OpenGL завершается при завершении работы приложения, то соответствующий код нужно поместить в FormClose:

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
  if(ghRC)
  {
    wglMakeCurrent(ghDC,0);
    wglDeleteContext(ghRC);
  }
  if(ghDC)
    ReleaseDC(Handle, ghDC);
}
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if ghRC<>0 then
  begin
    wglMakeCurrent(ghDC,0);
    wglDeleteContext(ghRC);
  end;
  if ghDC<>0 then
    ReleaseDC(Handle, ghDC);
end;

А теперь, давайте уже что-нибудь нарисуем!

Страницы: 1 2 Следующая »

#Delphi, #OpenGL, #основы

2 мая 2003 (Обновление: 20 мая 2011)

Комментарии [30]