OpenGL

rk7771

screen_kostka_opengl.jpg

1 Opis podstawowy.
2 Implementacja OpenGL w środowisku Delphi.
     2.1 Krok pierwszy - dołączenie bibliotek do środowiska Delphi.
     2.2 Krok drugi - budowa struktury programu w Delphi.
     2.3 Krok trzeci - uzupełnienie struktury programu funkcjami.
     2.4 Ustawienia startowe wywoływane podczas tworzenia formy (FormCreate)
     2.5 Aktywacja formy (FormActivate).
     2.6 Obsługa malowania na formularzu (FormPaint).
     2.7 Obsługa zdarzenia OnTimer komponentu Timer (Timer1Timer).
     2.8 Ustawienie formatu pikseli (setupPixelFormat).
     2.9 Inicjalizacja maszyny stanu Open GL (GLInit).
          2.9.1 Macierz modelowania
          2.9.2 Rzutowanie perspektywistyczne
          2.9.3 Rzutowanie ortograficzne
          2.9.4 Usuwanie ukrytych powierzchni
          2.9.5 Kierunek tworzenia wierzchołków
          2.9.6 Ukrywanie ścian
     2.10 Procedura rysująca - tworzącą grafikę trójwymiarową (Draw).
          2.10.7 Funkcja SwapBuffers
          2.10.8 Funkcja glFlush
          2.10.9 Funkcja glClear
          2.10.10 Funkcja glLoadIdentify
          2.10.11 Funkcja glTranslatef
          2.10.12 Funkcja glRotatef
          2.10.13 Funkcja glEnable
          2.10.14 Funkcja glBegin
          2.10.15 Funkcja glEnd
          2.10.16 Funkcja glVertex
          2.10.17 Funkcja glTexCoord2f
          2.10.18 Funkcja glTexParamateri
          2.10.19 Funkcja glBindTexture
     2.11 Procedura wywoływana podczas zmiany wielkości okna (FormResize).
     2.12 Informacje końcowe.
3 Gotowy przykład implementacji maszyny stanu OpenGL w Delphi.
     3.13 Kostka 3D
     3.14 Zegar GL

Opis podstawowy.

OpenGL zaprojektowany został z myślą tworzenia różnego rodzaju aplikacji. Dostępny jest dla wszystkich najważniejszych platform w różnych konfiguracjach sprzętowych. Biblioteka niskiego poziomu OpenGL umożliwia generowanie zaawansowanej grafiki i jest wykorzystywana w komputerowym wspomaganiu projektowania czy grach. W szczególności można powiedzieć, iż OpenGL określa interfejs programowy udostępniający programiście możliwości graficzne platformy na której tworzona będzie aplikacja.

W celu umożliwienia pełnej kontroli nad tworzoną grafiką i zwiększeniu uniwersalności OpenGL zawiera jedynie operacje niskiego poziomu. Operacje niskiego poziomu pozwalają stworzyć własną bibliotekę graficzną wysokiego poziomu. Przykładem biblioteki wysokiego poziomu może być GLU (OpenGL Utility Library), która jest dostarczana wraz z większością dystrybucji OpenGL. Biblioteka OpenGL związana jest z tworzeniem wyłącznie grafiki
i w przeciwieństwie do DirectX nie umożliwia ona tworzenia operacji związanych
z tworzeniem dźwięku, interakcją z użytkownikiem czy tworzenia interfejsu sieciowego.

Od roku 1992 rozwój specyfikacji OpenGL prowadzony jest przez OpenGL Architecture Review Board (ARB), w skład której wchodzą firmy ATI, Compaq (obecnie HP Compaq), Evans & Sutherland, Hewlett-Packard, IBM, Intel, Intergraph, nVidia, Microsoft oraz Silicon Graphics. Sama specyfikacja OpenGL opracowana została natomiast przez firmę Silicon Graphics, Inc. (SGI) jako interfejs służący do tworzenia grafiki, który jest niezależny od platformy. Zadaniem ARB jest przygotowywanie specyfikacji OpenGL i tym samym dyktowanie funkcjonalności kolejnych dystrybucji interfejsu OpenGL tworzonych przez producentów.

OpenGL wykorzystuje maszynę stanów, stany opisują natomiast sposób wykonywania operacji graficznych. Sama specyfikacja OpenGL zawiera zaś kilkaset funkcji udostępniających możliwości sprzętu graficznego. Korzystając z interfejsu programowego OpenGL można określać wiele aspektów maszyny stanów, takich jak na przykład:

  • bieżący kolor,
  • oświetlenie,
  • sposób łączenia kolorów

i tak dalej. Sposób tworzenia grafiki jest więc określony przez bieżącą konfigurację maszyny stanów. Zrozumienie znaczenia poszczególnych stanów maszyny umożliwia poprawne tworzenie aplikacji. Niewłaściwy wybór stanu skutkuje natomiast nieprzewidzianymi efektami operacji graficznych.

Jądrem biblioteki OpenGL jest potok tworzonej grafiki, a uświadomienie sobie, iż wszystko, co jest widoczne na ekranie, stanowi rezultat wykonania szeregu operacji w potoku jest najważniejszą rzeczą. Większość operacji w potoku wykonywana jest automatycznie przez bibliotekę OpenGL.

Bibliotekami uzupełniającymi OpenGL jest pakiet GLUT {OpenGL Utility Toolkit) zawierający zestaw pomocniczych bibliotek. Jest on dostępny dla najważniejszych platform. Biblioteki uzupełniające dają możliwość tworzenia menu, okien czy interfejsu interakcji z użytkownikiem. Są one niezależne od platformy dając możliwość łatwego przenoszenia między różnymi systemami operacyjnymi (np.: z Windows do Unix). Biblioteki GLUT są zupełnie wystarczające w przypadku tworzenia prostych aplikacji czy przykładów demonstracyjnych. Niedostarczają one jednak możliwości jakie oferuje system operacyjny.

Implementacja OpenGL w środowisku Delphi.

Krok pierwszy - dołączenie bibliotek do środowiska Delphi.

Do prawidłowego działania OpenGL w Delphi niezbędne jest dołączenie plików pas, tj.:

  • GL.pas,
  • GLext.pas,
  • GLU.pas,
  • GLUT.pas.

Są to biblioteki podstawowe wraz z bibliotekami rozszerzonymi. W celu dołączenia tekstur w formacie plików jpg do tworzonych brył można wykorzystać plik:

  • textures.pas,

natomiast wykorzystanie plików bmp wymaga dołączenia pliku:

  • bmp.pas.

Krok drugi - budowa struktury programu w Delphi.

W nowym projekcie tworzonej aplikacji w delphi utworzyć należy procedury:

  • tworzenia formy: FormCreate,
  • aktywacji formy: FormActivate,
  • obsługi zmiany wielkości formy: FormResize,
  • rysowania formy: FormPaint,
  • obsługi zegara: Timer1Timer,
  • inicjalizacji maszyny stanu Open GL: GLInit,
  • ustawienie formatu pikseli: setupPixelFormat,
  • tworzenia grafiki: Draw.

Procedury: FormCreate, FormActivate, FormResize, FormPaint oraz Timer1Timer tworzymy w object inspektorze. Procedury: GLInit, setupPixelFormat oraz Draw dodajemy ręcznie w kodzie źrodłowym. W sekcji uses dodajemy obsługę bibliotek OpenGl tj.: Gl, Glu, OpenGL oraz dodajemy pliki ładowania tekstur. Stosownie od preferencji: textures lub bmp. Do programu dodajemy również Timer w celu obsługi animacji.

Wygląd schematu programu:

unit nazwa_unit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Gl, Glu, OpenGL, Textures, ExtCtrls;

type
  Tprez_form = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure GLInit();
    procedure setupPixelFormat(DC:HDC);
  private
    procedure Draw();
  end;

var
  prez_form: Tprez_form;

implementation

{$R *.dfm}

procedure Tprez_form.FormCreate(Sender: TObject);
begin
  //ustawienia startowe wywoływane podczas tworzenia formy
end;

procedure Tprez_form.FormActivate(Sender: TObject);
begin
  // aktywacja formy
  SetupPixelFormat(); //wywołanie procedury ustawiającej format pikseli
  GLInit;             //wywołanie procedury inicjalizującej Open GL
