OpenGL tutorial za Delphi i Lazarus

OpenGL tutorial za Delphi i Lazarus

offline
  • Srđan Tot
  • Am I evil? I am man, yes I am.
  • Pridružio: 12 Jul 2005
  • Poruke: 2483
  • Gde živiš: Ljubljana

Citat:OpenGL is the premier environment for developing portable, interactive 2D and 3D graphics applications. Since its introduction in 1992, OpenGL has become the industry's most widely used and supported 2D and 3D graphics application programming interface (API), bringing thousands of applications to a wide variety of computer platforms. OpenGL fosters innovation and speeds application development by incorporating a broad set of rendering, texture mapping, special effects, and other powerful visualization functions. Developers can leverage the power of OpenGL across all popular desktop and workstation platforms, ensuring wide application deployment.

Vise o OpenGL: http://www.opengl.org/about/overview/

Da vise ne gnjavim ovim stvarima... ko bude citao ovaj tutorial sigurno ce vec znati sta je OpenGL Smile Pokazacemo osnove koriscenja ovog API-ja u Delphi/Lazarus pod Windows-om. Ako neko bude imao zelju, mogao bi da pise i tutorijale za OpenGL pod Linux-om.

Koristicemo WinAPI za kreiranje prozora i obradu poruka i u prvom primeru cemo pokazati kako kreirati prozor i obojiti njegovu povrsinu uz pomoc OpenGL-a.

uses   Windows, Messages, OpenGL; const   AppWidth = 640;   AppHeight = 480; var   winClass: TWndClassEx;   Wnd: HWND;   Position: TRect;   scrWidth, scrHeight: Integer; begin   FillChar(winClass, SizeOf(winClass), 0);   winClass.cbSize := sizeof(WNDCLASSEX);   winClass.style := CS_HREDRAW or CS_VREDRAW;   winClass.lpfnWndProc := @WndProc;   winClass.hInstance := HInstance;   winClass.hCursor := LoadCursor(0, IDC_ARROW);   winClass.lpszClassName := 'OGLTut1Class';   RegisterClassEx(winClass);   Wnd := CreateWindowEx(0, 'OGLTut1Class', 'OpenGL Tutorial 1',     WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, HInstance, nil);   scrWidth := GetSystemMetrics(SM_CXSCREEN);   scrHeight := GetSystemMetrics(SM_CYSCREEN);   Position.Left := Trunc((scrWidth - AppWidth) / 2);   Position.Top := Trunc((scrHeight - AppHeight) / 2);   Position.Right := Position.Left + AppWidth;   Position.Bottom := Position.Top + AppHeight;   AdjustWindowRectEx(Position, WS_OVERLAPPEDWINDOW, False, 0);   SetWindowPos(Wnd, HWND_TOP, Position.Left, Position.Top,     Position.Right - Position.Left, Position.Bottom - Position.Top,     SWP_SHOWWINDOW); . . .

Kao sto vidite, prozor se sasvim normalno kreira. Definise se klasa prozora koja se zatim registruje, kreira se prozor i postavlja mu se pozicija na sredinu ekrana.

Da bi bili u mogucnosti da koristimo OpenGL za crtanje po device context-u prozora, prvo ga moramo pripremiti. To cemo uraditi u sledecoj funkciji:
procedure SetupPixelFormat(DC: HDC); var   PixelFormat: GLuint;   pfdDesc: PIXELFORMATDESCRIPTOR; begin   FillChar(pfdDesc, SizeOf(PIXELFORMATDESCRIPTOR), 0);   pfdDesc.nSize := SizeOf(PIXELFORMATDESCRIPTOR);   pfdDesc.nVersion := 1;   pfdDesc.dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER;   pfdDesc.iPixelType := PFD_TYPE_RGBA;   pfdDesc.cColorBits := 24;   pfdDesc.cDepthBits := 16;   pfdDesc.iLayerType := PFD_MAIN_PLANE;   PixelFormat := ChoosePixelFormat(DC, @pfdDesc);   SetPixelFormat(DC, PixelFormat, @pfdDesc); end;
Ovu proceduru verovatno nikad necete morati da menjate Smile Izabracemo format koji podrzava OpenGL i DoubleBuffering. Trazicemo 24bitnu paletu boja (osam bita za svaku od boja) i 16bitni Z-Buffer (Z-Buffer cemo koristiti da bi se objekti pravilno iscrtavali... npr. objekat u daljini nece biti iscrtan preko objakta koji je blizu bez obzira na red iscrtavanja).

