OpenGL v Delphi 4



První trojúhelník

Příklad a zdroják ke stažení (209k)

V tomto tutorialu se dozvíte, jak vytvořit základní strukturu OpenGL aplikace a jak nakreslit trojúhelník.

Abychom mohli používat OpenGL příkazy, budeme potřebovat knihovny OpenGL32.pas a Geometry.pas
program first;

uses
  Forms,
  first_f in 'first_f.pas' {Form1},
  OpenGL in 'gl/OpenGL.pas',    //načti OpenGL.pas
  Geometry in 'gl/Geometry.pas',    //načti Geometry.pas
  KThread in 'KThread.pas',
  DrawObjects in 'DrawObjects.pas';

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
Vytvoříme si formulář, do kterého budeme vykreslovat scénu.
procedure TForm1.FormCreate(Sender: TObject);
begin
  //nastav OpenGL
  InitOpenGL;  //Inicializace OpenGL ovladačů (OpenGL32.dll, GLU32.dll)
  SetWindowStats(640, 480, 16);    //nastav rozlišení 640*480 16 bitovou hloubku barev
  InitWindow;    //nastav okno pro rendering
  InitArea;    //nastav základní renderovací scénu

  //nastav vlákno - oddělený proces, který bude nepretržitě volat proceduru Loop
  RenderThread := TMyThread.Create(True);    //vytvoř první vlákno, ve kterém poběží rendering scény
  RenderThread.Priority := tpTimeCritical;    //vlákno získá nejvyšší prioritu
                         //tpIdle;    //vlákno je prováděno, když systém je nečinný
                         //tpLowest;    //priorita vlákna je dva body pod normálem
                         //tpLower;    //priorita vlákna je jeden bod pod normálem
                         //tpNormal;    //vlákno má normálni prioritu
                         //tpHigher;    //priorita vlákna je jeden bod nad normálem
                         //tpHighest;    //priorita vlákna je dva body nad normálem


  RenderThread.Run;    //rozjeď toto vlákno
end;
Nejdříve nastavíme OpenGL a pak vlákno TThread, ve kterém nepřetržitě voláme proceduru Loop;. Priorita vlákna TThread je nastavena na maximum Priority = tpTimeCritical;, aby jiná činnost systému nezpomalovala překreslování OpenGL objektů.
procedure TForm1.SetWindowStats(Width,Height,Color: longint);
var
  dmScreenSettings: DEVMODE;    // Device Mode - mód zařízení
begin
    // zeptej se, jestli budu chtít okno přepnout do fullscreenu
    if Application.MessageBox(
        'Would You Like To Run In Fullscreen Mode?',
        'Start FullScreen?',
        MB_YESNO + MB_ICONQUESTION) =IDYES
    then begin
      //pokud zadám Ano nastav tyto hodnoty
      ZeroMemory( @dmScreenSettings, sizeof( DEVMODE ) );    //vyčisti pamět
      dmScreenSettings.dmSize := sizeof( DEVMODE );    //velikost struktury
      dmScreenSettings.dmPelsWidth  := Width;   //nastav šířku obrazovky
      dmScreenSettings.dmPelsHeight := Height;    //nastav výšku obrazovky
      dmScreenSettings.dmFields     := DM_PELSWIDTH or DM_PELSHEIGHT;
      dmScreenSettings.dmBitsPerPel := Color;    //nastav hloubku barev
      ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN);    //přepni do fullscreenu
      Showcursor(false);    //schovej kurzor myši
    end else begin
      //pokud nechci fullscreen
      Form1.WindowState := wsNormal;    //přepni maximalizované okno na Normal
      Form1.BorderStyle := bsSizeable;    //a nastav, standardní okno
      Form1.ClientWidth := Width;    //přečti šířku okna
      Form1.ClientHeight := Height;    //přečti výšku okna
      Form1.Left := 0;    //nastav levý okraj okna na 0
      Form1.Top := 0;    //nastav horní okraj okna na 0
    end;
end;
Funkce SetWindowStats() nám vyplivne zprávu, ve které se nás zeptá, jestli budeme chtít aplikaci rozjet v okně, nebo ve fullscreenu. Podle volby, kterou si zvolíme pak změní rozlišení obrazovky a velikost formuláře.

Formulář je připraven na fullscreen,
Form1.WindowState := wsMaximized; //maximalizované okno
Form1.BorderStyle := bsNone; //okno bez rámečku
takže když potvrdíme nabídku pro fullscreen, tak se přepne rozlišení a okno se automaticky roztáhne přes celou obrazovku.
Pokud nebudeme chtít fullscreen, přepne se okno z wsMaximized na wsNormal a vytvoří se okraje z bsNone na bsSizeable
procedure TForm1.InitWindow;
begin
  SetPixelFormatDescriptor(Form1.Canvas.Handle);    //nastav formát výstupního pixelu
  RC := wglCreateContext(Form1.Canvas.Handle);     //vytvoř RC (OpenGL Rendering Context)
  //připoj RC (OpenGL Rendering Context) do DC (Windows Device Context), který komunikuje s wokenim GDI (Graphics Device Interface)
  wglMakeCurrent(Form1.Canvas.Handle,RC);
  glClearColor(0.0,0.0,0.0,0.0);    //nastav černé pozadí scény
  wglMakeCurrent(0,0);    //vynuluj RC (OpenGL Rendering Context)