end;

procedure Tprez_form.FormPaint(Sender: TObject);
begin
  draw();  // wywołanie procedury rysującej
end;

procedure Tprez_form.GLInit();
begin
  //inicjalizacja maszyny stanu Open GL
  //a w szczególności określenie macierzy 
  //rzutowania ? glMatrixModel, itp.
end;

procedure Tprez_form.setupPixelFormat(DC:HDC);
const
  //określenie stałych
var
  //określenie zmiennych
begin
  //ustawienie formatu pikseli
end;

procedure Tprez_form.Draw();
begin
  //procedura rysująca
end;

procedure Tprez_form.Timer1Timer(Sender: TObject);
begin
  //obsługa zegara
  Draw();  //wywołanie procedury rysującej
end;

Krok trzeci - uzupełnienie struktury programu funkcjami.

Przed uzupełnieniem struktury programu funkcjami, nadmienić trzeba, iż w sekcji
uses dodane zostały odwołania do bibliotek OpenGL, tj.: Gl, Glu, OpenGL. Dodano również
plik textures.pas w celu ładowania tekstur z plików jpg. Zamiast pliku textures.pas
dołączyć można bmp.pas co umożliwi ładowanie tekstur z plików bmp. Nie jest to jednak
zasadne ze względu na wielkości bitmap.

Ustawienia startowe wywoływane podczas tworzenia formy (FormCreate)

W momencie gdy formularz jest tworzony (czy to w sposób automatyczny, czy też ręczny) uaktywnianych zostaje kilka zdarzeń w skład których wchodzą:

  • zdarzenie OnCreate - informujące o tworzeniu formy,
  • zdarzenie OnShow - informujący o wyświetleniu formy na ekranie,
  • zdarzenie OnActivate - które sygnalizuje, że dany formularz został właśnie wybrany spośród pozostałych formularzy ujętych w programie,
  • zdarzenia OnResize oraz OnPaint - które aktywowane są przy zmianie, jak i na początku tworzenia formularza.

W procedurze FormCreate ująć należy funkcje uruchamiane podczas tworzenia formy. W skład podstawowych funkcji zaliczyć można:

  • podstawienie wartości do zmiennych statycznych (np.: ścieżka programu/),
  • ustawienie wyglądu formy (wielkość, pozycja, ikony systemowe w pasku, nazwa formy),
  • ustawienia dla timera ? interwał,

Przykładowa postać procedury FormCreate obsługująca zdarzenie OnCreate:

procedure Tprez_form.FormCreate(Sender: TObject);
begin
  sc_programu:=ExtractFilePath(ParamStr(0));

  //obsługa ikon systemowych w pasku tytułowym
  prez_form.BorderIcons := [biSystemMenu];     
  //pozostawienie ikony[x] zamykającej program
  //pusta wartość [] powoduje usunięcie wszystkich
  //ikon z paska tytułowego formy

  //ustawienie na false wartości prez_form.Scaled umożliwia
  //zmianę jej wielkości w kodzie źródłowym - funkcja którą 
  //trzeba zmienić od wersji Delphi wyższej niż 7
  prez_form.Scaled := false;

  //ustawienia wielkości formy
  prez_form.Width := Screen.Width-100;
  prez_form.Height := Screen.Height-100;

  //ustawienia pozycji formy na ekranie  
  prez_form.Top := 0;
  prez_form.Left := 0;

  //zmiana interwału dla komponentu Timer
  Timer1.Interval := 100;
end;

W celu ujednolicenia i braku migania formy podczas jej ładowania wskazane jest powtórzyć
ustawienia dla formy obejmujące jej pozycję na ekranie w procedurze FormActivate, tj.:

prez_form.Scaled := false;
prez_form.Width := Screen.Width-100;
prez_form.Height := Screen.Height-100;
prez_form.Top := 0;
prez_form.Left := 0;

Ustawienie prez_form.Scaled := false dotyczy Delphi wyższych niż wersja 7. Od wersji 7 w dół ustawienie to nie jest wymagane.

Aktywacja formy (FormActivate).

Procedura aktywacji formy FormActivate (obsługi zdarzenia OnActivate) oprócz wspomnianych powyżej ustawień pozycji formy na ekranie musi zawierać odwołania do procedur SetupPixelFormat oraz GLInit. Odpowiedzialne są one w szczególności za:

  • procedura SetupPixelFormat - ustawienie formatu pikseli,
  • GLInit - inicjalizację maszyny stanów OpenGL.

Dodatkowo procedura aktywacji formy zawierać musi kontekst tworzenia grafiki oraz kontekst urządzenia, które ujęte są:

var
  DC:HDC;
  RC:HGLRC;
begin
  DC:=GetDC(Handle);
  RC:=wglCreateContext(DC);
  wglMakeCurrent(DC, RC);
end;

Cała, minimalna, postać procedury aktywacji formy (FormActivate) wyglądać powinna jak na poniższym przykładzie:

procedure Tprez_form.FormActivate(Sender: TObject);
var
  DC:HDC;
  RC:HGLRC;
begin

  prez_form.Scaled := false;

  //ustawienia wielkości formy
  prez_form.Width := Screen.Width-100;
  prez_form.Height := Screen.Height-100;

  //ustawienia pozycji formy na ekranie  
  prez_form.Top := 0;
  prez_form.Left := 0;

  DC:=GetDC(Handle);
  SetupPixelFormat(DC);     //wywołanie procedury odpowiedzialnej
                            //ustawienie formatu pikseli
  RC:=wglCreateContext(DC);
  wglMakeCurrent(DC, RC);
  GLInit;                   //wywołanie procedury inicjalizującej
                            //maszynę stanów OpenGL

end;

Obsługa malowania na formularzu (FormPaint).

Po pierwszym uruchomieniu programu niezbędne jest obsłużenie procedury malowanie na formularzu: FormPaint (obsługującej zdarzenie OnPaint). Związane jest to z przyczyną zastosowanej przez Windows domyślnej procedury odświeżania okien. Po uruchomieniu programu należy wymusić na systemie Windows operację malowania utrwalając efekt rysowania grafiki trójwymiarowej na formie.

Procedura malowania po uruchomieniu programu zawierać powinna tylko wywołanie głównej procedury rysowania grafiki trójwymiarowej, tj.: w naszym przypadku procedury Draw. Przedstawia to poniższy przykład:

procedure Tprez_form.FormPaint(); 
begin
  Draw();
end;

Obsługa zdarzenia OnTimer komponentu Timer (Timer1Timer).

W procedurze obsługi zdarzenia OnTimer komponentu Timer należy zastosować funkcję związane z ruchem, np.: obracającego się sześcianu który mógłby być uzupełniony wypełnionymi teksturami. Należy również umieścić w niej wywołanie głównej procedury
tworzącej grafikę trójwymiarową, tj.: w naszym przypadku Draw(). Konstrukcja procedury wyglądać może:

procedure Tprez_form.Timer1Timer(Sender: TObject);
begin
  { 
   funkcje obliczające nowe wartości zmiennych
   związane z ruchem czyli funkcjami wykorzystywanymi
   w procedurze Draw do przeprowadzenia obrotów, przesunięć
    jak np.: 
   - glTranslatef(0.0,0.0, przesunięcie) - funkcja związana z przesunięciem
     dla której można obliczać w procedurze Timer1Timer wartość zmiennej 
      przesuniecie
   - glRotatef(obr_poziom, 1, 0, 0) - funkcja związana z obrotem dla której można
      obliczać w procedurze Timer1Timer wartości zmiennej obr_poziom

   oraz innymi, np.: zmianą ustawienia oświetlenia, czasową zmianą intensywności mgły, 
   czasem życia cząstek wykorzystywanych podczas opadu deszczu, śniegu, wybuchem itp. 
   }

  //ponowne wywołanie głównej procedury tworzącej grafikę trójwymiarową
  //częstotliwość wywołań uzależniona jest od ustawionego interwału komponentu Timer1,
  //którego wartość została statycznie przypisana w procedurze FormCreate

  Draw();
end;

Ustawienie formatu pikseli (setupPixelFormat).