Sada cemo napisati i WinProc proceduru koja odredjuje kako ce se nas prozor ponasati:
var   RC: HGLRC;   DC: HDC; function WndProc(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; var   Width, Height: Integer; begin   Result := 0;   case Msg of     WM_CREATE:     begin       DC := GetDC(Wnd);       SetupPixelFormat(DC);       RC := wglCreateContext(DC);       wglMakeCurrent(DC, RC);     end;     WM_CLOSE, WM_DESTROY:     begin       wglMakeCurrent(DC, 0);       wglDeleteContext(RC);       PostQuitMessage(0);     end;     WM_SIZE:     begin       Width := LOWORD(lParam);       Height := HIWORD(lParam);       if Height = 0 then         Height := 1;       glViewport(0, 0, Width, Height);       glMatrixMode(GL_PROJECTION);       glLoadIdentity;       gluPerspective(45, Width / Height, 0.1, 1000);       glMatrixMode(GL_MODELVIEW);       glLoadIdentity;     end;   else     Result := DefWindowProc(Wnd, Msg, wParam, lParam);   end; end;
WM_CREATE poruku cemo iskoristiti kao signal za kreiranje rendering context-a. Kada ga kreiramo povezujemo ga sa device context-om prozora i spremni smo za crtanje.

Kada prozor treba da se zatvori na nama je da prekinemo vezu izmedju rendering context-a i device context-a i da obrisemo rendering context.

Poslednja poruka koja nam je bitna je WM_SIZE u kojoj pomocu funkcije glViewport odredjujemo koliko ce velik biti prostor u kojem OpenGL moze da crta i postavljamo matricu projekcije (u ovom primeru to nije potrebno posto necemo nista crtati).

Ostatak koda je obicna petlja u kojoj citamo poruke poslate prozoru i ako poruka nema pozivamo iscrtavanje sve dok se prozor ne zatvori:
. . .   InitializeGL;   Msg.message := WM_NULL;   while Msg.message <> WM_QUIT do   begin     if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then     begin       TranslateMessage(Msg);       DispatchMessage(Msg);     end     else     begin       RenderScene;       Sleep(1);     end;   end;
Sleep pozivamo da ne bi imali 100% zauzece procesora... onima kojima je brzina vaznija od zauzeca procesora taj red nije potreban.

Ostalo je jos samo da definisemo 2 funkcije:
procedure InitializeGL; begin   glClearColor(0.18, 0.20, 0.66, 0); end; procedure RenderScene; begin   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);   SwapBuffers(DC); end;
InitializeGL pozivamo pre pocetka crtanja i tu cemo stavljati sva podesavanja koja je dovoljno postaviti jednom.

RenderScene ce iscrtavati nasu scenu i u ovom slucaju ce samo ocistiti pozadinu u boju koju smo postavili u InitializeGL funkciji. Takodje cemo resetovati i Z-Buffer, ali nam to trenutno nije bitno.

Posle renderovanja scene potrebno je da pozovemo SwapBuffers proceduru koja ce prikazati sadrzaj buffera u kojem smo sve iscrtali.

To bi bilo sve za ovaj put.