end;
Funkce InitWindow; nastaví Form1 jako okno pro rendering. Barva pozadí se nastavuje ve funkci glClearColor(0.0, 0.0, 0.0, 0.0); (černé pozadí)
Hodnoty barev jsou glClearColor(red, green, blue, alfa);
procedure TForm1.SetPixelFormatDescriptor;    //nastavení pixelového formátu
var
  pfd: TPIXELFORMATDESCRIPTOR;
begin
  with pfd do begin
    nSize := sizeof(TPIXELFORMATDESCRIPTOR);    //velikost descriptoru
    nVersion := 1;    //číslo verze
    dwFlags := PFD_DRAW_TO_WINDOW or
                  PFD_SUPPORT_OPENGL or
                  PFD_DOUBLEBUFFER;    //formáty podporující wokna, OpenGL, nastavení doublebufferu
    iPixelType := PFD_TYPE_RGBA;    //používej barvy ve formátu RGBA
    cColorBits := 24;    //buffer barev
    cDepthBits   := 32;    //32Bit Z-Buffer (Depth buffer)
    iLayerType   := PFD_MAIN_PLANE;    //vykreslovací vrstva
  end;

  m_iPixelFormat := ChoosePixelFormat(DC, @pfd);    //vyber nejvhodnější pixelový formát
  SetPixelFormat(DC, m_iPixelFormat,@pfd);    //a tento pixelovy formát nastav
end;
Funkce SetPixelFormatDescriptor nastavuje formát pixelu obrazovky
procedure TForm1.InitArea;
begin
  glMatrixMode(GL_PROJECTION);    //nastav matici zobrazení prostoru
  glLoadIdentity;    //použij tuto matici
  //Nastav úhel pohledu kamery, poměr výšky a šířky okna, nejbližší a nejvzdálenější viditelný bod
  gluPerspective(45,Form1.ClientWidth / Form1.ClientHeight,0.1,100);
  glViewport(0,0,Form1.ClientWidth,Form1.ClientHeight);    //nastav velikost zobrazovaci plochy
end;
Funkce InitArea nastavuje renderovací scénu.

Hodnoty funkce gluPerspective(45,Form1.ClientWidth / Form1.ClientHeight,0.1,100); jsou:
gluPerspective(zorný úhel kamery, poměr šířky a výšky projekčního plátna (formuláře), nejbližší bod, nejvzdálenější bod);

Hodnoty funkce glViewport(0, 0, ClientWidth, ClientHeight); jsou:
glViewport(x,y,šířka,výška);
x,y je levý-dolní roh zobrazovací plochy
procedure TForm1.Loop;
begin
  //připoj RC (OpenGL Rendering Context) do DC (Windows Device Context), který komunikuje s wokenim GDI (Graphics Device Interface)
  wglMakeCurrent(Form1.Canvas.Handle,RC);
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);    //vyčisti obrazovkový buffer
  RenderGLScene;   //volej proceduru pro rendering objektu
  SwapBuffers(Form1.Canvas.Handle);    //prohoď buffery (double burrefing)
  wglMakeCurrent(0,0);    //vynuluj RC (OpenGL Rendering Context)
end;
Funkce Loop běží v nekonečné smyčce a volá funkci RenderGLScene; ve které budeme vykreslovat objekty.

Funkce Loop běží v odděleném procesu TThread.
unit KThread;

interface

uses Classes;

type
  TMyThread=class(TThread)
  private
    procedure Render;     //překreslování scény
  protected
  public
    constructor Create(Suspend:Boolean);    //vytvořeni vlákna
    procedure Execute; override;     //činnost vlákna
    destructor Destroy; override;    //destruktor vlákna
    procedure Run;    //spuštění vlákna
    procedure Done;    //ukončení vlákna
  end;

var
  RenderThread : TMyThread;    //proměnná RenderThread typu TMyThread

implementation

uses first_f;


procedure TMyThread.Run;
begin
  RenderThread.Resume;    //spusť vlákno
end;

procedure TMyThread.Done;
begin
  RenderThread.Destroy;    //volej destruktor vlákna
end;

constructor TMyThread.Create(Suspend:Boolean);
begin
  inherited Create(Suspend);    //vytvoř vlákno
end;

procedure TMyThread.Execute;
begin
  while not Terminated do Synchronize(Render);    //pokud neni vlákno ukončeno, volej Render
end;

procedure TMyThread.Render;
begin
  Form1.Loop;    //volej proceduru Loop pro překreslování scény
end;

destructor TMyThread.Destroy;
begin
  inherited Destroy;     //ukonči aplikaci