Format pikseli trzeba zawsze określić przed stworzeniem kontekstu tworzenia grafiki. Określić można na przykład tryb koloru, bufor głębi, liczbę bitów opisujących piksel czy sposób buforowania zawartości okna. Ustawienie formatu pikseli stanowi kolejne rozszerzenie interfejsu programowego Windows.
W stałych należy określić strukturę TPIXELFORMATDESCRIPTOR:

const
   pfd:TPIXELFORMATDESCRIPTOR = (
       //rozmiar struktury
        nSize:sizeof(TPIXELFORMATDESCRIPTOR);	        
       //zawsze wartość 1
        nVersion:1;					
        //znaczniki właściwości bufora pikseli
        dwFlags:PFD_SUPPORT_OPENGL 
                or PFD_DRAW_TO_WINDOW 
                or PFD_DOUBLEBUFFER;		        
        //typ danych piksela
        iPixelType:PFD_TYPE_RGBA;		
        //liczba bitów piksela
        cColorBits:24;					
        //liczba bitów koloru czerwonego oraz 
        //przesunięcie bitów koloru czerwonego
        cRedBits:0; cRedShift:0;			                                                        
        //liczba bitów koloru zielonego oraz
        //przesunięcie bitów koloru zielonego
        cGreenBits:0;  cGreenShift:0;			 
        //liczba bitów koloru niebieskiego oraz
        //przesunięcie bitów koloru niebieskiego
        cBlueBits:0; cBlueShift:0;			 
        //liczba bitów alfa oraz
        //przesunięcie bitów alfa
        cAlphaBits:0;  cAlphaShift:0;   		
        //liczba bitów bufora akumulacji							
        cAccumBits: 0;					
        //liczba bitów akumulacji czerni
        cAccumRedBits: 0;  				
        //liczba bitów akumulacji zieleni
        cAccumGreenBits: 0;     			
        //liczba bitów akumulacji błękitu
        cAccumBlueBits: 0;				
       //liczba bitów akumulacji alfa
        cAccumAlphaBits: 0;				
        //liczba bitów bufora głębi
        cDepthBits:16;					
        //liczba bitów bufora powielania
        cStencilBits:0;					
        //liczba buforów pomocniczych
        cAuxBuffers:0;					
        //nie jest już wykorzystywany
        iLayerType:PFD_MAIN_PLANE;  		
       //liczba podkładanych i nakładanych jednostek
        bReserved: 0;					
        //nie jest już wykorzystywany
        dwLayerMask: 0;				
        //indeks podłożonej płaszczyzny
        dwVisibleMask: 0;				
        //nie jest już wykorzystywany
        dwDamageMask: 0;                    		
   );

gdzie:

  • nsize: opisuje rozmiar struktury przekazywanych za pomocą wskaźnika, służy do określenia wielkości pamięci zajmowanej przez strukturę. Umieszczenie w pierwszym polu umożliwia szybkie pobranie przez deferencję wskaźnika.
  • Dwflags: określa właściwości bufora pikseli (PFD_DRAW_TO_WINDOW ? umożliwia tworzenie grafiki w oknie, PFD_SUPPORT_OPENGL ? umożliwia tworzenie grafiki opengl, PFD_DOUBLEBUFFER ? podwójne buforowanie /wyklucza się ze znacznikiem PFD_SUPPORT_GDI/, PFD_SWAP_LAYER_BUFFERS ? urządzenie może przełączać pojedyncze plany warstw z formatami pikseli o podwójnym buforowaniu /w przeciwnym razie wszystkie plany warstw przełączane są grupowo/; jeśli znacznik jest ustawiony, to dostępna jest funkcja wglSwapLayerBuffers, PFD_DEPTH_DONTCARE ? dla danego formatu pikseli może być wykorzystywany bufor głębi, PFD_DOUBLEBUFFER_DONTCARE ? piksele mogą być buforowane pojedynczo lub podwójnie),
  • IPixelType: określa typ danych piksela (PFD_TYPE_RGBA ? piksele opisane w standardzie RGBA czyli dla każdego piksela określony jest kolor czerwony, zielony, niebieski oraz współczynnik alfa, PFD_TYPE_COLORINDEX ? piksele opisane są za pomocą wartości indeksu koloru),
  • CcolorBits: opisuje liczbę bitów określających kolor piksela i może przyjmować wartości 8, 16, 24 i 32. W przypadku gdy karta grafiki nie umożliwia reprezantacji koloru pikseli za pomocą danej liczby bitów to przyjmowana jest jej największa możliwa wartość.

W zmiennych należy określić:

pixelFormat : integer

a w samej procedurze przyporządkować wartość całkowitą indeksu dostępnego formatu pikseli:

pixelFormat := ChoosePixelFormat(DC, @pfd)

oraz sprawdzić wartość przypisania:

if (pixelFormat = 0) then
   exit;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then
   exit;

Cała procedura setupPixelFormat(DC:HDC) wygląda następująco:

procedure Tprez_form.setupPixelFormat(DC:HDC);
const
   pfd:TPIXELFORMATDESCRIPTOR = (
        nSize:sizeof(TPIXELFORMATDESCRIPTOR);	
        nVersion:1;			
        dwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or
                PFD_DOUBLEBUFFER;	
        iPixelType:PFD_TYPE_RGBA;	
        cColorBits:24;			
        cRedBits:0; cRedShift:0;	
        cGreenBits:0;  cGreenShift:0;
        cBlueBits:0; cBlueShift:0;
        cAlphaBits:0;  cAlphaShift:0;  
        cAccumBits: 0;
        cAccumRedBits: 0;  		
        cAccumGreenBits: 0;     	
        cAccumBlueBits: 0;
        cAccumAlphaBits: 0;
        cDepthBits:16;			
        cStencilBits:0;			
        cAuxBuffers:0;			
        iLayerType:PFD_MAIN_PLANE;  	
   bReserved: 0;
   dwLayerMask: 0;
   dwVisibleMask: 0;
   dwDamageMask: 0;                    
   );
var 
  pixelFormat:integer;
begin
   pixelFormat := ChoosePixelFormat(DC, @pfd);
   if (pixelFormat = 0) then
        exit;
   if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then
        exit;
end;

Inicjalizacja maszyny stanu Open GL (GLInit).

Procedura inicjalizacyjna maszyny stanu Open GL odpowiada za ustawienie sposobu wykonywania przekształceń, parametry rzutowania. Mogą być w niej zainicjowane również funkcje ukrywania powierzchni, ukrywania ścian niewidocznych (co w przypadku dużej ilości tworzonych elementów ma za zadanie przyśpieszenie działania skompilowanego programu). Przykładowy schemat procedury inicjalizującej maszynę stanu Open GL przedstawia poniższy przykład:

procedure Tprez_form.GLInit();
begin
  //ustawienie sposobu wykonywania przekształceń w Open GL
  glMatrixMode(GL_PROJECTION); //macierz rzutowania

  //parametry rzutowania perspektywicznego
  glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);

  //ponowne ustawienie sposobu wykonywania przekształceń w Open GL
  glMatrixMode(GL_MODELVIEW); //macierz modelowania

  //usunięcie ukrytych powierzchni
  glEnable(GL_DEPTH_TEST);

  //ustawienie kierunku tworzenia wierzchołków wielokątów
  glFrontFace(GL_CCW);

  //ukrywanie tylnych ścian
  glCullFace(GL_BACK);
end;

GDZIE:

Macierz modelowania

Macierz modelowania glMatrixMode definiuje układ współrzędnych wykorzystywanych podczas tworzenia obiektów i związana jest z otrzymywanym rezultatem przekształceń wierzchołków. Informuje ona maszynę OpenGL, że będzie modelowana macierz modelowania (GL_MODELVIEW), macierz rzutowania (GL_PROJECTION) lub macierz tekstury (GL_TEXTURE).

Rzutowanie perspektywistyczne