[url=https://www.mycity.rs/must-login.png source[/url] | [url=https://www.mycity.rs/must-login.png source[/url] | [url=https://www.mycity.rs/must-login.png

Dopuna: 15 Jul 2006 16:52

U ovom primeru cemo dodati samo malo koda u kojem cemo pokazati kako se iscrtavaju vertexi i nesto malo o postavljanju matrica koje uticu na konacnu poziciju objekata koje crtamo.

Prvo cemo analizirati kod koji smo vec napisali. Kada dobijemo WM_SIZE poruku, izmedju ostalog, izvrsava se sledeci kod:
. . . glMatrixMode(GL_PROJECTION); glLoadIdentity; gluPerspective(45, Width / Height, 0.1, 1000); glMatrixMode(GL_MODELVIEW); glLoadIdentity; . . .

glMatrixMode funkcijom odredjujemo koju cemo matricu da podesavamo. U OpenGL postoje 3 matrice koje mozemo menjati:
GL_MODELVIEW: matrica kojom se mnoze koordinate vertexa da bi se postavili na pravu poziciju u sceni.
GL_PROJECTION: matrica koja 3D koordinate prevodi u 2D koordinatu za prikaz na ekranu.
GL_TEXTURE: matrica kojom se mnoze koordinate textura i time odredjuje koji deo texture se prikazuje.

Kada izaberemo matricu koju zelimo da menjamo (prvo smo izabrali projekciju), mozemo pozivati funkcije koje vrse izmene. glLoadIdentity funkcija resetuje matricu tako da ona ne utice na poziciju vertexa. Posto skoro sve funkcije za menjanje matrice mnoze trenutnu matricu podacima koje prosledimo, prvo sto moramo je da matricu resetujemo. Da bismo lakse podesili matricu projekcije imamo funkciju gluPerspective. Parametre koje uzima su sirina vidika u stepenima, odos izmedju sirine i visine, minimalna i maximalna udaljenost vidljivih objekata.

Kada smo podesili matricu projekcije biramo GL_MODELVIEW matricu tako da svaki sledeci poziv funkcijama za menjanje matrice utice na nju.

Ok... sada cemo dodati kod za crtanje:
procedure RenderScene; begin   glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);   glLoadIdentity;   glTranslate(0, 0, -5);   glBegin(GL_TRIANGLES);     glColor3f(1, 0, 0);     glVertex3f(-1, -1, 0);     glColor3f(0, 1, 0);     glVertex3f(1, -1, 0);     glColor3f(0, 0, 1);     glVertex3f(0, 1, 0);   glEnd;   SwapBuffers(DC); end;

glClear funkciju smo vec spominjali u proslom primeru. Ona sluzi da resetuje buffere. Preskocicemo sledece dve funkcije i preci na glBegin.

glBegin funkcija oznacava da cemo da pocnemo sa unosenjem podataka o vertexima koje zelimo da nacrtamo. Parametar koji uzima odredjuje nacin na koji ce se vertexi obraditi:
GL_POINTS: svaki vertex definise posebnu tacku koja ce se iscrtati.

GL_LINES: dva vertexa odredjuju liniju koja ce biti nacrtana... broj vertexa mora biti paran.

GL_LINE_STRIP: crta se linija koja polazi iz prvog vertexa i prolazi kroz svaki naredni.

GL_LINE_LOOP: isto kao i GL_LINE_STRIP samo sto se crta i linija od zadnjeg do prvog vertexa i tako dobija zatvoren oblik.

GL_TRIANGLES: svaka 3 vertexa definisu trougao... broj vertexa mora biti deljiv sa 3.

GL_TRIANGLE_STRIP: trougao se iscrtava za svaki vertex posle drugog... za svako neparno n crta se trougao na koordinatama n, n + 1 i n + 2, a za svako parno n se crta na koordinatama n + 1, n, n + 2... broj vertexa mora biti veci od 2.

GL_TRIANGLE_FAN: zadaje se centralni vertex i zatim vertexi oko njega... trougao pod rednim brojem n je definisan vertexima 1, n + 1, n + 2... broj vertexa mora biti veci od 2.

GL_QUADS: svaka 4 vertexa definisu cetvorougao... broj vertexa mora biti deljiv sa 4.

