1. Opis ogólny OpenGL
2. Dołączenie bibliotek do środowiska Delphi
3. Budowa struktury programu w Delphi
4. Uzupełnienie struktury programu funkcjami
4.1. Ustawienia startowe wywoływane podczas tworzenia formy (FormCreate)
4.2. Aktywacja formy (FormActivate)
4.3. Obsługa malowania na formularzu (FormPaint)
4.4. Obsługa zdarzenia OnTimer komponentu Timer (Timer1Timer)
4.5. Ustawienie formatu pikseli (setupPixelFormat)
4.6. Inicjalizacja maszyny stanu Open GL (GLInit)
4.7. Procedura rysująca - tworzącą grafikę trójwymiarową (Draw)
4.8. Procedura wywoływana podczas zmiany wielkości okna (FormResize)
4.9. Informacje końcowe
5. Przykłady kodów źródłowych
5.1. Kostka 3D - obracający sześcian pokryty teksturą
5.2. Zegar GL
5.3. Zegar GL jako wygaszacz ekranu

OpenGl - Zegar 3D

1. Opis ogólny
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ć, że 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. Nie dostarczają one jednak możliwości jakie oferuje system operacyjny.

2. 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.


3. 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
    { Private declarations }
    procedure Draw();
  public
    { Public declarations }
  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;


4. 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.

4.1. 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 intervał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.

4.2. 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;


4.3. 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;


4.4. 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;


4.5. 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;


4.6. 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 - 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 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 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).


4.7. 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 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 opróżnia natomiast bufor.
  • Funkcja glClear zeruje bufory kolorów i głębi i w efekcie wypełnia okno kolorem czarnym.
  • Funkcja glLoadIdentify powoduje wyczyszczenie bieżącej macierzy przekształceń w celu umożliwienia wykonania kolejnych przekształceń widoku.
  • 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 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 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 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 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 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.

    Współrzędne tekstury

    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 określa sposób filtrowania tekstury. A oto jej struktura:

    GlTexParameteri(Glenum target, Glenum pname, Glint param)
    gdzie:
    1) target reprezentuje rodzaj używanych tekstur, może przyjmować wartości: GL_TEXTURE_1D, GL_TEXTURE_2D lub GL_TEXTURE3D,
    2) 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,
    3) 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, G L_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 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.


4.8. 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;


4.9. Informacje końcowe.
Wywołanie funkcji wczytujących wszelkiego grafiki do zmiennych można wykonać w dwojaki sposób:
  1. zakładając, że 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;
    
  2. 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;
    


5. Przykłady kodów źródłowych.
5.1. Kostka 3D – obracający sześcian pokryty teksturą.
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ć intervał Timera co powoduje zmianę szybkości przeprowadzanej animacji.

// -------------------------------------------------------------
//                         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.


5.2. Zegar GL.

Zegar 3D


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;
  Cylinder : gluQuadricObj;

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.


5.3. Zegar GL jako wygaszacz ekranu.
Kod źródłowy ujęty w pliku pas (sterowanie wielkością ekranu).

unit setup;

interface

uses
  Windows,
  Forms, //dla wielkości ekranu
  Messages;

var Width  : Integer = 800;
    Height : Integer = 600;
    PixelDepth : Integer = 32;
    FullScreen : Boolean = TRUE;


function SetupWin(hInstance : HINST; hPrevInstance : HINST) : Boolean; stdcall;

implementation

function SetupWin(hInstance : HINST; hPrevInstance : HINST) : Boolean; stdcall;
begin
  Width := Screen.Width;
  Height := Screen.Height;
  Result :=TRUE;
end;

end.


Kod źródłowy ujęty w pliku dpr.

program zegar_gl;

uses
  Windows,
  Messages,
  SysUtils,
  Dialogs,
  ToolWin,
  Gl,
  Glu,
  OpenGL,
  Textures,
  INIFiles,
  Setup;

const
  WND_TITLE = 'ZEGAR GL';
  // timer do wyliczenia FPS
  FPS_TIMER = 1;                    
  // wyliczenie FPS co 1000 ms
  FPS_INTERVAL = 500;               

  progr_nazwa = 'Zegar GL';
  progr_wersja = '1.0.0.1';

type
  TCoord = Record
    //kolory tarczy zegara
    R_color, G_color, B_color : glFLoat;
  end;

var
   // globalny uchwyt okna (window handle)
  h_Wnd  : HWND;                 
  // globalny context urządzenia (device context)   
  h_DC   : HDC;                      
  // context renderowania OpenGL (rendering context)
  h_RC   : HGLRC;                    
  // tablica klawiszy
  keys : Array[0..255] of Boolean;   
  // licznik dla FPS
  FPSCount : Integer = 0;           
  // czas trwania (czas zajęcia) 
  ElapsedTime : Integer;             
  // czas ramki
  FrameTime : Integer;               
  mpos : TPoint;

  sc_programu : string;
  ZEGAR_INI : TINIFile;
  ZEGAR_INI_file : string;
  //tarcza zegara
  tarcza_jpg : string;
  tarcza_text : glUint;
  //e_tarcza zegara zewnątrzne
  e_tarcza_jpg : string;
  e_tarcza_text : glUint;
  //e_tarcza zegara środek
  e_tarcza2_jpg : string;
  e_tarcza2_text : glUint;
  //tło - za zegarem
  tlo_jpg : string;
  tlo_text : glUint;
  tlo_pp : boolean = true;
  //parametr dla zmiany koloru
  x_color : integer;
  //obroty tarczy
  x_obr, y_obr : integer;
  obr_poziom, obr_pion : double;

  x_prz, y_prz, z_prz : double;
  pp : boolean = true;
  x_p : integer = 1;
  y_p : integer = 1;
  z_p : integer = 1;

  Vertex : Array of TCoord;

  //MGLA
  fogColor : array [0..3] of GLfloat;

