OpenGL
rk7771
1 Opis podstawowy.
2 Implementacja OpenGL w środowisku Delphi.
2.1 Krok pierwszy - dołączenie bibliotek do środowiska Delphi.
2.2 Krok drugi - budowa struktury programu w Delphi.
2.3 Krok trzeci - uzupełnienie struktury programu funkcjami.
2.4 Ustawienia startowe wywoływane podczas tworzenia formy (FormCreate)
2.5 Aktywacja formy (FormActivate).
2.6 Obsługa malowania na formularzu (FormPaint).
2.7 Obsługa zdarzenia OnTimer komponentu Timer (Timer1Timer).
2.8 Ustawienie formatu pikseli (setupPixelFormat).
2.9 Inicjalizacja maszyny stanu Open GL (GLInit).
2.9.1 Macierz modelowania
2.9.2 Rzutowanie perspektywistyczne
2.9.3 Rzutowanie ortograficzne
2.9.4 Usuwanie ukrytych powierzchni
2.9.5 Kierunek tworzenia wierzchołków
2.9.6 Ukrywanie ścian
2.10 Procedura rysująca - tworzącą grafikę trójwymiarową (Draw).
2.10.7 Funkcja SwapBuffers
2.10.8 Funkcja glFlush
2.10.9 Funkcja glClear
2.10.10 Funkcja glLoadIdentify
2.10.11 Funkcja glTranslatef
2.10.12 Funkcja glRotatef
2.10.13 Funkcja glEnable
2.10.14 Funkcja glBegin
2.10.15 Funkcja glEnd
2.10.16 Funkcja glVertex
2.10.17 Funkcja glTexCoord2f
2.10.18 Funkcja glTexParamateri
2.10.19 Funkcja glBindTexture
2.11 Procedura wywoływana podczas zmiany wielkości okna (FormResize).
2.12 Informacje końcowe.
3 Gotowy przykład implementacji maszyny stanu OpenGL w Delphi.
3.13 Kostka 3D
3.14 Zegar GL
Opis podstawowy.
OpenGL zaprojektowany został z myślą tworzenia różnego rodzaju aplikacji. Dostępny jest dla wszystkich najważniejszych platform w różnych konfiguracjach sprzętowych. Biblioteka niskiego poziomu OpenGL umożliwia generowanie zaawansowanej grafiki i jest wykorzystywana w komputerowym wspomaganiu projektowania czy grach. W szczególności można powiedzieć, iż OpenGL określa interfejs programowy udostępniający programiście możliwości graficzne platformy na której tworzona będzie aplikacja.
W celu umożliwienia pełnej kontroli nad tworzoną grafiką i zwiększeniu uniwersalności OpenGL zawiera jedynie operacje niskiego poziomu. Operacje niskiego poziomu pozwalają stworzyć własną bibliotekę graficzną wysokiego poziomu. Przykładem biblioteki wysokiego poziomu może być GLU (OpenGL Utility Library), która jest dostarczana wraz z większością dystrybucji OpenGL. Biblioteka OpenGL związana jest z tworzeniem wyłącznie grafiki
i w przeciwieństwie do DirectX nie umożliwia ona tworzenia operacji związanych
z tworzeniem dźwięku, interakcją z użytkownikiem czy tworzenia interfejsu sieciowego.
Od roku 1992 rozwój specyfikacji OpenGL prowadzony jest przez OpenGL Architecture Review Board (ARB), w skład której wchodzą firmy ATI, Compaq (obecnie HP Compaq), Evans & Sutherland, Hewlett-Packard, IBM, Intel, Intergraph, nVidia, Microsoft oraz Silicon Graphics. Sama specyfikacja OpenGL opracowana została natomiast przez firmę Silicon Graphics, Inc. (SGI) jako interfejs służący do tworzenia grafiki, który jest niezależny od platformy. Zadaniem ARB jest przygotowywanie specyfikacji OpenGL i tym samym dyktowanie funkcjonalności kolejnych dystrybucji interfejsu OpenGL tworzonych przez producentów.
OpenGL wykorzystuje maszynę stanów, stany opisują natomiast sposób wykonywania operacji graficznych. Sama specyfikacja OpenGL zawiera zaś kilkaset funkcji udostępniających możliwości sprzętu graficznego. Korzystając z interfejsu programowego OpenGL można określać wiele aspektów maszyny stanów, takich jak na przykład:
- bieżący kolor,
- oświetlenie,
- sposób łączenia kolorów
i tak dalej. Sposób tworzenia grafiki jest więc określony przez bieżącą konfigurację maszyny stanów. Zrozumienie znaczenia poszczególnych stanów maszyny umożliwia poprawne tworzenie aplikacji. Niewłaściwy wybór stanu skutkuje natomiast nieprzewidzianymi efektami operacji graficznych.
Jądrem biblioteki OpenGL jest potok tworzonej grafiki, a uświadomienie sobie, iż wszystko, co jest widoczne na ekranie, stanowi rezultat wykonania szeregu operacji w potoku jest najważniejszą rzeczą. Większość operacji w potoku wykonywana jest automatycznie przez bibliotekę OpenGL.
Bibliotekami uzupełniającymi OpenGL jest pakiet GLUT {OpenGL Utility Toolkit) zawierający zestaw pomocniczych bibliotek. Jest on dostępny dla najważniejszych platform. Biblioteki uzupełniające dają możliwość tworzenia menu, okien czy interfejsu interakcji z użytkownikiem. Są one niezależne od platformy dając możliwość łatwego przenoszenia między różnymi systemami operacyjnymi (np.: z Windows do Unix). Biblioteki GLUT są zupełnie wystarczające w przypadku tworzenia prostych aplikacji czy przykładów demonstracyjnych. Niedostarczają one jednak możliwości jakie oferuje system operacyjny.
Implementacja OpenGL w środowisku Delphi.
Krok pierwszy - dołączenie bibliotek do środowiska Delphi.
Do prawidłowego działania OpenGL w Delphi niezbędne jest dołączenie plików pas, tj.:
- GL.pas,
- GLext.pas,
- GLU.pas,
- GLUT.pas.
Są to biblioteki podstawowe wraz z bibliotekami rozszerzonymi. W celu dołączenia tekstur w formacie plików jpg do tworzonych brył można wykorzystać plik:
- textures.pas,
natomiast wykorzystanie plików bmp wymaga dołączenia pliku:
- bmp.pas.
Krok drugi - budowa struktury programu w Delphi.
W nowym projekcie tworzonej aplikacji w delphi utworzyć należy procedury:
- tworzenia formy: FormCreate,
- aktywacji formy: FormActivate,
- obsługi zmiany wielkości formy: FormResize,
- rysowania formy: FormPaint,
- obsługi zegara: Timer1Timer,
- inicjalizacji maszyny stanu Open GL: GLInit,
- ustawienie formatu pikseli: setupPixelFormat,
- tworzenia grafiki: Draw.
Procedury: FormCreate, FormActivate, FormResize, FormPaint oraz Timer1Timer tworzymy w object inspektorze. Procedury: GLInit, setupPixelFormat oraz Draw dodajemy ręcznie w kodzie źrodłowym. W sekcji uses dodajemy obsługę bibliotek OpenGl tj.: Gl, Glu, OpenGL oraz dodajemy pliki ładowania tekstur. Stosownie od preferencji: textures lub bmp. Do programu dodajemy również Timer w celu obsługi animacji.
Wygląd schematu programu:
unit nazwa_unit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Gl, Glu, OpenGL, Textures, ExtCtrls;
type
Tprez_form = class(TForm)
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure GLInit();
procedure setupPixelFormat(DC:HDC);
private
procedure Draw();
end;
var
prez_form: Tprez_form;
implementation
{$R *.dfm}
procedure Tprez_form.FormCreate(Sender: TObject);
begin
//ustawienia startowe wywoływane podczas tworzenia formy
end;
procedure Tprez_form.FormActivate(Sender: TObject);
begin
// aktywacja formy
SetupPixelFormat(); //wywołanie procedury ustawiającej format pikseli
GLInit; //wywołanie procedury inicjalizującej Open GL
end;
procedure Tprez_form.FormPaint(Sender: TObject);
begin
draw(); // wywołanie procedury rysującej
end;
procedure Tprez_form.GLInit();
begin
//inicjalizacja maszyny stanu Open GL
//a w szczególności określenie macierzy
//rzutowania ? glMatrixModel, itp.
end;
procedure Tprez_form.setupPixelFormat(DC:HDC);
const
//określenie stałych
var
//określenie zmiennych
begin
//ustawienie formatu pikseli
end;
procedure Tprez_form.Draw();
begin
//procedura rysująca
end;
procedure Tprez_form.Timer1Timer(Sender: TObject);
begin
//obsługa zegara
Draw(); //wywołanie procedury rysującej
end;
Krok trzeci - uzupełnienie struktury programu funkcjami.
Przed uzupełnieniem struktury programu funkcjami, nadmienić trzeba, iż w sekcji
uses dodane zostały odwołania do bibliotek OpenGL, tj.: Gl, Glu, OpenGL. Dodano również
plik textures.pas w celu ładowania tekstur z plików jpg. Zamiast pliku textures.pas
dołączyć można bmp.pas co umożliwi ładowanie tekstur z plików bmp. Nie jest to jednak
zasadne ze względu na wielkości bitmap.
Ustawienia startowe wywoływane podczas tworzenia formy (FormCreate)
W momencie gdy formularz jest tworzony (czy to w sposób automatyczny, czy też ręczny) uaktywnianych zostaje kilka zdarzeń w skład których wchodzą:
- zdarzenie OnCreate - informujące o tworzeniu formy,
- zdarzenie OnShow - informujący o wyświetleniu formy na ekranie,
- zdarzenie OnActivate - które sygnalizuje, że dany formularz został właśnie wybrany spośród pozostałych formularzy ujętych w programie,
- zdarzenia OnResize oraz OnPaint - które aktywowane są przy zmianie, jak i na początku tworzenia formularza.
W procedurze FormCreate ująć należy funkcje uruchamiane podczas tworzenia formy. W skład podstawowych funkcji zaliczyć można:
- podstawienie wartości do zmiennych statycznych (np.: ścieżka programu/),
- ustawienie wyglądu formy (wielkość, pozycja, ikony systemowe w pasku, nazwa formy),
- ustawienia dla timera ? interwał,
Przykładowa postać procedury FormCreate obsługująca zdarzenie OnCreate:
procedure Tprez_form.FormCreate(Sender: TObject);
begin
sc_programu:=ExtractFilePath(ParamStr(0));
//obsługa ikon systemowych w pasku tytułowym
prez_form.BorderIcons := [biSystemMenu];
//pozostawienie ikony[x] zamykającej program
//pusta wartość [] powoduje usunięcie wszystkich
//ikon z paska tytułowego formy
//ustawienie na false wartości prez_form.Scaled umożliwia
//zmianę jej wielkości w kodzie źródłowym - funkcja którą
//trzeba zmienić od wersji Delphi wyższej niż 7
prez_form.Scaled := false;
//ustawienia wielkości formy
prez_form.Width := Screen.Width-100;
prez_form.Height := Screen.Height-100;
//ustawienia pozycji formy na ekranie
prez_form.Top := 0;
prez_form.Left := 0;
//zmiana interwału dla komponentu Timer
Timer1.Interval := 100;
end;
W celu ujednolicenia i braku migania formy podczas jej ładowania wskazane jest powtórzyć
ustawienia dla formy obejmujące jej pozycję na ekranie w procedurze FormActivate, tj.:
prez_form.Scaled := false;
prez_form.Width := Screen.Width-100;
prez_form.Height := Screen.Height-100;
prez_form.Top := 0;
prez_form.Left := 0;
Ustawienie prez_form.Scaled := false dotyczy Delphi wyższych niż wersja 7. Od wersji 7 w dół ustawienie to nie jest wymagane.
Aktywacja formy (FormActivate).
Procedura aktywacji formy FormActivate (obsługi zdarzenia OnActivate) oprócz wspomnianych powyżej ustawień pozycji formy na ekranie musi zawierać odwołania do procedur SetupPixelFormat oraz GLInit. Odpowiedzialne są one w szczególności za:
- procedura SetupPixelFormat - ustawienie formatu pikseli,
- GLInit - inicjalizację maszyny stanów OpenGL.
Dodatkowo procedura aktywacji formy zawierać musi kontekst tworzenia grafiki oraz kontekst urządzenia, które ujęte są:
var
DC:HDC;
RC:HGLRC;
begin
DC:=GetDC(Handle);
RC:=wglCreateContext(DC);
wglMakeCurrent(DC, RC);
end;
Cała, minimalna, postać procedury aktywacji formy (FormActivate) wyglądać powinna jak na poniższym przykładzie:
procedure Tprez_form.FormActivate(Sender: TObject);
var
DC:HDC;
RC:HGLRC;
begin
prez_form.Scaled := false;
//ustawienia wielkości formy
prez_form.Width := Screen.Width-100;
prez_form.Height := Screen.Height-100;
//ustawienia pozycji formy na ekranie
prez_form.Top := 0;
prez_form.Left := 0;
DC:=GetDC(Handle);
SetupPixelFormat(DC); //wywołanie procedury odpowiedzialnej
//ustawienie formatu pikseli
RC:=wglCreateContext(DC);
wglMakeCurrent(DC, RC);
GLInit; //wywołanie procedury inicjalizującej
//maszynę stanów OpenGL
end;
Obsługa malowania na formularzu (FormPaint).
Po pierwszym uruchomieniu programu niezbędne jest obsłużenie procedury malowanie na formularzu: FormPaint (obsługującej zdarzenie OnPaint). Związane jest to z przyczyną zastosowanej przez Windows domyślnej procedury odświeżania okien. Po uruchomieniu programu należy wymusić na systemie Windows operację malowania utrwalając efekt rysowania grafiki trójwymiarowej na formie.
Procedura malowania po uruchomieniu programu zawierać powinna tylko wywołanie głównej procedury rysowania grafiki trójwymiarowej, tj.: w naszym przypadku procedury Draw. Przedstawia to poniższy przykład:
procedure Tprez_form.FormPaint();
begin
Draw();
end;
Obsługa zdarzenia OnTimer komponentu Timer (Timer1Timer).
W procedurze obsługi zdarzenia OnTimer komponentu Timer należy zastosować funkcję związane z ruchem, np.: obracającego się sześcianu który mógłby być uzupełniony wypełnionymi teksturami. Należy również umieścić w niej wywołanie głównej procedury
tworzącej grafikę trójwymiarową, tj.: w naszym przypadku Draw(). Konstrukcja procedury wyglądać może:
procedure Tprez_form.Timer1Timer(Sender: TObject);
begin
{
funkcje obliczające nowe wartości zmiennych
związane z ruchem czyli funkcjami wykorzystywanymi
w procedurze Draw do przeprowadzenia obrotów, przesunięć
jak np.:
- glTranslatef(0.0,0.0, przesunięcie) - funkcja związana z przesunięciem
dla której można obliczać w procedurze Timer1Timer wartość zmiennej
przesuniecie
- glRotatef(obr_poziom, 1, 0, 0) - funkcja związana z obrotem dla której można
obliczać w procedurze Timer1Timer wartości zmiennej obr_poziom
oraz innymi, np.: zmianą ustawienia oświetlenia, czasową zmianą intensywności mgły,
czasem życia cząstek wykorzystywanych podczas opadu deszczu, śniegu, wybuchem itp.
}
//ponowne wywołanie głównej procedury tworzącej grafikę trójwymiarową
//częstotliwość wywołań uzależniona jest od ustawionego interwału komponentu Timer1,
//którego wartość została statycznie przypisana w procedurze FormCreate
Draw();
end;
Ustawienie formatu pikseli (setupPixelFormat).
Format pikseli trzeba zawsze określić przed stworzeniem kontekstu tworzenia grafiki. Określić można na przykład tryb koloru, bufor głębi, liczbę bitów opisujących piksel czy sposób buforowania zawartości okna. Ustawienie formatu pikseli stanowi kolejne rozszerzenie interfejsu programowego Windows.
W stałych należy określić strukturę TPIXELFORMATDESCRIPTOR:
const
pfd:TPIXELFORMATDESCRIPTOR = (
//rozmiar struktury
nSize:sizeof(TPIXELFORMATDESCRIPTOR);
//zawsze wartość 1
nVersion:1;
//znaczniki właściwości bufora pikseli
dwFlags:PFD_SUPPORT_OPENGL
or PFD_DRAW_TO_WINDOW
or PFD_DOUBLEBUFFER;
//typ danych piksela
iPixelType:PFD_TYPE_RGBA;
//liczba bitów piksela
cColorBits:24;
//liczba bitów koloru czerwonego oraz
//przesunięcie bitów koloru czerwonego
cRedBits:0; cRedShift:0;
//liczba bitów koloru zielonego oraz
//przesunięcie bitów koloru zielonego
cGreenBits:0; cGreenShift:0;
//liczba bitów koloru niebieskiego oraz
//przesunięcie bitów koloru niebieskiego
cBlueBits:0; cBlueShift:0;
//liczba bitów alfa oraz
//przesunięcie bitów alfa
cAlphaBits:0; cAlphaShift:0;
//liczba bitów bufora akumulacji
cAccumBits: 0;
//liczba bitów akumulacji czerni
cAccumRedBits: 0;
//liczba bitów akumulacji zieleni
cAccumGreenBits: 0;
//liczba bitów akumulacji błękitu
cAccumBlueBits: 0;
//liczba bitów akumulacji alfa
cAccumAlphaBits: 0;
//liczba bitów bufora głębi
cDepthBits:16;
//liczba bitów bufora powielania
cStencilBits:0;
//liczba buforów pomocniczych
cAuxBuffers:0;
//nie jest już wykorzystywany
iLayerType:PFD_MAIN_PLANE;
//liczba podkładanych i nakładanych jednostek
bReserved: 0;
//nie jest już wykorzystywany
dwLayerMask: 0;
//indeks podłożonej płaszczyzny
dwVisibleMask: 0;
//nie jest już wykorzystywany
dwDamageMask: 0;
);
gdzie:
- nsize: opisuje rozmiar struktury przekazywanych za pomocą wskaźnika, służy do określenia wielkości pamięci zajmowanej przez strukturę. Umieszczenie w pierwszym polu umożliwia szybkie pobranie przez deferencję wskaźnika.
- Dwflags: określa właściwości bufora pikseli (PFD_DRAW_TO_WINDOW ? umożliwia tworzenie grafiki w oknie, PFD_SUPPORT_OPENGL ? umożliwia tworzenie grafiki opengl, PFD_DOUBLEBUFFER ? podwójne buforowanie /wyklucza się ze znacznikiem PFD_SUPPORT_GDI/, PFD_SWAP_LAYER_BUFFERS ? urządzenie może przełączać pojedyncze plany warstw z formatami pikseli o podwójnym buforowaniu /w przeciwnym razie wszystkie plany warstw przełączane są grupowo/; jeśli znacznik jest ustawiony, to dostępna jest funkcja wglSwapLayerBuffers, PFD_DEPTH_DONTCARE ? dla danego formatu pikseli może być wykorzystywany bufor głębi, PFD_DOUBLEBUFFER_DONTCARE ? piksele mogą być buforowane pojedynczo lub podwójnie),
- IPixelType: określa typ danych piksela (PFD_TYPE_RGBA ? piksele opisane w standardzie RGBA czyli dla każdego piksela określony jest kolor czerwony, zielony, niebieski oraz współczynnik alfa, PFD_TYPE_COLORINDEX ? piksele opisane są za pomocą wartości indeksu koloru),
- CcolorBits: opisuje liczbę bitów określających kolor piksela i może przyjmować wartości 8, 16, 24 i 32. W przypadku gdy karta grafiki nie umożliwia reprezantacji koloru pikseli za pomocą danej liczby bitów to przyjmowana jest jej największa możliwa wartość.
W zmiennych należy określić:
pixelFormat : integer
a w samej procedurze przyporządkować wartość całkowitą indeksu dostępnego formatu pikseli:
pixelFormat := ChoosePixelFormat(DC, @pfd)
oraz sprawdzić wartość przypisania:
if (pixelFormat = 0) then
exit;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then
exit;
Cała procedura setupPixelFormat(DC:HDC) wygląda następująco:
procedure Tprez_form.setupPixelFormat(DC:HDC);
const
pfd:TPIXELFORMATDESCRIPTOR = (
nSize:sizeof(TPIXELFORMATDESCRIPTOR);
nVersion:1;
dwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or
PFD_DOUBLEBUFFER;
iPixelType:PFD_TYPE_RGBA;
cColorBits:24;
cRedBits:0; cRedShift:0;
cGreenBits:0; cGreenShift:0;
cBlueBits:0; cBlueShift:0;
cAlphaBits:0; cAlphaShift:0;
cAccumBits: 0;
cAccumRedBits: 0;
cAccumGreenBits: 0;
cAccumBlueBits: 0;
cAccumAlphaBits: 0;
cDepthBits:16;
cStencilBits:0;
cAuxBuffers:0;
iLayerType:PFD_MAIN_PLANE;
bReserved: 0;
dwLayerMask: 0;
dwVisibleMask: 0;
dwDamageMask: 0;
);
var
pixelFormat:integer;
begin
pixelFormat := ChoosePixelFormat(DC, @pfd);
if (pixelFormat = 0) then
exit;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then
exit;
end;
Inicjalizacja maszyny stanu Open GL (GLInit).
Procedura inicjalizacyjna maszyny stanu Open GL odpowiada za ustawienie sposobu wykonywania przekształceń, parametry rzutowania. Mogą być w niej zainicjowane również funkcje ukrywania powierzchni, ukrywania ścian niewidocznych (co w przypadku dużej ilości tworzonych elementów ma za zadanie przyśpieszenie działania skompilowanego programu). Przykładowy schemat procedury inicjalizującej maszynę stanu Open GL przedstawia poniższy przykład:
procedure Tprez_form.GLInit();
begin
//ustawienie sposobu wykonywania przekształceń w Open GL
glMatrixMode(GL_PROJECTION); //macierz rzutowania
//parametry rzutowania perspektywicznego
glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);
//ponowne ustawienie sposobu wykonywania przekształceń w Open GL
glMatrixMode(GL_MODELVIEW); //macierz modelowania
//usunięcie ukrytych powierzchni
glEnable(GL_DEPTH_TEST);
//ustawienie kierunku tworzenia wierzchołków wielokątów
glFrontFace(GL_CCW);
//ukrywanie tylnych ścian
glCullFace(GL_BACK);
end;
GDZIE:
Macierz modelowania
Macierz modelowania glMatrixMode definiuje układ współrzędnych wykorzystywanych podczas tworzenia obiektów i związana jest z otrzymywanym rezultatem przekształceń wierzchołków. Informuje ona maszynę OpenGL, że będzie modelowana macierz modelowania (GL_MODELVIEW), macierz rzutowania (GL_PROJECTION) lub macierz tekstury (GL_TEXTURE).
Rzutowanie perspektywistyczne
Rzutowanie perspektywiczne glFrustrum pozwala uzyskać bardziej realistyczny obraz trójwymiarowego świata. W efekcie rysowania perspektywicznego obiekty znajdujące się dalej od obserwatora są rysowane na ekranie jako mniejsze. Rzutowanie perspektywistyczne polega na zastosowaniu bryły w kształcie ściętego ostrosłupa skierowanego mniejszą podstawą w stronę obserwatora. Open Gl, podczas rzutowania, przekształca ostrosłup w prostopadłościan i w efekcie obiekty znajdujące się dalej są bardziej pomniejszane od tych znajdujących się bliżej. Wielkość pomniejszenia realizuje się poprzez zmianę rozmiarów obu podstaw ostrosłupa. W przypadku gdy obie podstawy ostrosłupa są tych samych wymiarów uzyskane zostanie rzutowanie ortograficzne /zwane też równoległym/.
GlFrustrum(Gldouble left, Gldouble right, Gldouble bottom, Gldouble top, Gldouble near, Gldouble far)
gdzie:
- left, right, bottom, top ? wyznaczają płaszczyznę obcięcia zawierającą mniejszą podstawę ostrosłupa,
- near, far ? określają odległość do mniejszej i większej podstawy ostrosłupa,
- wierzchołki większej podstawy wyznacza się jako punkty przecięcia linii przechodzących przez punkt obserwatora i wierzchołki mniejszej podstawy z dalszą płaszczyzną obcięcia.
Im bliżej więc znajdować się będzie obserwator względem mniejszej z podstaw ostrosłupa, tym większa będzie druga podstawa oraz większe wrażenie perspektywy.
Rzutowanie ortograficzne
Rzutowanie ortograficzne wykorzystywane jest w grach opartych na grafice dwuwymiarowej, w komputerowym wspomaganiu projektowania. Rzutowanie ortograficzne nie tworzy tak realistycznego obrazu świata jak rzutowanie perspektywistyczne.
Usuwanie ukrytych powierzchni
Usunięcie ukrytych powierzchni GL_DEPTH_TEST - polecenie glEnable(GL_DEPTH_TEST) aktywuje usuwanie ukrytych powierzchni.
Kierunek tworzenia wierzchołków
Ustawienie kierunku tworzenia wierzchołków wielokątów glFrontFace ? kierunek tworzenia wierzchołków wielokątów może być określony w kierunku przeciwnym do obrotu wskazówek zegara (GL_CCW) lub zgodnym (GL_CW).
Ukrywanie ścian
Ukrywanie ścian glCullFace ? określa która ze stron ścian ma być rysowana. Może przyjmować wartości:
- GL_FRONT (przednie ściany),
- GL_BACK (tylne ściany),
- GL_FRONT_AND_BACK (przednie i tylne ściany ? nie ma zwykle sensu).
Procedura rysująca - tworzącą grafikę trójwymiarową (Draw).
Wywołanie procedury Draw należy umieszczać zawsze w procedurach obsługujących ruch myszą oraz w procedurach obsługujących ruch do przodu, do tyłu, w bok (klawisze kursorów). Można umieścić również jej wywołanie w procedurze Timer1Timer(Sender: TObject) dla elementów ruchomych np.: obracający się krąg na danej powierzchni
czy poruszający się inny przedmiot w przestrzeni X,Y,Z.
W procedurze Draw() umieszczane są najczęściej poniższe funkcje:
procedure Tprez_form.Draw();
begin
//zeruje bufor ekranu I bufor głębi
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
//resetuje macierz widoku modelu
glLoadIdentity();
//przesunięcie o wartość jednostek zmiennej ?przesunięcie? wzdłuż osi Z
//(znak ?-? powoduje przesunięcie wstecz), dla osi X i Y wartości są 0.0
//czyli brak przesunięcia
//dla obcnej sceny przesunięcie wynosi ?10.0 czyli oddalenie od miejsca obserwatora
glTranslatef(0.0,0.0, przesunięcie);
//obrót dookoła osi X o wartość obr_poziom, dla osi Y i Z
//wprowadzona jest wartość 0 czyli brak obrotu
glRotatef(obr_poziom, 1, 0, 0);
//obrót dookoła osi Y o wartość obr_pion, dla osi X i Z
//wprowadzona jest wartość 0 czyli brak obrotu
//obroty należy wykonywać dla każdej płaszczyzny osobno
glRotatef(obr_pion, 0, 1, 0);
//włączenie, poinformowanie OpenGL o braku rysowania płaszczyzn
//zainicjowanych funkcją glCullFace() ? brak obliczeń dla niewidocznych stron
glEnable(GL_CULL_FACE);
//włączenie tekstur dwuwymiarowych
glEnable(GL_TEXTURE_2D);
//włączenie tworzenia tekstury
glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, jakaś_tekstura);
//koniec tworzenia tekstury
//narysowanie kwadratu wypełnionego teksturą ?jakaś_tekstura?
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z);
glTexCoord2f(1.0, 0.0); glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z);
glTexCoord2f(1.0, 1.0); glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z);
glTexCoord2f(0.0, 1.0); glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z);
glEnd();
//przełączenie buforów
SwapBuffers(wglGetCurrentDC);
end;
Gdzie:
Funkcja SwapBuffers
Funkcja SwapBuffers ze zmienną jako parametr przechowującą kontekst urządzenia powoduje przełączenie buforów, zapewniając płynność animacji. Rysowana animacja w procedurze Draw() umieszczana jest w niewidocznym dla obserwatora buforze i następnie po jej narysowaniu za pomocą funkcji SwapBuffers jest przedstawiana obserwatorowi.
Funkcja glFlush
Funkcja glFlush() opróżnia natomiast bufor.
Funkcja glClear
Funkcja glClear zeruje bufory kolorów i głębi i w efekcie wypełnia okno kolorem czarnym.
Funkcja glLoadIdentify
Funkcja glLoadIdentity powoduje wyczyszczenie bieżącej macierzy przekształceń w celu umożliwienia wykonania kolejnych przekształceń widoku.
Funkcja glTranslatef
Funkcja glTranslatef reprezentuje przekształcenia modelowania, podobnie jak funkcja glRotatef. Należy ją stosować w przypadku braku wykorzystania /jak w naszym przykładzie/ funkcji gluLookAt. Funkcja glTranslate wykonuje przesunięcie o daną wartość X,Y,Z ? w naszym przykładzie przesunięcie następuje wzdłuż osi Z.
Funkcja glRotatef
Funkcja glRotatef podobnie jak funkcja glTranslatef reprezentuje przekształcenia modelowania o daną wartość. Jak sama nazwa wskazuje powoduje ona obrót wokół danej osi współrzędnych. Wybranie obrotu dla osi reprezentuje wartość 1 wprowadzona w odpowiednim miejscu, na przykład:
- glRotatef(obr, 1, 0, 0) ? obrót o wartość obr wokół osi X,
- glRotatef(obr, 0, 1, 0) ? obrót o wartość obr wokół osi Y,
- glRotatef(obr, 0, 0, 1) ? obrót o wartość obr wokół osi Z.
Obroty dla każdej osi należy wykonywać osobno. Nie wolno łączyć obrotu wokół osi X i Y w jednej funkcji.
Funkcja glEnable
Funkcja glEnable włącza różne stany OpenGl, natomiast funkcja glDisable powoduje wyłączenie stanu maszyny OpenGl. Na przykład: glEnable(GL_FOG) powoduje włączenie mgły, natomiast glDisable(GL_FOG) jej wyłączenie.
Funkcja glBegin
Funkcja tworząca grafikę glBegin() ? funkcja ta przekazuje maszynie OpenGL informację o rozpoczęciu tworzenia grafiki oraz o typie wykorzystywanych elementów. Posiada ona postać: GlBegin(Glenum mode)
Typ wykorzystywanych elementów określony jest za pomocą parametru mode i przyjmować on może wartości:
- GL_POINTS - pojedyncze punkty,
- GL_LINES - odcinki,
- GL_LINE_STRIP - sekwencja połączonych odcinków,
- GL_LINE_LOOP - zamknięta sekwencja połączonych odcinków,
- GL_TRIANGLES - pojedyncze trójkąty,
- GL_TRIANGLE_STRIP - sekwencja połączonych trójkątów,
- GL_TRIANGLE_FAN - sekwencja trójkątów posiadających jeden wspólny wierzchołek,
- GL_QUADS - czworokąty,
- GL_QUAD_STRIP - sekwencja połączonych trójkątów,
- GL_POLYGON - wielokąty o dowolnej liczbie wierzchołków.
Każde wywołanie funkcji glBegin(musi być zakończone wywołaniem funkcji glEnd().
Funkcja glEnd
Funkcja glEnd() powiadamia maszynę OpenGl o zakończeniu tworzenia grafiki z wykorzystaniem elementów określonych jako parametr funkcji glBegin. Funkcja glEnd nie posiada parametrów. Należy ponadto pamiętać, iż nie wolno zagnieżdżać funkcji glBegin oraz glEnd, czyli nie wolno wywoływać ich po raz kolejny w bloku funkcji glBegin i glEnd.
Wewnątrz funkcji glBegin oraz glEnd można tylko wywoływać funkcje:
- glVertex,
- glColor,
- glIndex,
- glNormal,
- glTexCoord,
- glEvalCoord,
- glEvalPoint,
- glMaterial,
- glEdgeFlag,
- glCallList,
- glCallLists.
Funkcja glVertex
Funkcja glVertex() wywoływana jest w celu określenia punktu w przestrzeni. Poniżej przedstawiam schemat wywołania funkcji: GlVertex[2,3,4][d,f,i,s][wektor]
gdzie:
- cyfra to liczba wymiarów,
- litera określa typ danych: double, float, int lub short,
- wektor oznacza tablicę za pomocą której zostaną przekazane parametry funkcji.
W kodzie można użyć: glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z). Tablica Vertex zaimplementowana musi zostać w sekcji var formy jako TCoord. Typ TCoord jest natomiast rekordem zawierającym zmienne X,Y,Z typu glFloat. Całość implementacji zmiennych tablicy i rekordu wyglądać może następująco:
type
Tprez_form = class(TForm)
? procedury ?
private
{ Private declarations }
? zmienne ? procedury
public
{ Public declarations }
end;
type
//rekord
TCoord = Record
X, Y, Z : glFLoat;
end;
var
prez_form: Tprez_form;
//tablica
Vertex : Array of TCoord;
W kodzie źródłowym w procedurze Draw można narysować GL_QUADS np.: w sposób jak poniżej stosując pętlę while. Parametr ?i? jest zmienną przetrzymującą liczby typu integer.
while i < 16 do
begin
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z);
i := i + 1;
glTexCoord2f(1.0, 0.0); glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z);
i := i + 1;
glTexCoord2f(1.0, 1.0); glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z);
i := i + 1;
glTexCoord2f(0.0, 1.0); glVertex3f(Vertex[i].X, Vertex[i].Y, Vertex[i].Z);
i := i + 1;
glEnd();
end;
Powyższy przykład narysuje cztery ?kwadraty? w zależności od danych przetrzymywanych
w tablicy Vertex.
Inny przykład użycia funkcji glVertex:
glBegin(GL_POINTS)
glVertex(0.0, 0.0, 0.0);
glEnd();
Pierwszy wiersz powyższego przykładu informuje maszynę OpenGL, iż będą rysowane punkty. Kolejny wiersz rysuje punkty a ostatni informuje maszynę OpenGL o zakończeniu rysowania.
Można również wewnątrz bloku wywołań funkcji glBegin() oraz glEnd wywołać funkcję glVertex() dowolną liczbę razy co przyśpiesza działanie programu, np.:
glBegin(GL_POINTS)
glVertex(0.0, 0.0, 0.0);
glVertex(0.0, 1.0, 0.0);
glEnd(;)
Poinformowanie maszyny OpenGL o rysowaniu odcinków oznacza natomiast, iż punkty mają być interpretowane jako końce odcinków. Pokazuje to poniższy przykład:
glBegin(GL_LINES)
glVertex(-1.0, -1.0, 0.0);
glVertex(2.0, 1.0, 0.0);
glEnd();
Wewnątrz bloku wywołań funkcji glBegin oraz glEnd można rysować wiele odcinków. Każde kolejne dwa punkty traktowane będą jako końce nowego odcinka.
Funkcja glTexCoord2f
Funkcja glTexCoord2f() określa współrzędne tekstury. Tworzenie sceny wymaga określenia współrzędnych tekstury dla wszystkich wierzchołków. Współrzędne tekstury pozwalają ustalić położenie tekseli na rysowanym objekcie. Współrzędne (0, 0) oznaczają lewy dolny narożnik tekstury. Współrzędne (1, 1) prawy górny narożnik. Podczas rysowania wielokąta trzeba określić współrzędne tekstury dla każdego z jego wierzchołków. W przypadku tekstur dwuwymiarowych mają one postać (s, t), gdzie s i t przyjmują wartości z przedziału od 0 do 1.
(0.0, 1.0) (1.0, 1.0)
|------------------------------------|
| |
| |
| |
| |
| |
| |
| |
|------------------------------------|
(0.0, 0.0) (1.0, 0.0)
Włączenie tworzenia tekstur:
Funkcja glTesGenf stosowana jest przy odwzorowaniu otoczenia /np.: w zastosowaniu czcionki konturowej pokrytej teksturą/. Pierwszy parametr informuje maszynę OpenGl o tym, która ze współrzędnych tekstur będzie generowana automatycznie /może on przyjmować wartość GL_S lub GL_T w odniesieniu do tekstur dwuwymiarowych/.Drugi parametr określa tryb odwzorowania tekstury używany podczas automatycznej generacji współrzędnej i może przyjmować wartości: GL_EYE_LINEAR ? tekstura umieszczana jest w stałym miejscu ekranu, obiekty pokryte są teksturą, GL_OBJECT_LINEAR ? tekstura umieszczana jest na obiekcie, GL_SPHERE_MAP ? tekstura reprezentuje odwzorowanie otoczenia (mapę sferyczną).
Funkcja glTexParamateri
Funkcja glTexParameteri ? okresla sposób filtrowania tekstury. A oto jej struktura:
GlTexParameteri(Glenum target, Glenum pname, Glint param)
gdzie:
- target reprezentuje rodzaj używanych tekstur, może przyjmować wartości: GL_TEXTURE_1D, GL_TEXTURE_2D lub GL_TEXTURE3D,
- pname pozwala określić, czy definiuje się obsługę powiększania czy pomniejszania
i może przyjmować odpowiednio GL_TEXTURE_MAG_FILTER lub GL_TEXTURE_MIN_FILTER, - param może przyjmować wartości: GL_NEAREST ? używa teksela położonego najbliżej środka rysowanego piksela, GL_LINEAR ? stosuje liniową interpolację (średnio ważoną) czterech tekseli położonych najbliżej środka rysowanego piksela, GL_NEAREST_MIPMAP_NEAREST ? używa obrazu o najbardziej zbliżonej rozdzielczości do rozdzielczości wielokąta oraz filtr GL_NEAREST, GL_NEAREST_MIPMAP_LINEAR ? używa obrazu o najbardziej zbliżonej rozdzielczości do rozdzielczości wielokąta oraz filtr GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST ? stosuje liniową interpolację dwóch mipmap o najbardziej zbliżonej rozdzielczości wielokąta oraz używa filtr GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR ? stosuje liniową interpolację dwóch mipmap o najbliższej zbliżonej rozdzielczości do rozdzielczości wielokąta oraz używa filtra GL_LINEAR.
Filtry wykorzystujące mipmapy można stosować jedynie dla parametru GL_TEXTURE_MIN_FILTER.
Funkcja glBindTexture
Funkcja glBindTexture wiąże obiekt tekstury z bieżącą teksturą. Aby skorzystać w programie z wielu różnych tekstur należy przy zmianie tekstury za każdym razem wywoływać powyższą funkcję glBindTexture.
Procedura wywoływana podczas zmiany wielkości okna (FormResize).
Po zmianie wielkości okna niezbędne jest ponowne wywołanie procedur SetupPixelFormat oraz GLInit. Należy również wywołać funkcje kontekstu tworzenia grafiki oraz kontekstu urządzenia. Przykładowy schemat procedury zawarty jest poniżej:
procedure Tprez_form.FormResize(Sender: TObject);
var
//dla Open GL
DC:HDC;
RC:HGLRC;
begin
// dla Open GL
DC:=GetDC(Handle);
SetupPixelFormat(DC);
RC:=wglCreateContext(DC);
wglMakeCurrent(DC, RC);
GLInit;
end;
Informacje końcowe.
Wywołanie funkcji wczytujących wszelkiego grafiki do zmiennych można wykonać w dwojaki sposób.
zakładając, iż wielkość formy programu nie będzie ulegać zmianom można tekstury załadować w procedurze FormActivate po wywołaniu procedur SetupPixelFormat, GLInit oraz po wywołaniu funkcji kontekstu tworzenia grafiki, jak i kontekstu urządzenia. Przedstawia to poniższy przykład:
procedure Tprez_form.FormActivate(Sender: TObject);
var
DC:HDC;
RC:HGLRC;
begin
DC:=GetDC(Handle);
SetupPixelFormat(DC);
RC:=wglCreateContext(DC);
wglMakeCurrent(DC, RC);
GLInit;
//zmienna musi być zainicjowana w sekcji private jako string
//zmienna := 'plik.jpg';
//jeżeli plik podstawiony do zmiennej istnieje ...
if fileexists(sc_programu + 'image\' + zmienna_jpg) then
begin
//załadowanie tekstury do zmiennej zmienna_text zainicjowanej
//w sekcji private jako glUnit
LoadTexture(sc_programu + 'image\' + zmienna_jpg, zmienna_text, false);
end;
end;
przy założeniu swobodnych zmian wielkości formy należy tekstury ładować w umieszczając funkcje na końcu procedury GLInit, jak na poniższym przykładzie:
procedure Tprez_form.GLInit();
begin
glMatrixMode(GL_PROJECTION);
glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
//zmienna musi być zainicjowana w sekcji private jako string
//zmienna := 'plik.jpg';
//jeżeli plik podstawiony do zmiennej istnieje ...
if fileexists(sc_programu + 'image\' + zmienna_jpg) then
begin
//załadowanie tekstury do zmiennej zmienna_text zainicjowanej
//w sekcji private jako glUnit
LoadTexture(sc_programu + 'image\' + zmienna_jpg, zmienna_text, false);
end;
end;
Gotowy przykład implementacji maszyny stanu OpenGL w Delphi.
Kostka 3D
Przykład poniżej przedstawia obracający się sześcian pokryty różnymi teksturami. Umieszczenie procedury FormResize pozwala na swobodne zmiany wielkości formy. Dwa przyciski button pozwalają zmienić interwał Timera co powoduje zmianę szybkości przeprowadzanej animacji.
// -------------------------------------------------------------
// Autor: rk7771
// -= wrzesień 2005 =-
// Opis:
// Obracający się sześcian pokryty teksturą
// -------------------------------------------------------------
unit projekt_1_unit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Gl, Glu, OpenGL, textures, ExtCtrls, Buttons, StdCtrls;
type
Tprojekt_1_form = class(TForm)
Timer1: TTimer;
Button1: TButton;
Button2: TButton;
procedure FormResize(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure GLInit();
procedure setupPixelFormat(DC:HDC);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
wielk : double;
obrot_poziom : integer;
obrot_pion : integer;
sc_programu : string;
jpg1 : string;
jpg2 : string;
jpg3 : string;
jpg4 : string;
jpg5 : string;
jpg6 : string;
FrontTex : glUint;
BackTex : glUint;
TopTex : glUint;
BottomTex : glUint;
LeftTex : glUint;
RightTex : glUint;
procedure Draw();
public
{ Public declarations }
end;
var
projekt_1_form: Tprojekt_1_form;
implementation
{$R *.dfm}
procedure Tprojekt_1_form.FormActivate(Sender: TObject);
var
//dla Open GL
DC:HDC;
RC:HGLRC;
begin
projekt_1_form.Top := 25;
projekt_1_form.Left := 25;
sc_programu:=ExtractFilePath(ParamStr(0));
Canvas.Font.Color := clRed;
Canvas.Font.Size := 12;
Canvas.TextOut(30, 130, 'Ładowanie grafiki, proszę czekać ...');
Application.ProcessMessages;
jpg1 := '1.jpg';
jpg2 := '2.jpg';
jpg3 := '3.jpg';
jpg4 := '4.jpg';
jpg5 := '5.jpg';
jpg6 := '6.jpg';
// dla Open GL
DC:=GetDC(Handle);
SetupPixelFormat(DC);
RC:=wglCreateContext(DC);
wglMakeCurrent(DC, RC);
GLInit;
wielk := -6.0;
end;
procedure Tprojekt_1_form.FormPaint(Sender: TObject);
begin
Draw();
end;
procedure Tprojekt_1_form.GLInit();
begin
glMatrixMode(GL_PROJECTION);
glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
//kontrola isnienia plików jpg
if fileexists(sc_programu + 'image\' + jpg1) and fileexists(sc_programu + 'image\' + jpg2)
and fileexists(sc_programu + 'image\' + jpg3) and fileexists(sc_programu + 'image\' + jpg4)
and fileexists(sc_programu + 'image\' + jpg5) and fileexists(sc_programu + 'image\' + jpg6) then
begin
//wczytanie plików jpg do zmiennych
LoadTexture(sc_programu + 'image\' + jpg1, FrontTex, false);
LoadTexture(sc_programu + 'image\' + jpg2, BackTex, false);
LoadTexture(sc_programu + 'image\' + jpg3, TopTex, false);
LoadTexture(sc_programu + 'image\' + jpg4, BottomTex, false);
LoadTexture(sc_programu + 'image\' + jpg5, LeftTex, false);
LoadTexture(sc_programu + 'image\' + jpg6, RightTex, false);
end;
end;
procedure Tprojekt_1_form.setupPixelFormat(DC:HDC);
const
pfd:TPIXELFORMATDESCRIPTOR = (
nSize:sizeof(TPIXELFORMATDESCRIPTOR);
nVersion:1;
dwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or
PFD_DOUBLEBUFFER;
iPixelType:PFD_TYPE_RGBA;
cColorBits:24;
cRedBits:0; cRedShift:0;
cGreenBits:0; cGreenShift:0;
cBlueBits:0; cBlueShift:0;
cAlphaBits:0; cAlphaShift:0;
cAccumBits: 0;
cAccumRedBits: 0;
cAccumGreenBits: 0;
cAccumBlueBits: 0;
cAccumAlphaBits: 0;
cDepthBits:16;
cStencilBits:0;
cAuxBuffers:0;
iLayerType:PFD_MAIN_PLANE;
bReserved: 0;
dwLayerMask: 0;
dwVisibleMask: 0;
dwDamageMask: 0;
);
var pixelFormat:integer;
begin
pixelFormat := ChoosePixelFormat(DC, @pfd);
if (pixelFormat = 0) then
exit;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then
exit;
end;
procedure Tprojekt_1_form.Draw();
begin
sc_programu:=ExtractFilePath(ParamStr(0));
//jpg - kontrola istnienia plików
if fileexists(sc_programu + 'image\' + jpg1) and fileexists(sc_programu + 'image\' + jpg2)
and fileexists(sc_programu + 'image\' + jpg3) and fileexists(sc_programu + 'image\' + jpg4)
and fileexists(sc_programu + 'image\' + jpg5) and fileexists(sc_programu + 'image\' + jpg6) then
begin
Application.ProcessMessages;
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0,0.0, wielk);
glRotatef(obrot_poziom, 1, 0, 0);
glRotatef(obrot_pion, 0, 1, 0);
glEnable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, FrontTex);
glBegin(GL_QUADS);
// Front Face
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, 1.0);
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, 1.0);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 1.0);
glEnd();
glBindTexture(GL_TEXTURE_2D, BackTex);
glBegin(GL_QUADS);
// Back Face
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);
glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, 1.0, -1.0);
glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);
glEnd();
glBindTexture(GL_TEXTURE_2D, TopTex);
glBegin(GL_QUADS);
// Top Face
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, 1.0, 1.0);
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, 1.0, 1.0);
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, -1.0);
glEnd();
glBindTexture(GL_TEXTURE_2D, BottomTex);
glBegin(GL_QUADS);
// Bottom Face
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0);
glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, -1.0, -1.0);
glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, 1.0);
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);
glEnd();
glBindTexture(GL_TEXTURE_2D, RightTex);
glBegin(GL_QUADS);
// Right face
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, -1.0);
glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, 1.0, 1.0);
glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, 1.0);
glEnd();
glBindTexture(GL_TEXTURE_2D, LeftTex);
glBegin(GL_QUADS);
// Left Face
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, 1.0);
glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, -1.0);
glEnd();
SwapBuffers(wglGetCurrentDC);
Canvas.Brush.Style := bsClear; // ustaw tło na przeźroczyste
Canvas.Font.Color := clRed;
Canvas.Font.Size := 12;
Canvas.TextOut(10, 30, 'Intervał timera: ' + inttostr(Timer1.Interval));
end;
end;
procedure Tprojekt_1_form.Timer1Timer(Sender: TObject);
begin
obrot_poziom := obrot_poziom + 3;
obrot_pion := obrot_pion + 6;
Draw();
end;
procedure Tprojekt_1_form.FormCreate(Sender: TObject);
begin
projekt_1_form.BorderIcons := [biSystemMenu];
Timer1.Interval := 100;
end;
procedure Tprojekt_1_form.Button1Click(Sender: TObject);
begin
//spowolnienie animacji
Timer1.Interval := Timer1.Interval + 5;
end;
procedure Tprojekt_1_form.Button2Click(Sender: TObject);
begin
//przyśpieszenie animacji
Timer1.Interval := Timer1.Interval - 5;
end;
procedure Tprojekt_1_form.FormResize(Sender: TObject);
var
//dla Open GL
DC:HDC;
RC:HGLRC;
begin
// dla Open GL
DC:=GetDC(Handle);
SetupPixelFormat(DC);
RC:=wglCreateContext(DC);
wglMakeCurrent(DC, RC);
GLInit;
end;
end.
Zegar GL
// -------------------------------------------------------------
// Autor: rk7771
// -= marzec 2007 =-
// ZEGAR GL
// -------------------------------------------------------------
unit zegar_gl_unit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Gl, Glu, OpenGL, ExtCtrls, textures;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure kolor_tarczy();
private
{ Private declarations }
//dla Open GL
DC:HDC;
RC:HGLRC;
//tarcza zegara
tarcza_jpg : string;
tarcza_text : glUint;
//kolory tarczy zegara
R_color, G_color, B_color : glFLoat;
//parametr dla zmiany koloru
x_color : integer;
procedure GLInit();
procedure setupPixelFormat(DC:HDC);
public
{ Public declarations }
sc_programu : string;
procedure draw();
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
sc_programu:=ExtractFilePath(ParamStr(0));
form1.BorderIcons := [biSystemMenu];
form1.BorderStyle := bsSingle;
R_color := 1.0;
G_color := 1.0;
B_color := 1.0;
x_color := 0;
DC:=GetDC(Handle);
SetupPixelFormat(DC);
RC:=wglCreateContext(DC);
wglMakeCurrent(DC, RC);
glLoadIdentity;
GLInit;
//wczytanie obrazków
//TARCZA
tarcza_jpg := 'tarcza.jpg';
if fileexists(sc_programu + tarcza_jpg) then
begin
LoadTexture(sc_programu + tarcza_jpg, tarcza_text, false);
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
wglmakecurrent(0,0);
wgldeletecontext(DC);
releasedc(handle,DC);
end;
procedure TForm1.GLInit();
begin
// set viewing projection
glMatrixMode(GL_PROJECTION);
glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);
// position viewer
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
//kierunek przeciwny do kierunku
//ruchu wskazówek zegara przy tworzeniu
//wierzchołków wielokątów
glFrontFace(GL_CCW);
//ukrywanie tylnych ścian
glCullFace(GL_BACK);
end;
procedure TForm1.setupPixelFormat(DC:HDC);
const
pfd:TPIXELFORMATDESCRIPTOR = (
nSize:sizeof(TPIXELFORMATDESCRIPTOR);
nVersion:1;
dwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or
PFD_DOUBLEBUFFER;
iPixelType:PFD_TYPE_RGBA;
cColorBits:24;
cRedBits:0; cRedShift:0;
cGreenBits:0; cGreenShift:0;
cBlueBits:0; cBlueShift:0;
cAlphaBits:0; cAlphaShift:0;
cAccumBits: 0;
cAccumRedBits: 0;
cAccumGreenBits: 0;
cAccumBlueBits: 0;
cAccumAlphaBits: 0;
cDepthBits:16;
cStencilBits:0;
cAuxBuffers:0;
iLayerType:PFD_MAIN_PLANE;
bReserved: 0;
dwLayerMask: 0;
dwVisibleMask: 0;
dwDamageMask: 0;
);
var pixelFormat:integer;
begin
pixelFormat := ChoosePixelFormat(DC, @pfd);
if (pixelFormat = 0) then
exit;
if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) then
exit;
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
draw();
end;
procedure TForm1.draw();
var
SysTime : SystemTime;
begin
if getasynckeystate(VK_ESCAPE) <> 0 then
begin
close;
end;
if getasynckeystate(VK_F2) <> 0 then
begin
kolor_tarczy();
end;
GetLocalTime(SysTime);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glPushMatrix();
glTranslatef(0, 0, -3.0);
glColor3f(R_color, G_color, B_color);
glBindTexture(GL_TEXTURE_2D, tarcza_text);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 1.0); glVertex3f(-1, 1, 0);
glTexCoord2f(0.0, 0.0); glVertex3f(-1, -1, 0);
glTexCoord2f(1.0, 0.0); glVertex3f(1, -1, 0);
glTexCoord2f(1.0, 1.0); glVertex3f(1, 1, 0);
glEnd();
glNormal3f( 0.0, 0.0, 1.0);
// hours
glPushMatrix();
glRotate(-SysTime.wHour*30 - SysTime.wMinute/2 +90 , 0, 0, 1);
glBegin(GL_QUADS);
glColor3f(0.4, 0.4, 0.4);
glVertex3f(-0.2, 0, 0);
glVertex3f(0, -0.03, 0);
glVertex3f(0.6, 0, 0);
glVertex3f(0, 0, 0.1);
glColor3f(1, 0.2, 0.2);
glVertex3f(-0.2, 0, 0);
glVertex3f(0, 0, 0.1);
glVertex3f(0.6, 0, 0);
glVertex3f(0, 0.03, 0);
glEnd();
glPopMatrix();
// minutes
glPushMatrix();
glRotate(-SysTime.wMinute*6 - SysTime.wSecond/10 +90, 0, 0, 1);
glBegin(GL_QUADS);
glColor3f(0.4, 0.4, 0.4);
glVertex3f(-0.2, 0, 0.1);
glVertex3f(0, -0.03, 0.1);
glVertex3f(0.7, 0, 0.1);
glVertex3f(0, 0, 0.2);
glColor3f(0.8, 0.6, 0.2);
glVertex3f(-0.2, 0, 0.1);
glVertex3f(0, 0, 0.1);
glVertex3f(0.7, 0, 0.1);
glVertex3f(0, 0.03, 0.1);
glEnd();
glPopMatrix();
// seconds
glPushMatrix();
glRotate(-SysTime.wSecond*6 +90, 0, 0, 1);
glBegin(GL_QUADS);
glColor3f(0.4, 0.4, 0.4);
glVertex3f(-0.15, 0, 0.1);
glVertex3f(0, 0, 0.1);
glVertex3f(0.8, 0, 0.1);
glVertex3f(0, 0.025, 0.1);
glEnd();
glPopMatrix();
// seconds
glPushMatrix();
glTranslate(0, -0.55, 0);
glRotate(-SysTime.wSecond*6+90, 0, 0, 1);
glBegin(GL_QUADS);
glColor3f(0.2, 0.2, 0.2);
glVertex3f(-0.08, 0, 0.1);
glVertex3f(0, 0, 0.1);
glVertex3f(0.25, 0, 0.1);
glVertex3f(0, 0.01, 0.1);
glEnd();
glPopMatrix();
glPopMatrix();
SwapBuffers(wglGetCurrentDC);
Canvas.Font.Size := 12;
//przezroczysty kolor tła
Canvas.Brush.Style := bsClear;
//granatowe napisy
Canvas.Font.Color := clNavy;
Canvas.TextOut(65, 50, FormatDateTime('h:nn:ss', Time));
//zielone napisy
Canvas.Font.Color := clGreen;
Canvas.TextOut(66, 51, FormatDateTime('h:nn:ss', Time));
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
draw();
end;
procedure TForm1.kolor_tarczy();
begin
inc(x_color);
if x_color > 17 then x_color := 0;
if x_color = 0 then
begin
//biały
R_color := 1.0;
G_color := 1.0;
B_color := 1.0;
end;
if x_color = 1 then
begin
//jasny szary
R_color := 0.7;
G_color := 0.7;
B_color := 0.7;
end;
if x_color = 2 then
begin
//szary
R_color := 0.5;
G_color := 0.5;
B_color := 0.5;
end;
if x_color = 3 then
begin
//ciemny szary
R_color := 0.3;
G_color := 0.3;
B_color := 0.3;
end;
if x_color = 4 then
begin
//czarny
R_color := 0.0;
G_color := 0.0;
B_color := 0.0;
end;
if x_color = 5 then
begin
//jasny żółty
R_color := 1.0;
G_color := 1.0;
B_color := 0.6;
end;
if x_color = 6 then
begin
//żółty
R_color := 1.0;
G_color := 0.8;
B_color := 0.2;
end;
if x_color = 7 then
begin
//pomarańczowy
R_color := 1.0;
G_color := 0.6;
B_color := 0.7;
end;
if x_color = 8 then
begin
//różowy
R_color := 1.0;
G_color := 0.6;
B_color := 0.6;
end;
if x_color = 9 then
begin
//czerwony
R_color := 1.0;
G_color := 0.2;
B_color := 0.2;
end;
if x_color = 10 then
begin
//fioletowy
R_color := 1.0;
G_color := 0.6;
B_color := 1.0;
end;
if x_color = 11 then
begin
//ciemny fioletowy
R_color := 0.6;
G_color := 0.5;
B_color := 1.0;
end;
if x_color = 12 then
begin
//jasny zielony
R_color := 0.6;
G_color := 1.0;
B_color := 0.7;
end;
if x_color = 13 then
begin
//soczysty zielony
R_color := 0.3;
G_color := 1.0;
B_color := 0.4;
end;
if x_color = 14 then
begin
//ciemniejszy zielony
R_color := 0.3;
G_color := 1.0;
B_color := 0.7;
end;
if x_color = 15 then
begin
//ciemny zielony
R_color := 0.0;
G_color := 0.5;
B_color := 0.1;
end;
if x_color = 16 then
begin
//niebieski
R_color := 0.0;
G_color := 0.0;
B_color := 1.0;
end;
if x_color = 17 then
begin
//jasny niebieski
R_color := 0.0;
G_color := 1.0;
B_color := 1.0;
end;
end;
end.
Kod źródłowy kostki 3D:
Poproszę o poprawienie formatowania bo się kompletnie rozjechało :(
Jeszcze kilka dni temu było OK.
Dzięki
Chyba coś jest nie w porządku w funkcji FormResize - u mnie jak zmieniam wielkość okna rośnie w nieskończoność ilość pamięci zajmowanej przez program! W końcu prowadzi to do błędu i zmulenia systemu. Myślę że jest to wina uruchamiania ciągle od nowa funkcji wglCreateContext. Pewnie trzeba najpierw zrobić DelateContext. Ja zrobiłem resize inaczej - poprzez dodanie na początku GLInit wywołani glViewport(0, 0, panel1.width, panel1.height); (u mnie okno OpenGL jest w panelu), natomiast w funkcji FormResize po prostu wywołuję GLInit.
Poza tym artykuł bardzo fajny :)
super art !!!!!!!!!!!!!!!!!
Już dawno takiego nie było
Tak trzymać
Pozdrawiam serdecznie
B. fajne
Czy mogę prosić o następujące:
grafika 2d - Opengl
użycie Cg
Adam Majewski
Wow niezly art ;> Nice nice ;> gw ;>
"GL_POLYGON ? wielokąty o dowolnej liczbie wierzchołków."
dodał bym tam słowo "wypukłe" za "wielokąty" ;>
Proponuje jakoś podzielić tren art na strony, bo ładowanie go jest już meczące
Poprawiłem: procedure Tprez_form.Draw(); na Tprez_form.FormPaint();
Poprawiłem: var przypisać begin na var pixelFormat:integer;
Wyszukałem i poprawiłem literówki (mam nadzieję, że wszystkie ...)
Nie żebym się czepiał, ale nie powinno być tam Tprez_form.FormPaint(); ??
" );
var przypisać
begin"
E... Że co?
No i jeszcze jakieś literówki/orty się czasami zdarzają. Mimo wszystko artykuł imponujący.
OpenGL
Proponuję wrzucić ten schemat w < code >< plain >< /plain >< /code >, tylko potem kolorowanie składni się wykłada :/
Przydał by się przykład w pliku.