GL_QUAD_STRIP: crta se cetvorougao za svaki par vertexa definisan posle prvog para... cetvorougao pod rednim brojem n je definisan vertexima 2n - 1, 2n, 2n + 2, 2n + 1... broj vertexa mora biti paran i veci od 2.

GL_POLYGON: isto kao GL_LINE_LOOP samo sto je unutrasnjost objekta popunjena.

U primeru smo izabrali GL_TRIANGLES tako i funkcijom glVertex3f smo definisali 3 vertexa. Postoje vise funkcija koje pocinju sa glVertex... razlike su u broju i tipu parametara koje uzimaju. Nastavak 3f znaci da uzima 3 float (single) parametra... 2i bi znacilo 2 parametra tipa integer. Parametri su uvek x, y, z i w. glVertex3f odgovara u skoro svim situacijama.

Pre nego sto se postavi vertex, moguce mu je postaviti boju, materijal, texturu,... U ovom primeru cemo postaviti samo boju. To se radi pozivanje funkcije glColor. Ona takodje moze primati vese ili manje paramtera koji mogu biti jednog ili drugog tipa sto zavisi od nastavka. Uglavnom cemo koristiti glColor3f (postavlja samo r,g i b) i glColor4f (postavlja r, g, b i a). Kada se boja postavi pozivom ove funkcije ona vazi za svaki naredni vertex.

Posto smo Z koordinatu vertexa postavili na 0, a minimalnu udaljenos prilikom postavljanja perspektiva na 0.1 (minimalna vrednost mora biti veca od 0) objekat se nece videti... za to nam sluze one dve funkcije koje smo preskocili u objasnjavanju. Posto smo prilikom WM_SIZE poruke na kraju izabrali GL_MODELVIEW matricu, sada mozemo raditi s njom. Prvo sto radimo je resetovanje, a zatim pozivamo funkciju glTranslate koja pravi matricu translacije. Posto smo Z parametar postavili na -5, trougao koji crtamo se nece nalaziti na udaljenosti 0, vec na udaljenosti 5 (u OpenGL smer Z ose je negativan) i time ce pasti u dozvoljeni opseg.

Na kraju pozivamo glEnd funkciju koja oznacava da smo zavrsili sa crtanjem odredjenog tipa objekata. Da smo hteli da crtamo neki kvadrat, posle ovog bi ponovo pozvali glBegin i izabrali GL_QUADS.

U sledecem primeru cemo pokazati kako se kreiraju display liste koje ubrzavaju iscrtavanje objekata.



[url=https://www.mycity.rs/must-login.png source[/url] | [url=https://www.mycity.rs/must-login.png source[/url] | [url=https://www.mycity.rs/must-login.png

Dopuna: 20 Jul 2006 0:58

Pre koji dan sam Linux i prvo sto sam pokusao da uradim je da napravim OpenGL aplikaciju... instalirao sam Lazarus i drajvere za graficku karticu i dok si rek'o keks... uradio sam prvi primer koji sam pisao u tutorialu za OpenGL pod Windowsom. Posto OpenGL nije vezan za neku odredjenu platformu kao DirectX, odlucio sam da tutorial pisem za Lazarus koristeci GLUT za rad sa prozorima tako da kod moze da se kompajlira na skoro svakoj platformi (za platforme na kojima rade Lazarus i GLUT).

Ako neko ima zelju da nastavi sa tutorijalima koji koriste WinAPI, neka slobodno krene dalje Smile



Registruj se da bi učestvovao u diskusiji. Registrovanim korisnicima se NE prikazuju reklame unutar poruka.
Ko je trenutno na forumu
 

Ukupno su 523 korisnika na forumu :: 8 registrovanih, 1 sakriven i 514 gosta   ::   [ Administrator ] [ Supermoderator ] [ Moderator ] :: Detaljnije

Najviše korisnika na forumu ikad bilo je 3466 - dana 01 Jun 2021 17:07

Korisnici koji su trenutno na forumu:
Korisnici trenutno na forumu: DM1994, DonRumataEstorski, kybonacci, Litostroton, Miskohd, Shilok, Tas011, wizzardone