|
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
|
|
Home