Rzutowanie perspektywiczne glFrustrum pozwala uzyskać bardziej realistyczny obraz trójwymiarowego świata. W efekcie rysowania perspektywicznego obiekty znajdujące się dalej od obserwatora są rysowane na ekranie jako mniejsze. Rzutowanie perspektywistyczne polega na zastosowaniu bryły w kształcie ściętego ostrosłupa skierowanego mniejszą podstawą w stronę obserwatora. Open Gl, podczas rzutowania, przekształca ostrosłup w prostopadłościan i w efekcie obiekty znajdujące się dalej są bardziej pomniejszane od tych znajdujących się bliżej. Wielkość pomniejszenia realizuje się poprzez zmianę rozmiarów obu podstaw ostrosłupa. W przypadku gdy obie podstawy ostrosłupa są tych samych wymiarów uzyskane zostanie rzutowanie ortograficzne /zwane też równoległym/.

GlFrustrum(Gldouble left, Gldouble right, Gldouble bottom, Gldouble top, Gldouble near, Gldouble far)
gdzie:

  • left, right, bottom, top ? wyznaczają płaszczyznę obcięcia zawierającą mniejszą podstawę ostrosłupa,
  • near, far ? określają odległość do mniejszej i większej podstawy ostrosłupa,
  • wierzchołki większej podstawy wyznacza się jako punkty przecięcia linii przechodzących przez punkt obserwatora i wierzchołki mniejszej podstawy z dalszą płaszczyzną obcięcia.

Im bliżej więc znajdować się będzie obserwator względem mniejszej z podstaw ostrosłupa, tym większa będzie druga podstawa oraz większe wrażenie perspektywy.

Rzutowanie ortograficzne

Rzutowanie ortograficzne wykorzystywane jest w grach opartych na grafice dwuwymiarowej, w komputerowym wspomaganiu projektowania. Rzutowanie ortograficzne nie tworzy tak realistycznego obrazu świata jak rzutowanie perspektywistyczne.

Usuwanie ukrytych powierzchni

Usunięcie ukrytych powierzchni GL_DEPTH_TEST - polecenie glEnable(GL_DEPTH_TEST) aktywuje usuwanie ukrytych powierzchni.

Kierunek tworzenia wierzchołków

Ustawienie kierunku tworzenia wierzchołków wielokątów glFrontFace ? kierunek tworzenia wierzchołków wielokątów może być określony w kierunku przeciwnym do obrotu wskazówek zegara (GL_CCW) lub zgodnym (GL_CW).

Ukrywanie ścian

Ukrywanie ścian glCullFace ? określa która ze stron ścian ma być rysowana. Może przyjmować wartości:

  • GL_FRONT (przednie ściany),
  • GL_BACK (tylne ściany),
  • GL_FRONT_AND_BACK (przednie i tylne ściany ? nie ma zwykle sensu).

Procedura rysująca - tworzącą grafikę trójwymiarową (Draw).

Wywołanie procedury Draw należy umieszczać zawsze w procedurach obsługujących ruch myszą oraz w procedurach obsługujących ruch do przodu, do tyłu, w bok (klawisze kursorów). Można umieścić również jej wywołanie w procedurze Timer1Timer(Sender: TObject) dla elementów ruchomych np.: obracający się krąg na danej powierzchni
czy poruszający się inny przedmiot w przestrzeni X,Y,Z.

W procedurze Draw() umieszczane są najczęściej poniższe funkcje:

procedure Tprez_form.Draw();
begin
  //zeruje bufor ekranu I bufor głębi
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  //resetuje macierz widoku modelu
  glLoadIdentity();                           

  //przesunięcie o wartość jednostek zmiennej ?przesunięcie? wzdłuż osi Z
  //(znak ?-? powoduje przesunięcie wstecz), dla osi X i Y wartości są 0.0 
  //czyli brak przesunięcia
  //dla obcnej sceny przesunięcie wynosi ?10.0 czyli oddalenie od miejsca obserwatora
  glTranslatef(0.0,0.0, przesunięcie);

  //obrót dookoła osi X o wartość obr_poziom, dla osi Y i Z 
  //wprowadzona jest wartość 0 czyli brak obrotu
  glRotatef(obr_poziom, 1, 0, 0);

  //obrót dookoła osi Y o wartość obr_pion, dla osi X i Z 
  //wprowadzona jest wartość 0 czyli brak obrotu
  //obroty należy wykonywać dla każdej płaszczyzny osobno
  glRotatef(obr_pion, 0, 1, 0);

  //włączenie, poinformowanie OpenGL o braku rysowania płaszczyzn 
  //zainicjowanych funkcją glCullFace() ? brak obliczeń dla niewidocznych stron
  glEnable(GL_CULL_FACE);

  //włączenie tekstur dwuwymiarowych
  glEnable(GL_TEXTURE_2D);

  //włączenie tworzenia tekstury
  glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
  glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
  glBindTexture(GL_TEXTURE_2D, jakaś_tekstura);
  //koniec tworzenia tekstury

  //narysowanie kwadratu wypełnionego teksturą ?jakaś_tekstura?
  glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(Vertex[i].X, Vertex[i].Y,  Vertex[i].Z);
    glTexCoord2f(1.0, 0.0); glVertex3f(Vertex[i].X, Vertex[i].Y,  Vertex[i].Z);
    glTexCoord2f(1.0, 1.0); glVertex3f(Vertex[i].X, Vertex[i].Y,  Vertex[i].Z);
    glTexCoord2f(0.0, 1.0); glVertex3f(Vertex[i].X, Vertex[i].Y,  Vertex[i].Z);
  glEnd();

  //przełączenie buforów
  SwapBuffers(wglGetCurrentDC);
end;

Gdzie:

Funkcja SwapBuffers

Funkcja SwapBuffers ze zmienną jako parametr przechowującą kontekst urządzenia powoduje przełączenie buforów, zapewniając płynność animacji. Rysowana animacja w procedurze Draw() umieszczana jest w niewidocznym dla obserwatora buforze i następnie po jej narysowaniu za pomocą funkcji SwapBuffers jest przedstawiana obserwatorowi.

Funkcja glFlush

Funkcja glFlush() opróżnia natomiast bufor.

Funkcja glClear

Funkcja glClear zeruje bufory kolorów i głębi i w efekcie wypełnia okno kolorem czarnym.

Funkcja glLoadIdentify

Funkcja glLoadIdentity powoduje wyczyszczenie bieżącej macierzy przekształceń w celu umożliwienia wykonania kolejnych przekształceń widoku.

Funkcja glTranslatef

Funkcja glTranslatef reprezentuje przekształcenia modelowania, podobnie jak funkcja glRotatef. Należy ją stosować w przypadku braku wykorzystania /jak w naszym przykładzie/ funkcji gluLookAt. Funkcja glTranslate wykonuje przesunięcie o daną wartość X,Y,Z ? w naszym przykładzie przesunięcie następuje wzdłuż osi Z.

Funkcja glRotatef

Funkcja glRotatef podobnie jak funkcja glTranslatef reprezentuje przekształcenia modelowania o daną wartość. Jak sama nazwa wskazuje powoduje ona obrót wokół danej osi współrzędnych. Wybranie obrotu dla osi reprezentuje wartość 1 wprowadzona w odpowiednim miejscu, na przykład:

  • glRotatef(obr, 1, 0, 0) ? obrót o wartość obr wokół osi X,
  • glRotatef(obr, 0, 1, 0) ? obrót o wartość obr wokół osi Y,
  • glRotatef(obr, 0, 0, 1) ? obrót o wartość obr wokół osi Z.

Obroty dla każdej osi należy wykonywać osobno. Nie wolno łączyć obrotu wokół osi X i Y w jednej funkcji.

Funkcja glEnable

Funkcja glEnable włącza różne stany OpenGl, natomiast funkcja glDisable powoduje wyłączenie stanu maszyny OpenGl. Na przykład: glEnable(GL_FOG) powoduje włączenie mgły, natomiast glDisable(GL_FOG) jej wyłączenie.

Funkcja glBegin