end;

end.
Priorita vlákna TThread je nastavena na maximum Priority = tpTimeCritical;, aby jiná činnost systému nezpomalovala překreslování OpenGL objektů. Vlákno vytváříme, nastavujeme prioritu a spouštíme při načítání formuláře TForm1.FormCreate().
//nastav vlákno - oddělený proces, který bude nepretržitě volat proceduru Loop
  RenderThread := TMyThread.Create(True);    //vytvoř první vlákno, ve kterém poběží rendering scény
  RenderThread.Priority := tpTimeCritical;    //vlákno získá nejvyšší prioritu
                         //tpIdle;    //vlákno je prováděno, když systém je nečinný
                         //tpLowest;    //priorita vlákna je dva body pod normálem
                         //tpLower;    //priorita vlákna je jeden bod pod normálem
                         //tpNormal;    //vlákno má normálni prioritu
                         //tpHigher;    //priorita vlákna je jeden bod nad normálem
                         //tpHighest;    //priorita vlákna je dva body nad normálem


  RenderThread.Run;    //rozjeď toto vlákno
Funkce Loop volá v nekonečné smyčce funkci RenderGLScene, ve které budeme vykrelovat všechny objekty.
procedure TForm1.RenderGLScene;
begin
  DrawTriangles;    //vykresli trojúhelník
end;
Ve funkci RenderGLScene budeme volat funkce pro vytváření OpenGL objektů. Nejzákladnější objekt je trojúhelník, ze kterého lze vytvořit jakýkoli složitější objekt. Všechny grafické karty podporující OpenGL mají instrukce, které urychlují vykreslování trojúhelníků. Pokud tedy budeme chtít, aby naše aplikace běžela opravdu plynule, je nejlepší všechny objekty tvořit z trojúhelníků glBegin(GL_TRIANGLES);, nebo ještě rychleji se překreslují trojúhelníky glBegin(GL_TRIANGLE_STRIP);, nebo glBegin(GL_TRIANGLE_FAN);, avšak u těchto dvou funkcí jsou menší omezení v případě texturování, ale to už moc předbíhám.
procedure DrawTriangles;
begin
  glPushMatrix;    //začátek prostorové matice
  glTranslatef(-1.5,0.0,-6.0);    //pohni trojúhelníkem o 1.5 doleva o 6.0 od kamery
  glBegin(GL_TRIANGLES);    //začni vykreslovat trojúhelník
    glVertex3f( 0.0, 1.0, 0.0);    //horní bod
    glVertex3f(-1.0,-1.0, 0.0);    //dolní levý bod
    glVertex3f( 1.0,-1.0, 0.0);    //dolní pravý bod
  glEnd();  //ukonči vykreslování trojúhelníka
  glPopMatrix;    //konec prostorové matice
end;
Mezi glPushMatrix; a glPopMatrix; budeme vykreslovat objekt. Tyto příkazy jsou tu proto, aby pohyb a rotace tohoto objektu neovlivňoval pohyb a rotaci jiného objektu.

glTranslatef(-1.5,0.0,-6.0); pohybuje celým objektem ve směru x,y,z. Hodnoty funkce jsou: glTranslatef(x,y,z);

glVertex3f( 0.0, 1.0, 0.0); určuje bod objektu. Hodnoty jsou glVertex3f(x,y,z);
procedure TForm1.FormResize(Sender: TObject);
begin
  //připoj RC (OpenGL Rendering Context) do DC (Windows Device Context), který komunikuje s wokenim GDI (Graphics Device Interface)
  wglMakeCurrent(Form1.Canvas.Handle,RC);
  InitArea;    //nastav renderovací scénu
  wglMakeCurrent(0,0);    //vynuluj RC (OpenGL Rendering Context)
end;
Funkce FormResize() nastavi velikost renderovací scény. Při změně velikosti formuláře, se automaticky změní velikost renderovací scény.
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if(Key=VK_ESCAPE) then    //jestliže klepnu na klávesu Esc
    Close;    //ukonči aplikaci
end;
Ve funkci FormKeyDown() budeme volat funkce, které se provedou po klepnutí na určitou klávesu. Zatím tu máme klávesu Esc, která nám ukončí aplikaci.
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if(Form1.BorderStyle = bsNone) then   //pokud je fullscreen
    ChangeDisplaySettings(DEVMODE(nil^), 0);    //nastav původní hodnotu rozlišení obrazovky

  ReleaseDC(Handle, Form1.Canvas.Handle);    //uvolni handle okna DC (Windows Device Context)
  wglMakeCurrent(0,0);    //vynuluj RC (OpenGL Rendering Context)
  wglDeleteContext(RC);    //vymaž RC (OpenGL Rendering Context)

  RenderThread.Done;  //ukonči vlákno
end;
Při uzavírání aplikace se uvolní handle okna, vymaže se OpenGL Rendering Context z paměti a ukončí se vlákno

Stejný tutorial pro C++ najdete zde


Barvy


Home