//dodanie ikony z zasobów
//edycja zasobów - tools -> image editor

{$R tarcza.res}

procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;

{----------------------------------------------------------------------}
{  Załadowanie prametrów startowych z pliku txt oraz ini, załadowanie }
{  grafiki (tarcza, obudowa tarczy, tło za tarczą                      }
{----------------------------------------------------------------------}
procedure laduj;
var
  H : THandle;
  kolor_txt : TextFile;
  kolor_file : string;
  tlo_zegar : integer;
begin
  sc_programu:=ExtractFilePath(ParamStr(0));
  // obsługa pliku INI - do słowa usue należy
  // dopisać słowao "INIFiles"
  ZEGAR_INI_file := sc_programu + 'zegar_gl.ini';
  if not fileexists (ZEGAR_INI_file) then
  begin
    //stworzenie pliku ini
    ZEGAR_INI := TINIFile.Create(ZEGAR_INI_file);
    try
      //nazwa, wersja
      ZEGAR_INI.WriteString('Program', 'Nazwa', progr_nazwa);
      ZEGAR_INI.WriteString('Program', 'Wersja', progr_wersja);
      //parametr dla koloru tarczy
      ZEGAR_INI.WriteString('Ustawienia', 'x_kolor', '0');
      //parametr dla obrotu tarczy
      ZEGAR_INI.WriteString('Ustawienia', 'x_obr', '0');
      ZEGAR_INI.WriteString('Ustawienia', 'y_obr', '0');
      //tło za zegarem
      ZEGAR_INI.WriteString('Ustawienia', 'tlo', '0');
    finally
      ZEGAR_INI.Free;
    end;
  end;

  if fileexists (ZEGAR_INI_file) then
  begin
    //stworzenie pliku ini
    ZEGAR_INI := TINIFile.Create(ZEGAR_INI_file);
    try
      //nazwa, wersja
      ZEGAR_INI.WriteString('Program', 'Nazwa', progr_nazwa);
      ZEGAR_INI.WriteString('Program', 'Wersja', progr_wersja);
      //parametr dla koloru tarczy
      x_color := strtoint(ZEGAR_INI.ReadString('Ustawienia', 'x_kolor', '0'));
      //parametr dla obrotu tarczy
      x_obr := strtoint(ZEGAR_INI.ReadString('Ustawienia', 'x_obr', '0'));
      y_obr := strtoint(ZEGAR_INI.ReadString('Ustawienia', 'y_obr', '0'));
      //tło za zegarem
      tlo_zegar := strtoint(ZEGAR_INI.ReadString('Ustawienia', 'tlo', '0'));
      if tlo_zegar = 0 then tlo_pp := false;
      if tlo_zegar = 1 then tlo_pp := true;
    finally
      ZEGAR_INI.Free;
    end;
  end;

  kolor_file := sc_programu + 'zegar_gl.txt';
  SetLength(Vertex, 2);
  if fileexists(kolor_file) then
  begin
    AssignFile(kolor_txt, kolor_file);
    Reset(kolor_txt);
    Readln(kolor_txt, Vertex[1].R_color, Vertex[1].G_color, Vertex[1].B_color);
    CloseFile(kolor_txt);
  end;

  if not fileexists(kolor_file) then
  begin
    Vertex[1].R_color := 1.0;
    Vertex[1].G_color := 1.0;
    Vertex[1].B_color := 1.0;
    AssignFile(kolor_txt, kolor_file);
    Rewrite(kolor_txt);
    Writeln(kolor_txt, Vertex[1].R_color, Vertex[1].G_color, Vertex[1].B_color);
    CloseFile(kolor_txt);
  end;

  //wczytanie obrazków
  //TARCZA
  tarcza_jpg := 'zegar_gl_et1.jpg';
  if fileexists(sc_programu + tarcza_jpg) then
  begin
    LoadTexture(sc_programu + tarcza_jpg, tarcza_text, false);
  end;
  //E_TARCZA (zabudowa zegara)
  e_tarcza_jpg := 'zegar_gl_et2.jpg';
  if fileexists(sc_programu + e_tarcza_jpg) then
  begin
    LoadTexture(sc_programu + e_tarcza_jpg, e_tarcza_text, false);
  end;
  //E_TARCZA2 (zabudowa zegara - srodek)
  e_tarcza2_jpg := 'zegar_gl_et3.jpg';
  if fileexists(sc_programu + e_tarcza2_jpg) then
  begin
    LoadTexture(sc_programu + e_tarcza2_jpg, e_tarcza2_text, false);
  end;
  //TŁO (za zegarem)
  tlo_jpg := 'zegar_gl_tl1.jpg';
  if fileexists(sc_programu + tlo_jpg) then
  begin
    LoadTexture(sc_programu + tlo_jpg, tlo_text, false);
  end;

  //zabezpieczenie przed ponownym uruchomieniem
  H := CreateFileMapping(THANDLE($FFFFFFFF),nil,
  PAGE_READONLY,0,32,'Screen');
  { Jezeli aplikacja jest uruchomiona ( ALREADY_EXISTS ) to zamknij
  druga }
  if GetLastError=ERROR_ALREADY_EXISTS then
  begin
    PostQuitMessage(0);
    CloseHandle(H);
  end;
end;

{------------------------------------------------------------------}
{  Zmiana koloru tarczy - włączenie/wyłączenie                     }
{------------------------------------------------------------------}
procedure kolor_tarczy;
var
  kolor_txt : TextFile;
  kolor_file : string;
  x_color_str : string;
begin
  inc(x_color);
  if x_color > 17 then x_color := 0;
  if x_color = 0 then
  begin
    //biały
    Vertex[1].R_color := 1.0;
    Vertex[1].G_color := 1.0;
    Vertex[1].B_color := 1.0;
  end;
  if x_color = 1 then
  begin
    //jasny szary
    Vertex[1].R_color := 0.7;
    Vertex[1].G_color := 0.7;
    Vertex[1].B_color := 0.7;
  end;
  if x_color = 2 then
  begin
    //szary
    Vertex[1].R_color := 0.5;
    Vertex[1].G_color := 0.5;
    Vertex[1].B_color := 0.5;
  end;
  if x_color = 3 then
  begin
    //ciemny szary
    Vertex[1].R_color := 0.3;
    Vertex[1].G_color := 0.3;
    Vertex[1].B_color := 0.3;
  end;
  if x_color = 4 then
  begin
    //czarny
    Vertex[1].R_color := 0.0;
    Vertex[1].G_color := 0.0;
    Vertex[1].B_color := 0.0;
  end;
  if x_color = 5 then
  begin
    //jasny żółty
    Vertex[1].R_color := 1.0;
    Vertex[1].G_color := 1.0;
    Vertex[1].B_color := 0.6;
  end;
  if x_color = 6 then
  begin
    //żółty
    Vertex[1].R_color := 1.0;
    Vertex[1].G_color := 0.8;
    Vertex[1].B_color := 0.2;
  end;
  if x_color = 7 then
  begin
    //pomarańczowy
    Vertex[1].R_color := 1.0;
    Vertex[1].G_color := 0.6;
    Vertex[1].B_color := 0.7;
  end;
  if x_color = 8 then
  begin
    //różowy
    Vertex[1].R_color := 1.0;
    Vertex[1].G_color := 0.6;
    Vertex[1].B_color := 0.6;
  end;
  if x_color = 9 then
  begin
    //czerwony
    Vertex[1].R_color := 1.0;
    Vertex[1].G_color := 0.2;
    Vertex[1].B_color := 0.2;
  end;
  if x_color = 10 then
  begin
    //fioletowy
    Vertex[1].R_color := 1.0;
    Vertex[1].G_color := 0.6;
    Vertex[1].B_color := 1.0;
  end;
  if x_color = 11 then
  begin
    //ciemny fioletowy
    Vertex[1].R_color := 0.6;
    Vertex[1].G_color := 0.5;
    Vertex[1].B_color := 1.0;
  end;
  if x_color = 12 then
  begin
    //jasny zielony
    Vertex[1].R_color := 0.6;
    Vertex[1].G_color := 1.0;
    Vertex[1].B_color := 0.7;
  end;
  if x_color = 13 then
  begin
    //soczysty zielony
    Vertex[1].R_color := 0.3;
    Vertex[1].G_color := 1.0;
    Vertex[1].B_color := 0.4;
  end;
  if x_color = 14 then
  begin
    //ciemniejszy zielony
    Vertex[1].R_color := 0.3;
    Vertex[1].G_color := 1.0;
    Vertex[1].B_color := 0.7;
  end;
  if x_color = 15 then
  begin
    //ciemny zielony
    Vertex[1].R_color := 0.0;
    Vertex[1].G_color := 0.5;
    Vertex[1].B_color := 0.1;
  end;
  if x_color = 16 then
  begin
    //niebieski
    Vertex[1].R_color := 0.0;
    Vertex[1].G_color := 0.0;
    Vertex[1].B_color := 1.0;
  end;
  if x_color = 17 then
  begin
    //jasny niebieski
    Vertex[1].R_color := 0.0;
    Vertex[1].G_color := 1.0;
    Vertex[1].B_color := 1.0;
  end;

  kolor_file := sc_programu + 'zegar_gl.txt';
  SetLength(Vertex, 2);
  AssignFile(kolor_txt, kolor_file);
  Rewrite(kolor_txt);
  Writeln(kolor_txt, Vertex[1].R_color, Vertex[1].G_color, Vertex[1].B_color);
  CloseFile(kolor_txt);

  x_color_str := inttostr(x_color);
  if fileexists (ZEGAR_INI_file) then
  begin
    //stworzenie pliku ini
    ZEGAR_INI := TINIFile.Create(ZEGAR_INI_file);
    try
      //parametr dla koloru tarczy
      ZEGAR_INI.WriteString('Ustawienia', 'x_kolor', x_color_str);
    finally
      ZEGAR_INI.Free;
    end;
  end;
  sleep(200);
end;

{------------------------------------------------------------------}
{  Obroty tarczy wokół osi x i y - włączenie/wyłączenie            }
{------------------------------------------------------------------}
procedure obrot_tarczy;
var
  x_obr_str, y_obr_str : string;
begin
  x_obr_str := inttostr(x_obr);
  y_obr_str := inttostr(y_obr);

  if fileexists (ZEGAR_INI_file) then
  begin
    //stworzenie pliku ini
    ZEGAR_INI := TINIFile.Create(ZEGAR_INI_file);
    try
      //parametr dla obrotu tarczy
      ZEGAR_INI.WriteString('Ustawienia', 'x_obr', x_obr_str);
      ZEGAR_INI.WriteString('Ustawienia', 'y_obr', y_obr_str);
    finally
      ZEGAR_INI.Free;
    end;
  end;
  sleep(200);
end;

{------------------------------------------------------------------}
{  Tło za zegarem - włączenie/wyłączenie                           }
{------------------------------------------------------------------}
procedure tlo_za_zegarem;
begin
  tlo_pp := not tlo_pp;
  if fileexists (ZEGAR_INI_file) then
  begin
    //stworzenie pliku ini
    ZEGAR_INI := TINIFile.Create(ZEGAR_INI_file);
    try
      //tło za zegarem
      if tlo_pp=true then ZEGAR_INI.WriteString('Ustawienia', 'tlo', '1');
      if tlo_pp=false then ZEGAR_INI.WriteString('Ustawienia', 'tlo', '0');
    finally
      ZEGAR_INI.Free;
    end;
  end;
  sleep(200);
end;

{------------------------------------------------------------------}
{  Funkcje rysujące aktualną scenę                                 }
{------------------------------------------------------------------}
procedure glDraw();
var
  SysTime : SystemTime;
begin
  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(x_prz, y_prz, z_prz);

    glTranslatef(0, 0, -7.0);
    //obroty tarczy
    if y_obr <> 0 then
    begin
      glRotatef(obr_pion, 0, 1, 0);
    end;
    if x_obr <> 0 then
    begin
      glRotatef(obr_poziom, 1, 0, 0);
    end;

    // E_TARCZA (zabudowa zegara)
    glColor3f(1.0, 1.0, 1.0);

    glBindTexture(GL_TEXTURE_2D, e_tarcza_text);
    glBegin(GL_QUADS);
      //góra przód
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.05, 1.05,  0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.05, 0.95, 0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.05, 0.95, 0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(1.05, 1.05, 0.2);
      //dół przód
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.05, -1.05,  0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.05, -0.95, 0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.05, -0.95, 0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(1.05, -1.05, 0.2);
      //lewa - przód
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.05, 1.05,  0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.05, -1.05, 0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(-0.95, -1.05, 0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(-0.95, 1.05, 0.2);
      //prawa - przód
      glTexCoord2f(0.0, 1.0); glVertex3f(1.05, 1.05,  0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(1.05, -1.05, 0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(0.95, -1.05, 0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(0.95, 1.05, 0.2);

      //góra tył
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.05, 1.05,  -0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.05, 0.95, -0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.05, 0.95, -0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(1.05, 1.05, -0.2);
      //dół tył
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.05, -1.05,  -0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.05, -0.95, -0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.05, -0.95, -0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(1.05, -1.05, -0.2);
      //lewa - tył
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.05, 1.05,  -0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.05, -1.05, -0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(-0.95, -1.05, -0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(-0.95, 1.05, -0.2);
      //prawa - tył
      glTexCoord2f(0.0, 1.0); glVertex3f(1.05, 1.05,  -0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(1.05, -1.05, -0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(0.95, -1.05, -0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(0.95, 1.05, -0.2);

      //BOKI
      //góra
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.05, 1.05,  -0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(1.05, 1.05, -0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.05, 1.05, 0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(-1.05, 1.05, 0.2);
      //dół
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.05, -1.05,  -0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(1.05, -1.05, -0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.05, -1.05, 0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(-1.05, -1.05, 0.2);
      //lewa
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.05, -1.05,  -0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.05, 1.05, -0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(-1.05, 1.05, 0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(-1.05, -1.05, 0.2);
      //prawa
      glTexCoord2f(0.0, 1.0); glVertex3f(1.05, -1.05,  -0.2);
      glTexCoord2f(0.0, 0.0); glVertex3f(1.05, 1.05, -0.2);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.05, 1.05, 0.2);
      glTexCoord2f(1.0, 1.0); glVertex3f(1.05, -1.05, 0.2);
    glEnd();

    glBindTexture(GL_TEXTURE_2D, e_tarcza2_text);
    glBegin(GL_QUADS);
      //BOKI - E_TARCZA 2
      //góra
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.04, 1.04,  -0.19);
      glTexCoord2f(0.0, 0.0); glVertex3f(1.04, 1.04, -0.19);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.04, 1.04, 0.19);
      glTexCoord2f(1.0, 1.0); glVertex3f(-1.04, 1.04, 0.19);
      //dół
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.04, -1.04,  -0.19);
      glTexCoord2f(0.0, 0.0); glVertex3f(1.04, -1.04, -0.19);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.04, -1.04, 0.19);
      glTexCoord2f(1.0, 1.0); glVertex3f(-1.04, -1.04, 0.19);
      //lewa
      glTexCoord2f(0.0, 1.0); glVertex3f(-1.04, -1.04,  -0.19);
      glTexCoord2f(0.0, 0.0); glVertex3f(-1.04, 1.04, -0.19);
      glTexCoord2f(1.0, 0.0); glVertex3f(-1.04, 1.04, 0.19);
      glTexCoord2f(1.0, 1.0); glVertex3f(-1.04, -1.04, 0.19);
      //prawa
      glTexCoord2f(0.0, 1.0); glVertex3f(1.04, -1.04,  -0.19);
      glTexCoord2f(0.0, 0.0); glVertex3f(1.04, 1.04, -0.19);
      glTexCoord2f(1.0, 0.0); glVertex3f(1.04, 1.04, 0.19);
      glTexCoord2f(1.0, 1.0); glVertex3f(1.04, -1.04, 0.19);
    glEnd();


    glColor3f(Vertex[1].R_color, Vertex[1].G_color, Vertex[1].B_color);

    // *** PRZÓD ***

    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);

    // *** PRZÓD ***

    // 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 - large (duży - na środku)
    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 - small (mały - poniżej)
    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();

    // *** TYŁ ***

    // 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.1);
        glVertex3f(0, -0.03, -0.1);
        glVertex3f(0.6, 0, -0.1);
        glVertex3f(0, 0, -0.2);

        glColor3f(1, 0.2, 0.2);
        glVertex3f(-0.2, 0, -0.1);
        glVertex3f(0, 0, -0.2);
        glVertex3f(0.6, 0, -0.1);
        glVertex3f(0, 0.03, -0.1);
      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.2);
        glVertex3f(0, -0.03, -0.2);
        glVertex3f(0.7, 0, -0.2);
        glVertex3f(0, 0, -0.3);

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

    // seconds - large (duży - na środku)
    glPushMatrix();
      glRotate(-SysTime.wSecond*6 +90, 0, 0, 1);
      glBegin(GL_QUADS);
        glColor3f(0.4, 0.4, 0.4);
        glVertex3f(-0.15, 0, -0.2);
        glVertex3f(0, 0, -0.2);
        glVertex3f(0.8, 0, -0.2);
        glVertex3f(0, 0.025, -0.2);
      glEnd();
    glPopMatrix();

    // seconds - small (mały - poniżej)
    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.2);
        glVertex3f(0, 0, -0.2);
        glVertex3f(0.25, 0, -0.2);
        glVertex3f(0, 0.01, -0.2);
      glEnd();
    glPopMatrix();

  glPopMatrix();

  if tlo_pp=true then
  begin
    //TŁO (za zegarem)
    //nieprzeźroczyste
    glPushMatrix();
      glTranslatef(0.0,0.0,-12);
      glRotatef(ElapsedTime/60, 0.0, 0.0, 1.0);
      glRotatef(20*sin(ElapsedTime/800), 1.0, 0.0, 0.0);
      glRotatef(30*sin(ElapsedTime/600), 0.0, 1.0, 0.0);
      glCOlor3f(0.5, 0.5, 0.5);
      glBindTexture(GL_TEXTURE_2D, tlo_text);
      glBegin(GL_QUADS);
        glTexCoord2f(0.0, 0.0); glVertex3f(-20.0, -20.0, -5);
        glTexCoord2f(1.0, 0.0); glVertex3f( 20.0, -20.0, -5);
        glTexCoord2f(1.0, 1.0); glVertex3f( 20.0,  20.0, -5);
        glTexCoord2f(0.0, 1.0); glVertex3f(-20.0,  20.0, -5);
      glEnd;
    glPopMatrix();

    //przeźroczyste
    glPushMatrix();
      //EFEKT MGLY
      //efekt mgly: GL_LINEAR, GL_EXP, GL_EXP2
      glFogi(GL_FOG_MODE,GL_EXP);

      //kolor mgly
      fogColor[0] := 0.8;
      fogColor[1] := 0.5;
      fogColor[2] := 0.5;
      fogColor[3] := 0.5;

      glFogfv(GL_FOG_COLOR, @fogColor);

      //gestosc mgly
      glFogf(GL_FOG_DENSITY, 0.025);

      // dokładność perspektywy mgły
      // GL_NICEST, GL_FASTEST, GL_DONT_CARE
      //rysowanie mgły dla każdego wierzchołka bryły - spowalnia
      // glHint(GL_FOG_HINT, GL_NICEST);  

      glFogf(GL_FOG_START, -2.0);
      glFogf(GL_FOG_END, 2.0);

      //włączenie mgły
      glEnable(GL_FOG);

      //przeźroczystość
      glEnable(GL_BLEND);
      glBlendFunc(GL_ONE, GL_SRC_ALPHA);

      glTranslatef(0.0,0.0,-12);
      glRotatef(ElapsedTime/60, 0.0, 0.0, 1.0);
      glRotatef(20*sin(ElapsedTime/800), 1.0, 0.0, 0.0);
      glRotatef(30*sin(ElapsedTime/600), 0.0, 1.0, 0.0);
      glCOlor3f(0.5, 0.5, 0.5);
      glBindTexture(GL_TEXTURE_2D, tlo_text);
      glBegin(GL_QUADS);
        glTexCoord2f(0.0, 0.0); glVertex3f(-20.0, -20.0, 0);
        glTexCoord2f(1.0, 0.0); glVertex3f( 20.0, -20.0, 0);
        glTexCoord2f(1.0, 1.0); glVertex3f( 20.0,  20.0, 0);
        glTexCoord2f(0.0, 1.0); glVertex3f(-20.0,  20.0, 0);
      glEnd;

      //wyłączenie przeźroczystości
      glDisable(GL_BLEND);

      //wyłączenie mgły
      glDisable(GL_FOG);

    glPopMatrix;
  end;

  SwapBuffers(wglGetCurrentDC);
end;

{------------------------------------------------------------------}
{  Initializacja OpenGL                                               }
{------------------------------------------------------------------}
procedure glInit();
begin
  // ustawiamy czarne tło
  glClearColor(0.0, 0.0, 0.0, 0.0); 	   
  // ustawienie kolorów wielokąta
  // gdzie parametr GL_SMOTH nakazuje
  // zastosowanie płynnego przejścia
  // pomiędzy kolorami wierzchołków
  // poprzez interpolacje
  //glShadeModel(GL_SMOOTH);             
  // ustawiamy głębokość bufora koloru
  // wartości z zakresu 0.0 do 1.0 włącznie
  glClearDepth(1.0);                     
  // włączenie głębokości bufora
  glEnable(GL_DEPTH_TEST);               
  // ustawienie poprawnej początkowej wartości głębokości okna
  glDepthFunc(GL_LESS);		               

  glEnable(GL_COLOR_MATERIAL);

  // przekazujemy wskazówki dotyczące działania
  // biblioteki OpenGL - a dokładniej wpływając
  // na jakość koloru i interpolacji tekstur 
  // poprzez żądanie użycia metody dającej najlepszą jakość (GL_NICEST)
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
            (GL_PERSPECTIVE_CORRECTION_HINT)


  // załadowanie parametrów z pliku txt i ini, załadowanie grafiki
  laduj;

  //włączenie światła
  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHTING);
  glPointSize(4);
end;

{------------------------------------------------------------------}
{  Zmiana rozdzielczości ekranu                                    }
{------------------------------------------------------------------}
procedure glResizeWnd(Width, Height : Integer);
begin

  // zabezpieczenie przed dzieleniem przez zero
  if (Height = 0) then                
    Height := 1;

  // zdefiniowanie widoku - gdzie dwa pierwsze parametry
  // to x i y określające prawy dolny róg widoku  
  glViewport(0, 0, Width, Height);    

  // ustawienie odwołania do macierzy rzutowania
  // macierz rzutowania używana jest do definiowania
 // bryły obcinania
  glMatrixMode(GL_PROJECTION);        

  // załadowanie macierzy tożsamościowej do bieżącej 
  // macierzy (macierzy rzutowania ustawionej powyżej)
  glLoadIdentity();                   

  // określenie perspektywy, parametry kolejno
  //- kąt pola widzenia w kierunku pionowym (45.0)
  //- stosunek wysokości do szerokości bryły (Width/Height),
  //-odległość do bliższej i dalszej płaszczyzny obcinania (0.1 i 100.0)
  gluPerspective(45.0, Width/Height, 0.1, 100.0);

  // ustawienie odwołania do macierzy widoku
  glMatrixMode(GL_MODELVIEW);         

  // załadowanie macierzy tożsamościowej do bieżącej
  // macierzy (macierzy widoku ustawionej powyżej)
  glLoadIdentity();                   

end;

{------------------------------------------------------------------}
{  Procedura nacisnięcia klawisza                                  }
{------------------------------------------------------------------}
procedure ProcessKeys;
begin
  if Keys[VK_ESCAPE] then
  begin
    PostQuitMessage(0);
  end;
  if Keys[VK_UP] then
  begin
    PostQuitMessage(0);
  end;
  if Keys[VK_DOWN] then
  begin
    PostQuitMessage(0);
  end;
  if Keys[VK_LEFT] then
  begin
    PostQuitMessage(0);
  end;
  if Keys[VK_RIGHT] then
  begin
    PostQuitMessage(0);
  end;

  if Keys[VK_F1] then
  begin
    MessageBox(0, 'Zegar GL - Screen Saver - (c) rk7771 -=marzec 2007=-', 
              'O programie ...', MB_OK);
    PostQuitMessage(0);
  end;

  if Keys[VK_F2] then
  begin
    // zmiana koloru tarczy
    kolor_tarczy; 
  end;

  if Keys[VK_F3] then
  begin
    inc(y_obr);
    if y_obr > 1 then y_obr:=0;
    //zmiana sposobu obracania
    obrot_tarczy; 
  end;

  if Keys[VK_F4] then
  begin
    inc(x_obr);
    if x_obr > 1 then x_obr:=0;
    //zmiana sposobu obracania
    obrot_tarczy; 
  end;

  if Keys[VK_F5] then
  begin
    //ustawienie tła za zegarem
    tlo_za_zegarem;  
  end;
end;

{------------------------------------------------------------------}
{  Określenie reakcji aplikacji przy otrzymaniu komunikatu         }
{------------------------------------------------------------------}
function WndProc(hWnd: HWND; Msg: UINT;  wParam: WPARAM;  lParam: LPARAM): LRESULT; stdcall;
begin
  case (Msg) of
    WM_CREATE:
      begin
        // Miejsce na wstawienie funkcji, które maja być wykonane przy starcie programu
      end;

    WM_CLOSE:
      begin
        PostQuitMessage(0);
        Result := 0
      end;

    // zmiana parametru wparam po naciśnieciu klawisza
    WM_KEYDOWN:       
      begin
        keys[wParam] := True;
        Result := 0;
      end;

    // zmiana parametru wparam przy naciśnieciu klawisza
    WM_KEYUP:         
      begin
        keys[wParam] := False;
        Result := 0;
      end;
    // Zmiana wielkości okna
    WM_SIZE:          
      begin
        glResizeWnd(LOWORD(lParam),HIWORD(lParam));
        Result := 0;
      end;

    // Użycie Timer'a
    WM_TIMER :                     
      begin
        if wParam = FPS_TIMER then
        begin
          // nowe obliczenia po jednej sekundzie
          FPSCount :=Round(FPSCount * 1000/FPS_INTERVAL);   
          SetWindowText(h_Wnd, PChar(WND_TITLE + '   [' + intToStr(FPSCount) + ' FPS]'));
          FPSCount := 0;
          Result := 0;
        end;
      end;
    else
      // domyślne ustawienia jeżeli nie została wywołana żadna z powyższych funkcji
      Result := DefWindowProc(hWnd, Msg, wParam, lParam);    
  end;
end;


{---------------------------------------------------------------------}
{  Zmiana właściwości okna przy starcie programu                      }
{---------------------------------------------------------------------}
procedure glKillWnd(Fullscreen : Boolean);
begin
  // Zmień w tle jezeli nie jest pełny ekran
  if Fullscreen then
  begin
    ChangeDisplaySettings(devmode(nil^), 0);
    ShowCursor(True);
  end;

  // Renderuj (generuj grafikę) w bieżącym context'ie,
  // i wydaj urządzeniu context jeżeli jest uzyty do generowania grafiki
  if (not wglMakeCurrent(h_DC, 0)) then
    MessageBox(0, 'Błąd wydania DC i RC!', 'Error', MB_OK or MB_ICONERROR);

  // Uwaga podczas kasowania renderowania context'u
  if (not wglDeleteContext(h_RC)) then
  begin
    MessageBox(0, 'Bład renderowania kontextu', 'Error', MB_OK or MB_ICONERROR);
    h_RC := 0;
  end;

  // Uwaga podczas zmiany context'u urządzenia
  if ((h_DC > 0) and (ReleaseDC(h_Wnd, h_DC) = 0)) then
  begin
    MessageBox(0, 'Bład kontextu urządzenia!', 'Error', MB_OK or MB_ICONERROR);
    h_DC := 0;
  end;

  // Uwaga podczas niszczenia okna
  if ((h_Wnd <> 0) and (not DestroyWindow(h_Wnd))) then
  begin
    MessageBox(0, 'Nie mogę usunąć/zniszczyć okna!', 'Error', MB_OK or MB_ICONERROR);
    h_Wnd := 0;
  end;

  // Uwaga jeżeli clasa okna nie jest zarejestrowana
  if (not UnRegisterClass('OpenGL', hInstance)) then
  begin
    MessageBox(0, 'Nie mogę wyrejestrować klasy okna!', 'Error', MB_OK or MB_ICONERROR);
    hInstance := 0;
  end;

end;

{----------------------------------------------------------------------------------------}
{  Utworzenie okna oraz obsługa komunikatów OpenGL podczas generowania context'u grafiki}
{----------------------------------------------------------------------------------------}
function glCreateWnd(Width, Height : Integer; Fullscreen : Boolean; PixelDepth : Integer) 
: Boolean;
var
  // klasa okna
  wndClass : TWndClass;         
  // styl okna 
  dwStyle : DWORD;               
  // rozszerzony styl okna
  dwExStyle : DWORD;             
  // ustawienia ekranu (pełny ekran, itp...)
  dmScreenSettings : DEVMODE;    
  // ustawienia dla generowania OpenGL
  PixelFormat : GLuint;          
  // bieżąca instancja
  h_Instance : HINST;            
  // ustawienia OpenGL dla okna
  pfd : TPIXELFORMATDESCRIPTOR;  
begin
  h_Instance := GetModuleHandle(nil);
  // wyczyszczenie struktury klasy okna
  ZeroMemory(@wndClass, SizeOf(wndClass));  

  // ustawienie klasy okna
  with wndClass do                    
  begin
    style         := CS_HREDRAW or    // odświeżanie okna jeżeli długość się zmieni
                     CS_VREDRAW or    // odświeżanie okna jeżeli wysokość się zmieni
                     CS_OWNDC;        // unikalny kontekst urządzenia dla okna
    // ustaw procedurę okna na funkcję WndProc
    lpfnWndProc   := @WndProc;        
    hInstance     := h_Instance;
    hCursor       := LoadCursor(0, IDC_ARROW);
    lpszClassName := 'OpenGL';
  end;

  //Uwaga przy rejestracji klasy okna
  if (RegisterClass(wndClass) = 0) then
  begin
    MessageBox(0, 'Błąd podczas rejestracji klasy okna!', 'Error', MB_OK or MB_ICONERROR);
    Result := False;
    Exit
  end;

  // Zmiana na pełny ekran jeżeli taki stan jest oczekiwany
  if Fullscreen then
  begin
    ZeroMemory(@dmScreenSettings, SizeOf(dmScreenSettings));
    // ustawienie parametrów właściwości ekranu
    with dmScreenSettings do begin              
      dmSize       := SizeOf(dmScreenSettings);
      dmPelsWidth  := Width;                    // długość okna
      dmPelsHeight := Height;                   // wysokość okna
      dmBitsPerPel := PixelDepth;             // głębia kolorów okna
      dmFields     := DM_PELSWIDTH or DM_PELSHEIGHT or DM_BITSPERPEL;
    end;

    // Próbuj zmienić ustawienia ekranu na pełny ekran
    if (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN) = DISP_CHANGE_FAILED) then
    begin
      MessageBox(0, 'Nie mogę przełączyć na pełny ekran!', 'Error', MB_OK or MB_ICONERROR);
      Fullscreen := False;
    end;
  end;

  // Jeżeli ustawienia wskazują na pełny ekran
  if (Fullscreen) then
  begin
    dwStyle := WS_POPUP or
               WS_CLIPCHILDREN
               or WS_CLIPSIBLINGS;
    dwExStyle := WS_EX_APPWINDOW;
    // wyłącz kursor
    ShowCursor(False);                    
  end
  else
  begin
    dwStyle := WS_OVERLAPPEDWINDOW or
               WS_CLIPCHILDREN or
               WS_CLIPSIBLINGS;
    dwExStyle := WS_EX_APPWINDOW or
                 WS_EX_WINDOWEDGE;
    ShowCursor(False);                    // wyłącz kursor
  end;

  // Uwaga podczas tworzenia aktualnego okna
  h_Wnd := CreateWindowEx(dwExStyle,        // Rozszerzony styl okna
                          'OpenGL',              // nazwa klasy
                          WND_TITLE,       // opis okna (caption)
                          dwStyle,                // styl okna
                          0, 0,                       // pozycja okna
                          Width, Height,      // wielkość okna
                          0,                           // brak rodzica okna
                          0,                         // brak menu
                          h_Instance,          // instancja
                          nil);                      // obsługa tworzenia okna
  if h_Wnd = 0 then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Nie moge stworzyć okna!', 'Error', MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Próba pobrania context'u urządzenia
  h_DC := GetDC(h_Wnd);
  if (h_DC = 0) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Nie mogę pobrać kontekstu urządzenia!', 'Error', MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Ustawienia okna OpenGL
  with pfd do
  begin
    // rozmiar struktury
    nSize           := SizeOf(TPIXELFORMATDESCRIPTOR); 
    // zawsze wartość 1
    nVersion        := 1;                              
    // znaczniki właściwości bufora pikseli - dla okna
    // - dla bufora rysowania OepnGL           
    // - dla podwójnego buforowania  
    dwFlags         := PFD_DRAW_TO_WINDOW    
                           or PFD_SUPPORT_OPENGL         
                           or PFD_DOUBLEBUFFER;            
    // typ danych piksela
    iPixelType      := PFD_TYPE_RGBA;       
    // liczba bitów piksela           
    cColorBits      := PixelDepth;             
    // liczba bitów koloru czerwonego        
    cRedBits        := 0;                              
    // przesunięcie bitów koloru czerwonego
    cRedShift       := 0;                              
    // liczba bitów koloru zielonego
    cGreenBits      := 0;                             
    // przesunięcie bitów koloru zielonego
    cGreenShift     := 0;                            
    // liczba bitów koloru niebieskiego 
    cBlueBits       := 0;                              
    // przesunięcie bitów koloru niebieskiego
    cBlueShift      := 0;                              
    // liczba bitów alfa  - nie obsługiwane 
    cAlphaBits      := 0;                              
    // przesunięcie bitów alfa - nie obsługiwane
    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 - nie obsługiwane
    cAuxBuffers     := 0;                              
    // nie jest już wykorzystywany - ignorowany
    iLayerType      := PFD_MAIN_PLANE;                 
    // liczba podkładanych i nakładanych jednostek
    bReserved       := 0;                              
    // nie jest już wykorzystywany - ignorowany
    dwLayerMask     := 0;                              
    // indeks podłożonej płaszczyzny
    dwVisibleMask   := 0;                              
    // nie jest już wykorzystywany - ignorowany
    dwDamageMask    := 0;                              
  end;

  // Uwaga podczas wyszukiwania obsługiwanego przez urządzenie formatu pixeli
  PixelFormat := ChoosePixelFormat(h_DC, @pfd);
  if (PixelFormat = 0) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Nie mogę znaleźć obsługiwanego formatu pikseli', 'Error', 
               MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Ustawienie specyfikacji urządzenia w zakresie formatu pixeli
  if (not SetPixelFormat(h_DC, PixelFormat, @pfd)) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Nie mogę ustawić formatu pikseli', 'Error', 
               MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Utworzenie renderowania context'u
  h_RC := wglCreateContext(h_DC);
  if (h_RC = 0) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Nie mogę utworzyć renderowania kontekstu OpenGL', 'Error', 
               MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Użycie specyfikacji OpenGL do generowania context'u
  if (not wglMakeCurrent(h_DC, h_RC)) then
  begin
    glKillWnd(Fullscreen);
    MessageBox(0, 'Nie mogę aktywować renderowania kontekstu OpenGL', 'Error', 
               MB_OK or MB_ICONERROR);
    Result := False;
    Exit;
  end;

  // Inicjalizacja Timer'a do wyliczania FPS
  SetTimer(h_Wnd, FPS_TIMER, FPS_INTERVAL, nil);

  // Ustawienia do zrobienia w przypadku gdy okno jest pierwszoplanowe (na wierzchu)
  ShowWindow(h_Wnd, SW_SHOW);
  SetForegroundWindow(h_Wnd);
  SetFocus(h_Wnd);

  // Sprawdzenie zmiany właściwości okna OpenGL
  glResizeWnd(Width, Height);
  glInit();

  Result := True;
end;


{--------------------------------------------------------------------}
{  Obsługa komunikatów w pętli                                       }
{--------------------------------------------------------------------}
function WinMain(hInstance : HINST; hPrevInstance : HINST;
                 lpCmdLine : PChar; nCmdShow : Integer) : Integer; stdcall;
var
  msg : TMsg;
  finished : Boolean;
  DemoStart, LastTime : DWord;
begin
  finished := False;

  // Pobranie rozdzielczości oraz głębi - wywołuje funkcję z setup.pas
  if not SetupWin(hInstance, hPrevInstance) then
  begin
    Result := 0;
    Exit;
  end;

  // Wykonanie inicjalizacji programu (aplikacji)
  if not glCreateWnd(setup.Width, Setup.Height, Setup.FullScreen, Setup.PixelDepth) then
  begin
    Result := 0;
    Exit;
  end;

  // Pobranie czasu przy starcie programu
  DemoStart := GetTickCount();            
  SetCursorPos(400,300);

  // Pętla własnych komunikatów
  while not finished do
  begin
    // Sprawdzenie czy są komunikaty dla tego okna
    if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then 
    begin
      // Wyjście jeżeli otrzymano komunikat WM_QUIT
      if (msg.message = WM_QUIT) then     
        finished := True
      else
      // Przetłumaczenie i odesłanie komunikatu
      begin                               
          // Przetłumaczenie
          TranslateMessage(msg);            
          // Odesłanie
          DispatchMessage(msg);             
      end;
    end
    else
    begin
      // Zwiększ licznik FPS
      Inc(FPSCount);                      

      FrameTime := GetTickCount() - ElapsedTime - DemoStart;
      LastTime :=ElapsedTime;
      // Wyliczenie Elapsed Time
      ElapsedTime :=GetTickCount() - DemoStart;     
      // Wyliczenie średniej
      ElapsedTime :=(LastTime + ElapsedTime) DIV 2; 

      //Obroty zegara na scenie
      if y_obr <> 0 then
      begin
        obr_pion := obr_pion + 0.05;
      end;
      if x_obr <> 0 then
      begin
        obr_poziom := obr_poziom + 0.04;
      end;

      //Poruszanie zegara po scenie
      if pp = true then
      begin
        if x_prz > 2.5 then x_p := -1;
        if x_prz <-2.5 then x_p := 1;
        if x_p = 1 then x_prz := x_prz + 0.001;
        if x_p = -1 then x_prz := x_prz - 0.001;

        if y_prz > 2 then y_p := -1;
        if y_prz <-2 then y_p := 1;
        if y_p = 1 then y_prz := y_prz + 0.001;
        if y_p = -1 then y_prz := y_prz - 0.001;

        if z_prz >-2 then z_p := -1;
        if z_prz <-8 then z_p := 1;
        if z_p = 1 then z_prz := z_prz + 0.001;
        if z_p = -1 then z_prz := z_prz - 0.001;
      end;

      //Wywołanie procedury rysującej scenę
      glDraw();                           // Rysuj scenę

      if (keys[VK_ESCAPE]) then           
        // Po naciśnięciu ESC ustaw wartość finished na TRUE
        finished := True
      else
        // Sprawdzenie czy nie został naciśnięty klawisz
        ProcessKeys;                      
    end;
  end;
  glKillWnd(FALSE);
  Result := msg.wParam;
end;


begin
  WinMain( hInstance, hPrevInst, CmdLine, CmdShow );
end.


Poniżej zrzut ekranu z innego projektu pokazującego możliwość OpenGl w zakresie tworzenia prezentacji pomieszczeń 3D:

Przestrzeń 3D