Funkcja tworząca grafikę glBegin() ? funkcja ta przekazuje maszynie OpenGL informację o rozpoczęciu tworzenia grafiki oraz o typie wykorzystywanych elementów. Posiada ona postać: GlBegin(Glenum mode)
Typ wykorzystywanych elementów określony jest za pomocą parametru mode i przyjmować on może wartości:

  • GL_POINTS - pojedyncze punkty,
  • GL_LINES - odcinki,
  • GL_LINE_STRIP - sekwencja połączonych odcinków,
  • GL_LINE_LOOP - zamknięta sekwencja połączonych odcinków,
  • GL_TRIANGLES - pojedyncze trójkąty,
  • GL_TRIANGLE_STRIP - sekwencja połączonych trójkątów,
  • GL_TRIANGLE_FAN - sekwencja trójkątów posiadających jeden wspólny wierzchołek,
  • GL_QUADS - czworokąty,
  • GL_QUAD_STRIP - sekwencja połączonych trójkątów,
  • GL_POLYGON - wielokąty o dowolnej liczbie wierzchołków.

Każde wywołanie funkcji glBegin(musi być zakończone wywołaniem funkcji glEnd().

Funkcja glEnd

Funkcja glEnd() powiadamia maszynę OpenGl o zakończeniu tworzenia grafiki z wykorzystaniem elementów określonych jako parametr funkcji glBegin. Funkcja glEnd nie posiada parametrów. Należy ponadto pamiętać, iż nie wolno zagnieżdżać funkcji glBegin oraz glEnd, czyli nie wolno wywoływać ich po raz kolejny w bloku funkcji glBegin i glEnd.

Wewnątrz funkcji glBegin oraz glEnd można tylko wywoływać funkcje:

  • glVertex,
  • glColor,
  • glIndex,
  • glNormal,
  • glTexCoord,
  • glEvalCoord,
  • glEvalPoint,
  • glMaterial,
  • glEdgeFlag,
  • glCallList,
  • glCallLists.

Funkcja glVertex

Funkcja glVertex() wywoływana jest w celu określenia punktu w przestrzeni. Poniżej przedstawiam schemat wywołania funkcji: GlVertex[2,3,4][d,f,i,s][wektor]
gdzie:

  • cyfra to liczba wymiarów,
  • litera określa typ danych: double, float, int lub short,
  • wektor oznacza tablicę za pomocą której zostaną przekazane parametry funkcji.

W kodzie można użyć: glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z). Tablica Vertex zaimplementowana musi zostać w sekcji var formy jako TCoord. Typ TCoord jest natomiast rekordem zawierającym zmienne X,Y,Z typu glFloat. Całość implementacji zmiennych tablicy i rekordu wyglądać może następująco:

type
  Tprez_form = class(TForm)
  ? procedury ?
  private
    { Private declarations }
     ? zmienne ? procedury
  public
    { Public declarations }
   end;

type
  //rekord
  TCoord = Record
     X, Y, Z : glFLoat;
  end;

var
  prez_form: Tprez_form;
  //tablica
  Vertex : Array of TCoord;

W kodzie źródłowym w procedurze Draw można narysować GL_QUADS np.: w sposób jak poniżej stosując pętlę while. Parametr ?i? jest zmienną przetrzymującą liczby typu integer.

while i <  16 do
begin
  glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0); glVertex3f(Vertex[i].X, Vertex[i].Y,  Vertex[i].Z);
    i := i + 1;
    glTexCoord2f(1.0, 0.0); glVertex3f(Vertex[i].X, Vertex[i].Y,  Vertex[i].Z);
    i := i + 1;
    glTexCoord2f(1.0, 1.0); glVertex3f(Vertex[i].X, Vertex[i].Y,  Vertex[i].Z);
    i := i + 1;
    glTexCoord2f(0.0, 1.0); glVertex3f(Vertex[i].X, Vertex[i].Y,  Vertex[i].Z);
    i := i + 1;
  glEnd();
end;

Powyższy przykład narysuje cztery ?kwadraty? w zależności od danych przetrzymywanych
w tablicy Vertex.

Inny przykład użycia funkcji glVertex:

glBegin(GL_POINTS)
  glVertex(0.0, 0.0, 0.0);
glEnd();

Pierwszy wiersz powyższego przykładu informuje maszynę OpenGL, iż będą rysowane punkty. Kolejny wiersz rysuje punkty a ostatni informuje maszynę OpenGL o zakończeniu rysowania.

Można również wewnątrz bloku wywołań funkcji glBegin() oraz glEnd wywołać funkcję glVertex() dowolną liczbę razy co przyśpiesza działanie programu, np.:

glBegin(GL_POINTS)
  glVertex(0.0, 0.0, 0.0);
  glVertex(0.0, 1.0, 0.0);
glEnd(;)

Poinformowanie maszyny OpenGL o rysowaniu odcinków oznacza natomiast, iż punkty mają być interpretowane jako końce odcinków. Pokazuje to poniższy przykład:

glBegin(GL_LINES)
  glVertex(-1.0, -1.0, 0.0);
  glVertex(2.0, 1.0, 0.0);
glEnd();

Wewnątrz bloku wywołań funkcji glBegin oraz glEnd można rysować wiele odcinków. Każde kolejne dwa punkty traktowane będą jako końce nowego odcinka.

Funkcja glTexCoord2f

Funkcja glTexCoord2f() określa współrzędne tekstury. Tworzenie sceny wymaga określenia współrzędnych tekstury dla wszystkich wierzchołków. Współrzędne tekstury pozwalają ustalić położenie tekseli na rysowanym objekcie. Współrzędne (0, 0) oznaczają lewy dolny narożnik tekstury. Współrzędne (1, 1) prawy górny narożnik. Podczas rysowania wielokąta trzeba określić współrzędne tekstury dla każdego z jego wierzchołków. W przypadku tekstur dwuwymiarowych mają one postać (s, t), gdzie s i t przyjmują wartości z przedziału od 0 do 1.

(0.0, 1.0)                           (1.0, 1.0)
    |------------------------------------|
    |                                    |
    |                                    |
    |                                    |
    |                                    |
    |                                    |
    |                                    |
    |                                    |
    |------------------------------------|
(0.0, 0.0)                           (1.0, 0.0)

Włączenie tworzenia tekstur:

Funkcja glTesGenf stosowana jest przy odwzorowaniu otoczenia /np.: w zastosowaniu czcionki konturowej pokrytej teksturą/. Pierwszy parametr informuje maszynę OpenGl o tym, która ze współrzędnych tekstur będzie generowana automatycznie /może on przyjmować wartość GL_S lub GL_T w odniesieniu do tekstur dwuwymiarowych/.Drugi parametr określa tryb odwzorowania tekstury używany podczas automatycznej generacji współrzędnej i może przyjmować wartości: GL_EYE_LINEAR ? tekstura umieszczana jest w stałym miejscu ekranu, obiekty pokryte są teksturą, GL_OBJECT_LINEAR ? tekstura umieszczana jest na obiekcie, GL_SPHERE_MAP ? tekstura reprezentuje odwzorowanie otoczenia (mapę sferyczną).

Funkcja glTexParamateri

Funkcja glTexParameteri ? okresla sposób filtrowania tekstury. A oto jej struktura:
GlTexParameteri(Glenum target, Glenum pname, Glint param)
gdzie:

  • target reprezentuje rodzaj używanych tekstur, może przyjmować wartości: GL_TEXTURE_1D, GL_TEXTURE_2D lub GL_TEXTURE3D,
  • pname pozwala określić, czy definiuje się obsługę powiększania czy pomniejszania
    i może przyjmować odpowiednio GL_TEXTURE_MAG_FILTER lub GL_TEXTURE_MIN_FILTER,
  • param może przyjmować wartości: GL_NEAREST ? używa teksela położonego najbliżej środka rysowanego piksela, GL_LINEAR ? stosuje liniową interpolację (średnio ważoną) czterech tekseli położonych najbliżej środka rysowanego piksela, GL_NEAREST_MIPMAP_NEAREST ? używa obrazu o najbardziej zbliżonej rozdzielczości do rozdzielczości wielokąta oraz filtr GL_NEAREST, GL_NEAREST_MIPMAP_LINEAR ? używa obrazu o najbardziej zbliżonej rozdzielczości do rozdzielczości wielokąta oraz filtr GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST ? stosuje liniową interpolację dwóch mipmap o najbardziej zbliżonej rozdzielczości wielokąta oraz używa filtr GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR ? stosuje liniową interpolację dwóch mipmap o najbliższej zbliżonej rozdzielczości do rozdzielczości wielokąta oraz używa filtra GL_LINEAR.

