OpenGL
narzędzie dla bardzo ambitnych
Robert Machejek
Instytut Informatyki Uniwersytet Śląski w Katowicach
12 grudnia 2008
OpenGL - co to właściwie jest?
OpenGL (ang. Open Graphics Library) - specyfikacja uniwersalnego API do generowania grafiki. Zestaw funkcji składa się z 250 podstawowych wywołań, umożliwiających budowanie złożonych trójwymiarowych scen z podstawowych figur geometrycznych.
OpenGL wykorzystywany jest często przez gry komputerowe i wygaszacze ekranu, spełnia rolę analogiczną, jak konkurencyjny Direct3D (część DirectX) w systemie Windows firmy Microsoft. Również programy do przedstawiania wyników badań naukowych, CAD, oraz wirtualnej rzeczywistości używają OpenGL.
OpenGL - krótka historia
Troszkę historii..
1992 - powstaje wersja 1.0 specyfikacji OpenGL przenośnej między platformami. OpenGL Architecture Review Board (SG,HP,IBM) 1995 - wersja 1.1 tej biblioteki z wieloma poprawkami
przyśpieszającymi wyświetlanie grafiki.
No to na pewno warte uwagi
OpenGL nie należy do języków opisu sceny. Scena tworzona jest w OpenGL z wielokątów poprzez wywoływanie odpowiednich procedur Języki programowania:
Pascal,C/C++,Visual Basic,Python,Java
Składnia - bo jak bez niej żyć
Polecenia OpenGL określane są jako funkcje lub procedury. Znaczna cześć funkcji wykonuje te same operacje, ale różni się zbiorem argumentów.
Przyjęta konwencja nazewnictwa określa ilość i rodzaj parametrów funkcji według poniższego schematu:
rtype Name {1|2|3|4} {b|s|i|f|d|ub|us|ui} {v}
[args, ]Targ1, ..., TargN[, args]
gdzie poszczególne elementy oznaczają:
• rtype - wartość zwracana przez funkcje,
• Name - nazwa funkcji,
• 1, 2, 3, 4 - ilość argumentów funkcji,
• b - argumenty typu GLbyte,
• s - argumenty typu GLshort,
• ... itd... czyli najnormalniej typy danych GLfloat, GLdouble, GLubyte, GLushort, GLuint
• v - argument funkcji stanowi tablica wartości,
• T arg1, ..., T argN - argumenty funkcji.
Typy danych - aby ładnie na różnych platformach hulało
Typ danych
OpenGL
min. liczba bitów
Opisik
GLboolean 1 typ logiczny
GLbyte 8 liczba całkowita ze znakiem (U2)
GLubyte 8 liczba całkowita bez znaku
GLchar 8 ciąg znaków tekstowych
GLshort 16 liczba całkowita ze znakiem (U2)
GLushort 16 liczba całkowita bez znaku
GLint 32 liczba całkowita ze znakiem (U2)
GLuint 32 liczba całkowita bez znaku
GLsizei 32 nieujemna liczba całkowita
GLenum 32 typ wyliczeniowy całkowity
GLintptr ptrbits wskaźnik na liczbę całkowitą ze znakiem (U2) GLsizeiptr ptrbits wskaźnik na nieujemna liczbę całkowitą
GLbitfield 32 pole bitowe
GLfloat 32 liczba zmiennoprzecinkowa
GLclampf 32 liczba zmiennoprzecinkowa z przedziału [0, 1]
GLdouble 64 liczba zmiennoprzecinkowa
GLclampd 64 liczba zmiennoprzecinkowa z przedziału [0, 1]
Wady a może zalety
OpenGL nie ma funkcji obsługujących operacje:
wejścia/wyjścia
interakcje z użytkownikiem zarządzanie oknami.
Wywołania funkcji w OpenGL są zgodne z konwencją wywoływania funkcji w języku C.
No to narysujmy tło
Na dobry początek tworzymy klasę MyView public class MyView : OpenGLControl {public MyView() : base()
{}public override void glDraw()
{GL.glClear(GL.GL_COLOR_BUFFER_BIT);}
protected override void InitGLContext() {base.InitGLContext();
GL.glClearColor( 0.0f, 0.0f, 1.0f, 0.0f );}
protected override void OnSizeChanged(EventArgs e) {base.OnSizeChanged(e);}
}
No to narysujmy tło
Mamy już klasę to teraz wypada konstruktor ładny napisać public class MyView : OpenGLControl
public Form1()
{InitializeComponent();
this.ClientSize = new System.Drawing.Size(640, 480);
// Ustawiamy rozmiar widoku this.view = new MyView();
// Tworzymy obiekt klasy MyView this.view.Parent = this;
// Ustawiamy rodzica na gªówne okno aplikacji this.view.Dock = DockStyle.Fill;
// B¦dziemy wypeªnia¢ caªy okno.
}
... a jeżeli komuś się wydaje że to już koniec to zapomniał o ...
No to narysujmy tło
...o metodzie main klasy Form1 static void Main()
{ Form1 form = new Form1();
form.Show();
while( !form.IsDisposed ) { form.view.glDraw();
form.Refresh();
Application.DoEvents();
}form.Dispose();
}
No to narysujmy tło
Na koniec wypadało by się pochwalić wynikami :D ...
No to narysujmy tło
... wiec się chwalimy
aby nie było bo mieliśmy się czegoś nauczyć
Przywołani do porządku dowiedzmy się co te dziwne znaczki na wcześniejszych stronach oznaczają
glDraw()- funkcja odpowiedzialna za wy±wietlanie. Standardowo metoda ta jest wywoªywana na zdarzeniePaint.
InitGLContext()- w tej metodzie przeprowadzane s¡ wszystkie inicjacje jak np ustawienie koloru czyszczenia bufora kolorów.
OnSizeChanged() - metoda obsªuguj¡ca zdarzenie zmiany rozmiaru okna..
ClientSize - ustawiamy rozmiar po obszaru roboczego po jakim b¦dziemy rysowa¢.
view.Parent - ustawiamy rodzica dla na gªówne okno. Dzi¦ki temu okre±lamy gdzie b¦dziemy rysowa¢ za pomoc¡ klasy MyView. viewDock- okre±lany sposób wypeªnienia obszaru roboczego. W tym przypadku ustawiamy wypeªnianie caªego okna.
...a co namieszaliśmy w metodzie Main?
Po pierwsze usunęliśmy standardowe uruchomienie aplikacji. W tym przypadku było to konieczne. Problem tkwi bowiem w nieustannym odświeżaniu okna. Problem rozwiązaliśmy następująco:
tworzymy obiekt reprezentujący nasze okno głównego wyświetlamy okno
wykonujemy pętle do momentu kiedy okno nie zostanie uznane za niewłaściwą kontrolkę (następuje to np po wywołaniu komendy zamknięcia aplikacji). W każdej iteracji wywołujemy metodą rysującą klasyMyView, odświeżamy okno a następnie obsługujemy
komunikaty okna.
rysujemy trójkącik :D
W tym celu musimy naspidać metodęDraw() public override void Draw() {#region Draw Triangle
Gl.glPushMatrix();
base.Draw();
Gl.glBegin(Gl.GL_TRIANGLES);
// Rysowanie trójk¡ta Gl.glColor4fv(colorTop);
Gl.glVertex3f(0.0f, (oat)(+height / 2), 0.0f);
// Wierzchoªek trójk¡ta Gl.glColor4fv(colorLeftBottom);
Gl.glVertex3f((oat)(-xSize / 2), (oat)(-height / 2), 0.0f);
// Lewy bok trójk¡ta
Gl.glColor4fv(colorRightBottom);
Gl.glVertex3f((oat)(+xSize / 2), (oat)(-height / 2), 0.0f);
// rodeczek trójk¡ta Gl.glEnd();
// I to by byªo na tyle z trójk¡cikiem Gl.glPopMatrix();
#endregion }
rysujemy trójkącik cd..
Biorąc pod uwagę, że tym razem korzystamy z bogatej biblioteki TaoFrameworkw wyniku powyższej operacji powinniśmy otrzymać poniższy efekt
rysujemy trójkącik cd..
Chcąc narysować kwadracik w wersji ambitniejszej wracamy do pierwszej implementacji (niebieskie tło) i nadpisujemy klasy zgodnie ze schematem.
protected override void InitGLContext() { GL.glShadeModel(GL.GL_SMOOTH);
// gładkie cieniowanie
GL.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
// kolor czyszczenia bufora kolorów GL.glClearDepth(1.0f);
// wartość wypełnienia bufora głębi GL.glEnable(GL.GL_DEPTH_TEST);
GL.glDepthFunc(GL.GL_LEQUAL);
GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT,GL.GL_NICEST);
}
rysujemy kwadracik cd..
protected override void OnSizeChanged(EventArgs e) {base.OnSizeChanged(e);
// wywoªujemy metod¡ klasy bazowej System.Drawing.Size s = Size;
// pobieramy nowy rozmiar okna
GL.glMatrixMode(GL.GL_PROJECTION);
GL.glLoadIdentity();
GL.gluPerspective(45.0f, (double)s.Width /(double) s.Height, 0.1f, 100.0f);
GL.glMatrixMode(GL.GL_MODELVIEW);
GL.glLoadIdentity();
}
rysujemy kwadracik cd..
public override void glDraw(){ GL.glClear(GL.GL_COLOR_BUFFER_BIT); GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); GL.glMatrixMode(GL.GL_MODELVIEW); GL.glLoadIdentity(); GL.glTranslatef(0.0f,0.0f,-5.0f);
GL.glRotatef(xrot,1.0f,0.0f,0.0f); // obrót po osi X xrot+=0.3f; // zwiększenie kąta obrotu po osi X GL.glRotatef(yrot,0.0f,1.0f,0.0f); // obrót po osi Y yrot+=0.2f; // zwiększenie kąta obrotu po osi Y GL.glRotatef(zrot,0.0f,0.0f,1.0f); // obrót po osi Z zrot+=0.4f; // zwiększenie kąta obrotu po osi Z GL.glBegin(GL.GL_QUADS); // rozpoczęcie rysowania czworoboków
GL.glColor3f(1.0f, 0.0f, 0.0f); GL.glVertex3f(1.0f,1.0f,1.0f); // deklaracja koloru i wierzchołka GL.glColor3f(1.0f, 0.0f, 0.0f); GL.glVertex3f(-1.0f,1.0f,1.0f)
GL.glColor3f(1.0f, 0.0f, 0.0f); GL.glVertex3f(-1.0f,-1.0f,1.0f);
GL.glColor3f(1.0f, 0.0f, 0.0f); GL.glVertex3f(1.0f,-1.0f,1.0f);
GL.glColor3f(0.0f, 1.0f, 0.0f); GL.glVertex3f(-1.0f,1.0f,-1.0f);
GL.glColor3f(0.0f, 1.0f, 0.0f); GL.glVertex3f(1.0f,1.0f,-1.0f);
GL.glColor3f(0.0f, 1.0f, 0.0f); GL.glVertex3f(1.0f,-1.0f,-1.0f);
GL.glColor3f(0.0f, 1.0f, 0.0f); GL.glVertex3f(-1.0f,-1.0f,-1.0f);
GL.glColor3f(0.0f, 0.0f, 1.0f); GL.glVertex3f(1.0f,1.0f,-1.0f);
GL.glColor3f(0.0f, 0.0f, 1.0f); GL.glVertex3f(-1.0f,1.0f,-1.0f);
GL.glColor3f(0.0f, 0.0f, 1.0f); GL.glVertex3f(-1.0f,1.0f,1.0f);
GL.glColor3f(0.0f, 0.0f, 1.0f); GL.glVertex3f(1.0f,1.0f,1.0f);
GL.glColor3f(1.0f, 0.0f, 1.0f); GL.glVertex3f(1.0f,-1.0f,1.0f);
GL.glColor3f(1.0f, 0.0f, 1.0f); GL.glVertex3f(-1.0f,-1.0f,1.0f);
GL.glColor3f(1.0f, 0.0f, 1.0f); GL.glVertex3f(-1.0f,-1.0f,-1.0f);
GL.glColor3f(1.0f, 0.0f, 1.0f); GL.glVertex3f(1.0f,-1.0f,-1.0f);
GL.glColor3f(0.0f, 1.0f, 1.0f); GL.glVertex3f(1.0f,1.0f,-1.0f);
GL.glColor3f(0.0f, 1.0f, 1.0f); GL.glVertex3f(1.0f,1.0f,1.0f);
GL.glColor3f(0.0f, 1.0f, 1.0f); GL.glVertex3f(1.0f,-1.0f,1.0f);
GL.glColor3f(0.0f, 1.0f, 1.0f); GL.glVertex3f(1.0f,-1.0f,-1.0f);
GL.glColor3f(1.0f, 1.0f, 0.0f); GL.glVertex3f(-1.0f,1.0f,1.0f);
GL.glColor3f(1.0f, 1.0f, 0.0f); GL.glVertex3f(-1.0f,1.0f,-1.0f);
GL.glColor3f(1.0f, 1.0f, 0.0f); GL.glVertex3f(-1.0f,-1.0f,-1.0f);
GL.glColor3f(1.0f, 1.0f, 0.0f); GL.glVertex3f(-1.0f,-1.0f,1.0f);
GL.glEnd(); // koniec rysowania }
rysujemy kwadracik cd..
ambitnego Bohachevskiego narysujmy
Zaczynamy standardowo - nadpisujemy klasy do czego zdążyliśmy się już chyba przyzwyczaić.
protected override void InitGLContext(){
GL.glShadeModel(GL.GL_SMOOTH); // gªadkie cieniowanie
GL.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // kolor czyszczenia bufora kolorów
GL.glClearDepth(1.0f); // warto±¢ wypeªnienia bufora gª¦bi GL.glEnable(GL.GL_DEPTH_TEST); // uaktywnienie testu gª¦bi GL.glDepthFunc(GL.GL_LEQUAL); // rodzaj funkcji testuj¡cej gª¦bie GL.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
BuildLists();
}
ambitnego Bohachevskiego narysujmy
Nadpisujmy dalej - w końcu wersja dla ambitnych ma być
protected override void OnSizeChanged(EventArgs e){ {
base.OnSizeChanged(e); // wywoªujemy metod¡ klasy bazowej System.Drawing.Size s = Size; // pobieramy nowy rozmiar okna GL.glMatrixMode(GL.GL_PROJECTION);
GL.glLoadIdentity();
GL.gluPerspective(45.0f, (double)s.Width / (double)s.Height, 0.1f, 100.0f);
GL.glMatrixMode(GL.GL_MODELVIEW) ustalamy widok;
GL.glLoadIdentity();
}
ambitnego Bohachevskiego narysujmy
Teraz utworzymy sobie klasę którą sobie porysujemy public void DrawGraph(int functionNumber){ {
float r = 0, g = 0, b = 0;
GetData(bohachevsky f1.dat); break; } GL.glBegin(GL.GL_QUAD_STRIP);
for (int layer = 0; layer < 10; layer++) {
for (int i = 0; i < pointCount; i++) {
if (zTable[i] < 0) GL.glColor3f(0.0f, 0.6f, 1.0f);
else
GL.glColor3f(0.0f, 0.3f, 1.0f);
GL.glVertex3f(xTable[i], zTable[i], yTable[i]);
GL.glVertex3f(xTable[i + layer * pointCount], zTable[i + layer * pointCount], yTable[i + layer * pointCount]);
if (i == 96) { i = i + 2;
GL.glEnd();
GL.glBegin(GL.GL_QUAD_STRIP);
}
{ r = r + 0.01f; g = g + 0.01f; b = b + 0.01f; } i++;
} } GL.glEnd(); }) }
Magiczny plik bohachevsky f1.dat
Magiczny pliczek to nic innego jak współrzędne które posłużą do wyrysowania funkcji oraz typ zmiennej
#x y z type -50 -50 7500 GLint -48.9899 -50 7400.61 GLint -47.9798 -50 7302.07 GLint -46.9697 -50 7206.74 GLint -45.9596 -50 7112.31 GLint -44.9495 -50 7021.02 GLint -43.9394 -50 6930.72 GLint -42.9293 -50 6843.46 GLint -41.9192 -50 6757.3 GLint -40.9091 -50 6674.05 GLint -39.899 -50 6592.06 GLint -38.8889 -50 6512.8 GLint -37.8788 -50 6434.98 GLint ... ... ... ...
-29.798 -50 5888.32 GLint -28.7879 -50 5828.92 GLint -27.7778 -50 5772.05 GLint -26.7677 -50 5716.63 GLint -25.7576 -50 5663.95 GLint ... ... ... ...
wynik końcowy wcześniejszego kodu
Czyli ładny wykresik dostaliśmy
Podsumowanie
Reasumując olbrzymią zaletą OpenGL jest wizualizacja (takie ładne wszystko). Wodą jest z całą pewnością skomplikowana składnia.
Dziękuje za uwagę