Filtry wykorzystujące mipmapy można stosować jedynie dla parametru GL_TEXTURE_MIN_FILTER.

Funkcja glBindTexture

Funkcja glBindTexture wiąże obiekt tekstury z bieżącą teksturą. Aby skorzystać w programie z wielu różnych tekstur należy przy zmianie tekstury za każdym razem wywoływać powyższą funkcję glBindTexture.

Procedura wywoływana podczas zmiany wielkości okna (FormResize).

Po zmianie wielkości okna niezbędne jest ponowne wywołanie procedur SetupPixelFormat oraz GLInit. Należy również wywołać funkcje kontekstu tworzenia grafiki oraz kontekstu urządzenia. Przykładowy schemat procedury zawarty jest poniżej:

procedure Tprez_form.FormResize(Sender: TObject);
var
  //dla Open GL
  DC:HDC;
  RC:HGLRC;
begin
  // dla Open GL
  DC:=GetDC(Handle);
  SetupPixelFormat(DC);
  RC:=wglCreateContext(DC);
  wglMakeCurrent(DC, RC);
  GLInit;
end;

Informacje końcowe.

Wywołanie funkcji wczytujących wszelkiego grafiki do zmiennych można wykonać w dwojaki sposób.

zakładając, iż wielkość formy programu nie będzie ulegać zmianom można tekstury załadować w procedurze FormActivate po wywołaniu procedur SetupPixelFormat, GLInit oraz po wywołaniu funkcji kontekstu tworzenia grafiki, jak i kontekstu urządzenia. Przedstawia to poniższy przykład:

procedure Tprez_form.FormActivate(Sender: TObject);
var
  DC:HDC;
  RC:HGLRC;
begin
  DC:=GetDC(Handle);
  SetupPixelFormat(DC);
  RC:=wglCreateContext(DC);
  wglMakeCurrent(DC, RC);
  GLInit;

  //zmienna musi być zainicjowana w sekcji private jako string
  //zmienna := 'plik.jpg';
  //jeżeli plik podstawiony do zmiennej istnieje ...
  if fileexists(sc_programu + 'image\' + zmienna_jpg) then
  begin
    //załadowanie tekstury do zmiennej zmienna_text zainicjowanej 
    //w sekcji private jako glUnit
    LoadTexture(sc_programu + 'image\' + zmienna_jpg, zmienna_text, false);
  end;
end;

przy założeniu swobodnych zmian wielkości formy należy tekstury ładować w umieszczając funkcje na końcu procedury GLInit, jak na poniższym przykładzie:

procedure Tprez_form.GLInit();
begin
  glMatrixMode(GL_PROJECTION);
  glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);
  glMatrixMode(GL_MODELVIEW);
  glEnable(GL_DEPTH_TEST);

  //zmienna musi być zainicjowana w sekcji private jako string
  //zmienna := 'plik.jpg';
  //jeżeli plik podstawiony do zmiennej istnieje ...
  if fileexists(sc_programu + 'image\' + zmienna_jpg) then
  begin
    //załadowanie tekstury do zmiennej zmienna_text zainicjowanej 
    //w sekcji private jako glUnit
    LoadTexture(sc_programu + 'image\' + zmienna_jpg, zmienna_text, false);
  end;
end;

Gotowy przykład implementacji maszyny stanu OpenGL w Delphi.

Kostka 3D

Przykład poniżej przedstawia obracający się sześcian pokryty różnymi teksturami. Umieszczenie procedury FormResize pozwala na swobodne zmiany wielkości formy. Dwa przyciski button pozwalają zmienić interwał Timera co powoduje zmianę szybkości przeprowadzanej animacji.

screen_kostka_opengl.jpg

// -------------------------------------------------------------
//                         Autor: rk7771
//                    -= wrzesień 2005 =-
// Opis:
//       Obracający się sześcian pokryty teksturą
// -------------------------------------------------------------

unit projekt_1_unit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Gl, Glu, OpenGL, textures, ExtCtrls, Buttons, StdCtrls;

type
  Tprojekt_1_form = class(TForm)
    Timer1: TTimer;
    Button1: TButton;
    Button2: TButton;
    procedure FormResize(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure GLInit();
    procedure setupPixelFormat(DC:HDC);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
    wielk : double;
    obrot_poziom : integer;
    obrot_pion : integer;
    sc_programu : string;
    jpg1 : string;
    jpg2 : string;
    jpg3 : string;
    jpg4 : string;
    jpg5 : string;
    jpg6 : string;

    FrontTex : glUint;
    BackTex : glUint;
    TopTex : glUint;
    BottomTex : glUint;
    LeftTex : glUint;
    RightTex : glUint;

    procedure Draw();
  public
    { Public declarations }
  end;

var
  projekt_1_form: Tprojekt_1_form;


implementation

{$R *.dfm}

procedure Tprojekt_1_form.FormActivate(Sender: TObject);
var
  //dla Open GL
  DC:HDC;
  RC:HGLRC;
begin
  projekt_1_form.Top := 25;
  projekt_1_form.Left := 25;

  sc_programu:=ExtractFilePath(ParamStr(0));

  Canvas.Font.Color := clRed;
  Canvas.Font.Size := 12;
  Canvas.TextOut(30, 130, 'Ładowanie grafiki, proszę czekać ...');

  Application.ProcessMessages;

  jpg1 := '1.jpg';
  jpg2 := '2.jpg';
  jpg3 := '3.jpg';
  jpg4 := '4.jpg';
  jpg5 := '5.jpg';
  jpg6 := '6.jpg';

  // dla Open GL
  DC:=GetDC(Handle);
  SetupPixelFormat(DC);
  RC:=wglCreateContext(DC);
  wglMakeCurrent(DC, RC);
  GLInit;
  wielk := -6.0;
end;

procedure Tprojekt_1_form.FormPaint(Sender: TObject);
begin
  Draw();
end;

procedure Tprojekt_1_form.GLInit();
begin
  glMatrixMode(GL_PROJECTION);
  glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);
  glMatrixMode(GL_MODELVIEW);
  glEnable(GL_DEPTH_TEST);

  //kontrola isnienia plików jpg
  if fileexists(sc_programu + 'image\' + jpg1) and fileexists(sc_programu + 'image\' + jpg2)
     and fileexists(sc_programu + 'image\' + jpg3) and fileexists(sc_programu + 'image\' + jpg4)
     and fileexists(sc_programu + 'image\' + jpg5) and fileexists(sc_programu + 'image\' + jpg6) then
  begin
    //wczytanie plików jpg do zmiennych
    LoadTexture(sc_programu + 'image\' + jpg1, FrontTex, false);
    LoadTexture(sc_programu + 'image\' + jpg2, BackTex, false);
    LoadTexture(sc_programu + 'image\' + jpg3, TopTex, false);
    LoadTexture(sc_programu + 'image\' + jpg4, BottomTex, false);
    LoadTexture(sc_programu + 'image\' + jpg5, LeftTex, false);
    LoadTexture(sc_programu + 'image\' + jpg6, RightTex, false);
  end;

end;

procedure Tprojekt_1_form.setupPixelFormat(DC:HDC);
const
   pfd:TPIXELFORMATDESCRIPTOR = (
        nSize:sizeof(TPIXELFORMATDESCRIPTOR);
        nVersion:1;
        dwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or
                PFD_DOUBLEBUFFER;
        iPixelType:PFD_TYPE_RGBA;
        cColorBits:24;
        cRedBits:0; cRedShift:0;
        cGreenBits:0;  cGreenShift:0;
        cBlueBits:0; cBlueShift:0;
        cAlphaBits:0;  cAlphaShift:0;
        cAccumBits: 0;
        cAccumRedBits: 0;
        cAccumGreenBits: 0;
        cAccumBlueBits: 0;
        cAccumAlphaBits: 0;
        cDepthBits:16;
        cStencilBits:0;
        cAuxBuffers:0;
        iLayerType:PFD_MAIN_PLANE;
   bReserved: 0;
   dwLayerMask: 0;
   dwVisibleMask: 0;
   dwDamageMask: 0;
   );
var pixelFormat:integer;
begin
   pixelFormat := ChoosePixelFormat(DC, @pfd);
   if (pixelFormat = 0) then
        exit;
   if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then
        exit;
end;

procedure Tprojekt_1_form.Draw();
begin
  sc_programu:=ExtractFilePath(ParamStr(0));
  //jpg - kontrola istnienia plików
  if fileexists(sc_programu + 'image\' + jpg1) and fileexists(sc_programu + 'image\' + jpg2)
     and fileexists(sc_programu + 'image\' + jpg3) and fileexists(sc_programu + 'image\' + jpg4)
     and fileexists(sc_programu + 'image\' + jpg5) and fileexists(sc_programu + 'image\' + jpg6) then
  begin

    Application.ProcessMessages;

    glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glTranslatef(0.0,0.0, wielk);

    glRotatef(obrot_poziom, 1, 0, 0);
    glRotatef(obrot_pion, 0, 1, 0);

    glEnable(GL_CULL_FACE);
    glEnable(GL_TEXTURE_2D);

    glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
    glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);


    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);


    glBindTexture(GL_TEXTURE_2D, FrontTex);

    glBegin(GL_QUADS);
      // Front Face
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);
      glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);
      glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0,  1.0);
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0,  1.0);
    glEnd();

    glBindTexture(GL_TEXTURE_2D, BackTex);

    glBegin(GL_QUADS);
      // Back Face
      glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
      glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  1.0, -1.0);
      glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  1.0, -1.0);
      glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);
    glEnd();

    glBindTexture(GL_TEXTURE_2D, TopTex);

    glBegin(GL_QUADS);
      // Top Face
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0, -1.0);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  1.0,  1.0);
      glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  1.0,  1.0);
      glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0, -1.0);
    glEnd();

    glBindTexture(GL_TEXTURE_2D, BottomTex);

    glBegin(GL_QUADS);
      // Bottom Face
      glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0);
      glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, -1.0, -1.0);
      glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);
      glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);
    glEnd();

    glBindTexture(GL_TEXTURE_2D, RightTex);

    glBegin(GL_QUADS);
      // Right face
      glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);
      glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0, -1.0);
      glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  1.0,  1.0);
      glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);
    glEnd();

    glBindTexture(GL_TEXTURE_2D, LeftTex);

    glBegin(GL_QUADS);
      // Left Face
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
      glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);
      glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  1.0,  1.0);
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0, -1.0);
    glEnd();

    SwapBuffers(wglGetCurrentDC);

    Canvas.Brush.Style := bsClear; // ustaw tło na przeźroczyste
    Canvas.Font.Color := clRed;
    Canvas.Font.Size := 12;
    Canvas.TextOut(10, 30, 'Intervał timera: ' + inttostr(Timer1.Interval));

  end;
end;

procedure Tprojekt_1_form.Timer1Timer(Sender: TObject);
begin
  obrot_poziom := obrot_poziom + 3;
  obrot_pion := obrot_pion + 6;
  Draw();
end;

procedure Tprojekt_1_form.FormCreate(Sender: TObject);
begin
  projekt_1_form.BorderIcons := [biSystemMenu];

  Timer1.Interval := 100;
end;

procedure Tprojekt_1_form.Button1Click(Sender: TObject);
begin
  //spowolnienie animacji
  Timer1.Interval := Timer1.Interval + 5;
end;

procedure Tprojekt_1_form.Button2Click(Sender: TObject);
begin
  //przyśpieszenie animacji
  Timer1.Interval := Timer1.Interval - 5;
end;

procedure Tprojekt_1_form.FormResize(Sender: TObject);
var
  //dla Open GL
  DC:HDC;
  RC:HGLRC;
begin
  // dla Open GL
  DC:=GetDC(Handle);
  SetupPixelFormat(DC);
  RC:=wglCreateContext(DC);
  wglMakeCurrent(DC, RC);
  GLInit;
end;

end.

Zegar GL

// -------------------------------------------------------------
//                         Autor: rk7771
//                     -= marzec 2007 =-
//                            ZEGAR GL
// -------------------------------------------------------------

unit zegar_gl_unit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Gl, Glu, OpenGL, ExtCtrls, textures;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure kolor_tarczy();
  private
    { Private declarations }
    //dla Open GL
    DC:HDC;
    RC:HGLRC;
    //tarcza zegara
    tarcza_jpg : string;
    tarcza_text : glUint;
    //kolory tarczy zegara
    R_color, G_color, B_color : glFLoat;
    //parametr dla zmiany koloru
    x_color : integer;
    procedure GLInit();
    procedure setupPixelFormat(DC:HDC);
  public
    { Public declarations }
    sc_programu : string;
    procedure draw();
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  sc_programu:=ExtractFilePath(ParamStr(0));
  form1.BorderIcons := [biSystemMenu];
  form1.BorderStyle := bsSingle;
  R_color := 1.0;
  G_color := 1.0;
  B_color := 1.0;
  x_color := 0;
  DC:=GetDC(Handle);
  SetupPixelFormat(DC);
  RC:=wglCreateContext(DC);
  wglMakeCurrent(DC, RC);
  glLoadIdentity;
  GLInit;

  //wczytanie obrazków
  //TARCZA
  tarcza_jpg := 'tarcza.jpg';
  if fileexists(sc_programu + tarcza_jpg) then
  begin
    LoadTexture(sc_programu + tarcza_jpg, tarcza_text, false);
  end;

end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  wglmakecurrent(0,0);
  wgldeletecontext(DC);
  releasedc(handle,DC);
end;

procedure TForm1.GLInit();
begin
  // set viewing projection
  glMatrixMode(GL_PROJECTION);
  glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);
  // position viewer
  glMatrixMode(GL_MODELVIEW);
  glEnable(GL_DEPTH_TEST);

  //kierunek przeciwny do kierunku
  //ruchu wskazówek zegara przy tworzeniu
  //wierzchołków wielokątów
  glFrontFace(GL_CCW);
  //ukrywanie tylnych ścian
  glCullFace(GL_BACK);
end;

procedure TForm1.setupPixelFormat(DC:HDC);
const
   pfd:TPIXELFORMATDESCRIPTOR = (
        nSize:sizeof(TPIXELFORMATDESCRIPTOR);	
        nVersion:1;			
        dwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or
                PFD_DOUBLEBUFFER;	
        iPixelType:PFD_TYPE_RGBA;	
        cColorBits:24;			
        cRedBits:0; cRedShift:0;	
        cGreenBits:0;  cGreenShift:0;
        cBlueBits:0; cBlueShift:0;
        cAlphaBits:0;  cAlphaShift:0;   
        cAccumBits: 0;
        cAccumRedBits: 0;  		
        cAccumGreenBits: 0;     	
        cAccumBlueBits: 0;
        cAccumAlphaBits: 0;
        cDepthBits:16;			
        cStencilBits:0;			
        cAuxBuffers:0;			
        iLayerType:PFD_MAIN_PLANE;  	
   bReserved: 0;
   dwLayerMask: 0;
   dwVisibleMask: 0;
   dwDamageMask: 0;                    
   );
var pixelFormat:integer;
begin
   pixelFormat := ChoosePixelFormat(DC, @pfd);
   if (pixelFormat = 0) then
        exit;
   if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then
        exit;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
  draw();
end;

procedure TForm1.draw();
var
  SysTime : SystemTime;
begin
  if getasynckeystate(VK_ESCAPE) <> 0 then
  begin
    close;
  end;
  if getasynckeystate(VK_F2) <> 0 then
  begin
    kolor_tarczy();
  end;

  GetLocalTime(SysTime);

  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();

  glEnable(GL_TEXTURE_2D);   

  glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
  glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);


  glPushMatrix();

    glTranslatef(0, 0, -3.0);
    glColor3f(R_color, G_color, B_color);

    glBindTexture(GL_TEXTURE_2D, tarcza_text);
    glBegin(GL_QUADS);
      glTexCoord2f(0.0, 1.0); glVertex3f(-1, 1,  0);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1, -1, 0);
      glTexCoord2f(1.0, 0.0); glVertex3f(1, -1, 0);
      glTexCoord2f(1.0, 1.0); glVertex3f(1, 1, 0);
    glEnd();

    glNormal3f( 0.0, 0.0, 1.0);

    // hours
    glPushMatrix();
      glRotate(-SysTime.wHour*30 - SysTime.wMinute/2 +90 , 0, 0, 1);
      glBegin(GL_QUADS);
        glColor3f(0.4, 0.4, 0.4);
        glVertex3f(-0.2, 0, 0);
        glVertex3f(0, -0.03, 0);
        glVertex3f(0.6, 0, 0);
        glVertex3f(0, 0, 0.1);

        glColor3f(1, 0.2, 0.2);
        glVertex3f(-0.2, 0, 0);
        glVertex3f(0, 0, 0.1);
        glVertex3f(0.6, 0, 0);
        glVertex3f(0, 0.03, 0);
      glEnd();
    glPopMatrix();

    // minutes
    glPushMatrix();
      glRotate(-SysTime.wMinute*6 - SysTime.wSecond/10 +90, 0, 0, 1);
      glBegin(GL_QUADS);
        glColor3f(0.4, 0.4, 0.4);
        glVertex3f(-0.2, 0, 0.1);
        glVertex3f(0, -0.03, 0.1);
        glVertex3f(0.7, 0, 0.1);
        glVertex3f(0, 0, 0.2);

        glColor3f(0.8, 0.6, 0.2);
        glVertex3f(-0.2, 0, 0.1);
        glVertex3f(0, 0, 0.1);
        glVertex3f(0.7, 0, 0.1);
        glVertex3f(0, 0.03, 0.1);
      glEnd();
    glPopMatrix();

    // seconds
    glPushMatrix();
      glRotate(-SysTime.wSecond*6 +90, 0, 0, 1);
      glBegin(GL_QUADS);
        glColor3f(0.4, 0.4, 0.4);
        glVertex3f(-0.15, 0, 0.1);
        glVertex3f(0, 0, 0.1);
        glVertex3f(0.8, 0, 0.1);
        glVertex3f(0, 0.025, 0.1);
      glEnd();
    glPopMatrix();

    // seconds
    glPushMatrix();
      glTranslate(0, -0.55, 0);
      glRotate(-SysTime.wSecond*6+90, 0, 0, 1);
      glBegin(GL_QUADS);
        glColor3f(0.2, 0.2, 0.2);
        glVertex3f(-0.08, 0, 0.1);
        glVertex3f(0, 0, 0.1);
        glVertex3f(0.25, 0, 0.1);
        glVertex3f(0, 0.01, 0.1);
      glEnd();
    glPopMatrix();

  glPopMatrix();

  SwapBuffers(wglGetCurrentDC);

  Canvas.Font.Size := 12;
  //przezroczysty kolor tła
  Canvas.Brush.Style := bsClear;
  //granatowe napisy
  Canvas.Font.Color := clNavy;
  Canvas.TextOut(65, 50, FormatDateTime('h:nn:ss', Time));
  //zielone napisy
  Canvas.Font.Color := clGreen;
  Canvas.TextOut(66, 51, FormatDateTime('h:nn:ss', Time));

end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  draw();
end;

procedure TForm1.kolor_tarczy();
begin
  inc(x_color);
  if x_color > 17 then x_color := 0;
  if x_color = 0 then
  begin
    //biały
    R_color := 1.0;
    G_color := 1.0;
    B_color := 1.0;
  end;
  if x_color = 1 then
  begin
    //jasny szary
    R_color := 0.7;
    G_color := 0.7;
    B_color := 0.7;
  end;
  if x_color = 2 then
  begin
    //szary
    R_color := 0.5;
    G_color := 0.5;
    B_color := 0.5;
  end;
  if x_color = 3 then
  begin
    //ciemny szary
    R_color := 0.3;
    G_color := 0.3;
    B_color := 0.3;
  end;
  if x_color = 4 then
  begin
    //czarny
    R_color := 0.0;
    G_color := 0.0;
    B_color := 0.0;
  end;
  if x_color = 5 then
  begin
    //jasny żółty
    R_color := 1.0;
    G_color := 1.0;
    B_color := 0.6;
  end;
  if x_color = 6 then
  begin
    //żółty
    R_color := 1.0;
    G_color := 0.8;
    B_color := 0.2;
  end;
  if x_color = 7 then
  begin
    //pomarańczowy
    R_color := 1.0;
    G_color := 0.6;
    B_color := 0.7;
  end;
  if x_color = 8 then
  begin
    //różowy
    R_color := 1.0;
    G_color := 0.6;
    B_color := 0.6;
  end;
  if x_color = 9 then
  begin
    //czerwony
    R_color := 1.0;
    G_color := 0.2;
    B_color := 0.2;
  end;
  if x_color = 10 then
  begin
    //fioletowy
    R_color := 1.0;
    G_color := 0.6;
    B_color := 1.0;
  end;
  if x_color = 11 then
  begin
    //ciemny fioletowy
    R_color := 0.6;
    G_color := 0.5;
    B_color := 1.0;
  end;
  if x_color = 12 then
  begin
    //jasny zielony
    R_color := 0.6;
    G_color := 1.0;
    B_color := 0.7;
  end;
  if x_color = 13 then
  begin
    //soczysty zielony
    R_color := 0.3;
    G_color := 1.0;
    B_color := 0.4;
  end;
  if x_color = 14 then
  begin
    //ciemniejszy zielony
    R_color := 0.3;
    G_color := 1.0;
    B_color := 0.7;
  end;
  if x_color = 15 then
  begin
    //ciemny zielony
    R_color := 0.0;
    G_color := 0.5;
    B_color := 0.1;
  end;
  if x_color = 16 then
  begin
    //niebieski
    R_color := 0.0;
    G_color := 0.0;
    B_color := 1.0;
  end;
  if x_color = 17 then
  begin
    //jasny niebieski
    R_color := 0.0;
    G_color := 1.0;
    B_color := 1.0;
  end;
end;

end.

Kod źródłowy kostki 3D:

Kostka_3D_OpenGL.zip

10 komentarzy

Poproszę o poprawienie formatowania bo się kompletnie rozjechało :(
Jeszcze kilka dni temu było OK.
Dzięki

Chyba coś jest nie w porządku w funkcji FormResize - u mnie jak zmieniam wielkość okna rośnie w nieskończoność ilość pamięci zajmowanej przez program! W końcu prowadzi to do błędu i zmulenia systemu. Myślę że jest to wina uruchamiania ciągle od nowa funkcji wglCreateContext. Pewnie trzeba najpierw zrobić DelateContext. Ja zrobiłem resize inaczej - poprzez dodanie na początku GLInit wywołani glViewport(0, 0, panel1.width, panel1.height); (u mnie okno OpenGL jest w panelu), natomiast w funkcji FormResize po prostu wywołuję GLInit.
Poza tym artykuł bardzo fajny :)

super art !!!!!!!!!!!!!!!!!
Już dawno takiego nie było
Tak trzymać
Pozdrawiam serdecznie

B. fajne
Czy mogę prosić o następujące:
grafika 2d - Opengl
użycie Cg

Adam Majewski

Wow niezly art ;> Nice nice ;> gw ;>

"GL_POLYGON ? wielokąty o dowolnej liczbie wierzchołków."
dodał bym tam słowo "wypukłe" za "wielokąty" ;>

Proponuje jakoś podzielić tren art na strony, bo ładowanie go jest już meczące

Poprawiłem: procedure Tprez_form.Draw(); na Tprez_form.FormPaint();

  • pewnie błąd powstał przy kopiowaniu kodu źródłowego.
    Poprawiłem: var przypisać begin na var pixelFormat:integer;
    Wyszukałem i poprawiłem literówki (mam nadzieję, że wszystkie ...)
  procedure Tprez_form.Draw();
  begin
    Draw();
  end;

Nie żebym się czepiał, ale nie powinno być tam Tprez_form.FormPaint(); ??

" );
var przypisać
begin"

E... Że co?

No i jeszcze jakieś literówki/orty się czasami zdarzają. Mimo wszystko artykuł imponujący.

OpenGL
Proponuję wrzucić ten schemat w < code >< plain >< /plain >< /code >, tylko potem kolorowanie składni się wykłada :/

Przydał by się przykład w pliku.