Dezvoltarea aplicaţiilor OpenGL pe platforma .NET folosind suita TAO

De la Vasile Alaiba - MediaWiki

Salt la: navigare, căutare

Cuprins

Rezumat

Realizarea aplicaţiilor 3d pentru platforma .Net este dificilă deoarece aplicaţiile compilate pentru platforma .Net reprezintă cod managed care rulează pe maşina virtuală şi nu au acces direct la librării native de tip dll. TAO facilitează acest lucru oferind accesul la librăriile necesare prin intermediul unor wrappere. În acest tutorial vom prezenta paşii care trebuie urmaţi pentru realizarea unei aplicaţii 3d OpenGl în C# folosind TAO.

Introducere

Framework-ul TAO reprezintă o suita de binding-uri pentru librăriile cele mai utilizate în crearea de aplicaţii multimedia cu grafică 2d sau 3d. Librăriile la care avem acces sunt :

  • OpenAl - API multiplatformă folosit pentru generarea de sunete poziţionate 3D în jocuri şi alte aplicaţii audio.
  • OpenGl - Open Graphics Library API principal folosit la generarea de imagini 2D,3D
  • Sdl - Simple DirectMedia Layer este o librărie folosită la creare de aplicaţii multiplatformă multimedia care oferă acces uşor la realizarea de grafică 2D si 3D, sunet, de asemenea şi la dispozitivele de IO (mouse, tastatura).
  • Platform.Windows - oferă access la API-ul Windows şi la funcţiile de desenare de tip GDI (Graphics Device Interface)
  • PhysFs - oferă suport pentru realizarea si accesarea de arhive abstracte folosite în jocuri pentru a memora harţile de nivel (exemplu: Quake 1,2)
  • FreeGlut - este o alternativă opensource la GLUT (OpenGL Utility Toolkit). Oferă suport pentru operaţii de tip IO cu tastatura sau mouse-ul, definirea şi crearea de ferestre, acces-ul la diferite primitive grafice
  • ODE- Open Dynamics Engine reprezintă un motor de fizică în timp real fiind compus dintr-o parte care se ocupă cu fizica corpurilor rigide şi una care asigură detectarea coliziunilor.
  • Glfw - Facilitează dezvoltarea OpenGl oferind acces la creare de ferestre, monitorizare IO, încarcarea texturilor din fişiere, crearea şi sincronizarea thread-urilor.
  • DevIl - Developer's Image Library cu ajutorul căreia pot fi incărcate imagini salvate în diferite formate (.bmp, .cut, .dds, .doom, .exr, .hdr, .gif, .ico, .jp2, .jpg,.lbm, .mdl, .mng, .pal, .pbm, .pcd, .pcx, .pgm, .pic, .png, .ppm, .psd, .psp, .raw, .sgi, .tga si .tif file)
  • Cg - C for Graphics limbaj de nivel înalt dezvoltat de Nvidia folosit la crearea de shadere pentru plăcile video care suportă aceasta tehnologie.
  • Lua - permite executarea de script-uri scrise în limbajul Lua.

Avantaje

Permite accesul la librăriile menţionate mai sus, programatorul nemaifiind nevoit sa işi creeze propriile wrappere pentru ele. De asemenea majoritatea funcţiilor şi claselor au denumiri asemanatoare sau chiar identice cu cele din librăriile de bază astfel încât utilizarea lor este uşoara pentru cineva care a mai lucrat cu aceste librării dar în alte limbaje.

Dezavantaje

Datorită nivelului intermediar care se află între librăriile propriu-zise şi aplicaţia .Net se pierde din viteza de execuţie. Alt dezavantaj îl reprezintă faptul că framework-ul nu este foarte bine documentat. Utilizatorul trebuie să ştie să lucreze cu librăriile puse la dispozitie de Tao în limbajele lor native.

Instalarea framework-ului

Pentru a utiliza framework-ul Tao avem nevoie de ultima versiune a acestuia care poate fi downloadata de la adresa Tao framework. Instalăm pachetul downloadad şi astfel îl vom putea utiliza in proiectele noastre, dll-urile fiind automat inregistrare şi în GAC. În aplicatia realizată in acest tutorial vom folosi drept control pentru afişare controlul SimpleOpenGlControl care se gaseşte în Tao.Platform.Windows. In Visual Studio vom creea un nou proiect de tip Windows Application la care vom adăuga ca referinţe Tao.OpenGl şi Tao.Platform.Windows.

Notiuni de baza OpenGl

Coordonate

În OpenGl avem de a face cu un sistem de axe cartezian în care dupa cum se poate vedea si din figura axa X este spre dreapta, axa Y este îndreptata în sus iar axa Z (pentru cea de a treia coordonata - adâncimea spatiala ) înteapa monitorul dinspre monitor spre noi. Astfel orice punct din acest spatiu este determinat de trei coordonate.

Imagine:axe.jpg

Primitivele geometrice (puncte, segmente de dreapta si poligoane) sunt definite de unul sau mai multe vârfuri (vertex-uri). Un vârf defineste un punct, punctul terminal al unei muchii sau coltul în care se întâlnesc doua muchii ale unui poligon. Unui vârf îi sunt asociate data constând din coordonate de pozitie, culori, normale si coordonate de textura si fiecare vârf este procesat în mod independent si în acelasi fel.

Primitive de desenare

Pentru toate functiile de desenare OpenGl exista mai multe variante în functie de tipul argumentelor acceptate. Cele mai folosite sunt cele care accepta argumente în virgula mobila de tip float.Aceste functii au în denumirea lor sufixul 3f (pentru coordonate 3D) sau 2f (pentru coordonate 2D). În virgula mobila valorile pentru componentele de culoare de la 0.0 pâna la 1.0.

Desenarea unor consta din crearea unei liste de comenzi de desenare, lista cuprinsa între apelul functiilor glBegin() si glEnd(). Functia glBegin() primeste ca parametru tipul primitivelor grafice care urmeaza sa fie desenate. În functie de acest parametru si de modul în care organizate vârfurile putem desena oricare din primitivele ilustrate în tabelul de mai jos:

Primitiva Argument glBegin Explicatii
Imagine:PrimitiveGLPoints.JPG GL_POINTS un exemplu de cod care deseneaza câte un punct pe fiecare axa
glBegin(GL_POINTS);
  glVertex3f(0.5,0.0,0.0);
  glVertex3f(0.0,0.5,0.0);
  glVertex3f(0.0,0.0,0.5);
glEnd();
Imagine:PrimitiveGLLines.JPG GL_LINES Pentru a putea desena o linie avem nevoie de o pereche de puncte.Astfel în interiorul blocului înconjurat de glBegin() si glEnd() vom declara un numar par de puncte, câte doua pentru fiecare linie.
Imagine:PrimitiveGlLineStrip.JPG GL_LINE_STRIP În cazul în care ar trebui sa desenam un contur oarecare deschis ar trebui ca pentru fiecare linie sa specificam doua puncte chiar daca un punct este comun la doua linii care urmeaza una dupa alta.Deaorece aceste apeluri reduc performanta a fost introdus acest mod de desenare.Între glBegin() si glEnd() trebuie sa specificam doar punctele pentur prima linie si dupa ele urmatoarele puncte care compun conturul.
Imagine:PrimitiveGlLineLoop.JPG GL_LINE_LOOP Acest mod de desenare se aseamana cu cel de la GL_LINE_STRIP singura diferenta fiind ca se mai traseaza o linie de la primul la ultimult punct specificat astfel putem desena poligoane de diferite forme foarte usor.
Imagine:PrimitiveGlTriangles.JPG GL_TRIANGLES Folosind aces parametru pentur glBegin putem desena triunghiuri.Între glBegin si glEnd cu ajutorul functie glVertex3f trebuie sa specificam câte 3 puncte pentru fiecare triunghi care se doreste a fi desenat.Ordinea în care sunt specificate punctele este: primult punct reprezinta vârful din stânga jos al triunghiului, al doilea reprezinta vârful din dreapta jos iar ultimul vârful de sus al triunghiului.
Imagine:PrimitiveGlTriangleStrip.JPG GL_TRIANGLE_STRIP Parametrul este folosit tot pentru a îmbunatati viteza de desenare. Atunci când avem mai multe triunghiuri unite pe o latura nu trebuie decât sa declaram triunghiul initial de la care se pleaca si lista de puncte aditionale incluse în celelalte triunghiuri.
Imagine:PrimitiveGlTriangleFan.JPG GL_TRIANGLE_FAN Acest mod de desenare ajuta atunci când toate triunghiurile au ca vârf un punct comun.Astfel cu numai 5 apeluri la glVertex3f putem desena 3 triunghiuri pentru figura alaturata.În mod normal ar fi trebuit sa avem 6 apeluri.
Imagine:PrimitiveGlQauds.JPG GL_QUADS Permite desenarea de primitive de tipul patrulaterelor.Între glBegin si glEnd trebuie sa specificam cele 4 puncte care compun patrulaterul.
Imagine:PrimitiveGlQuadStrip.JPG GL_QUAD_STRIP Ca si în cazul GL_LINE_STRIP sau GL_TRIANGLE_STRIP acest parametru este folosit pentru a reduce din apelurile necesare catre functia glVertex3f. Specificam primul patrulater si dupa aceea punctele aditionale care formeaza însiruirea de patrulatere
Imagine:PrimitiveGlPolygon.JPG GL_POLYGON Folosind acest parametru în apelul functiei glBegin putem desena usor poligoane. Între glBegin si glEnd vom specifica punctele care compun poligonul.

Transformari geometrice

În grafica se doreste ca transformarile care se aplica obiectelor sa pastreze geometria acestora. Transformarile realizate de OpenGl fac parte dintr-o clasa speciala de transforma - transformarile afine. Transformarile suportate de OpenGl sunt:

  • Rotatia
  • Scalarea
  • Translatia
  • Proiectia

Transformarile se obtin prin înmultirea matricei de coordonate a unui vârf cu matricea de transformare dorita. Deoarece, matricea pentru scalare are dimensiunea 4x4 si pentru înmultire dimensiunile trebuie sa corespunda, s-a mai adaugat o coordonata pentru reprezentarea unui vârf, coordonata care are de obicei în OpenGl valoarea 1.0. Prin urmare, intern, coordonatele 3D ale unui vârf sunt reprezentate în OpenGl ca vector coloana cu 4 elemente, acest tip de coordonate fiind denumite coordonate omogene.

Rotatia

  • Matricea de transformare pentru rotatie în jurul axei Ox

Imagine:RotatieX.jpg

  • Matricea de transformare pentru rotatie în jurul axei Oy


Imagine:RotatieY.jpg

  • Matricea de transformare pentru rotatie în jurul axei Oz


Imagine:RotatieZ.jpg


Scalarea

Imagine:Scalare.JPG

Translatia

Imagine:Translatia.JPG

OpenGl asigura stive de matrice pentru memorarea matricelor pentru fiecare stare reprezentata printr-o matrice curenta (stive si matrice curente pentru modelare-vizualizare, proiectie, texturare etc). Se va comuta pe fiecare tip de stiva si se va stabili matricea curenta înainte de reprezentarea scenei. Matricea de modelare-vizualizare contine toate tranformarile geometrice care s-au aplicat scenei curente si ca dimensiune poate contine cel putin 32 de elemente. Stiva de proiectie se refera la pozitionarea camerei si la tipul acesteia. Prin schimbarea acestei stive putem obtine efecte interesante de vizualizare de tipul "fish-eye". Stiva de texturare contine transformarii aplicate texturiilor curente. Pentru a stabili care stiva de matrici vrem sa o modificam OpenGl pune la dispozitie functia [ glMatrixMode] care primeste ca parametru tipul stivei : GL_MODELVIEW (implicit), GL_PROJECTION, GL_TEXTURE.

Pentru pune sau scoate o anumita matrice de pe stiva curenta avem la dispozitie mai multe functii :

  • glLoadIdentity() - încarca matricea identitate pe stiva, deci asupra obiectelor din scena nu se executa nici o transformare
  • glLoadMatrixf(const GLFloat *m) - încarca în matricea curenta matricea trimisa ca parametru (pointer la tablou de 16 elemente)
  • glMulMatrix(const GLFloat *m) - înmulteste matricea curenta cu matricea primita ca parametru
  • glPushMatrix() - pune în vârful stivei matricea curenta
  • glPopMatrix() - extrage matricea din vârful stivei fara a o salva undeva.

Stivele de matrice sunt utilizate deoarece este mai eficient sa salvezi sau sa restaurezi o matrice decât sa o calculezi sau generzi din nou. Asupra obiectelor scenei se va aplica întotdeauna matricea curenta. Pentru a aplica mai mult de 2 transformari putem folosi functia de înmultire glMulMatrix. Modul descris mai sus de a executa transformari este destul de greoi deoarece trebuie specificate matricile de transformare. O modalitate mai usoara este folosirea functiilor de transformare.Acestea sunt :

  • glRotatef(GLfLoat angle,GLfLoat x,GLfLoat y,GLfLoat z) - parametrul angle reprezinta unghiul de rotatie în grade iar x,y si z reprezinta coordonatele vectorului în jurul caruia se face rotatia.
  • glScalef(GLfloat x, GLfloat y, GLfloat z) - parametri reprezinta factorii de scalare pentru fiecare axa
  • glTranslate(GLfLoat tx,GLfLoat ty,GLfLoat tz) - functia va produce o translatie de vector de coordonate (tx ty tz)

Realizarea unei aplicatii simple

În sprijinul celor expuse mai sus vom dezvolta o aplicatie simpla care sa permita vizualizarea unui obiect 3D, rotirea si miscarea sa. Pentru aceasta vom creea un proiect nou de tip windows application. Am putea porni si de la un proiect gol sau de la un proiect de tip consola dar aici v-om utiliza un control numit SimpleOpengGlControl care se gaseste în Tao.Platform.Windows care faciliteaza mult dezvoltarea de aplicatii de acest fel cu singurul inpediment ca nu vom putea face afisea fullscreen.Acest control va face pentru noi toate initializarile necesare.

Imagine:Pas1.JPG

Ca referinte vom avea nevoie de Tao.OpenGl pentru a avea acces la functiile de baza de desenare si Tao.Platform.Windows pentru accesul la SimpleOpenglControl . Vom începe crearea aplicatiei prin adaugarea la forma principala a unui control de tip SimpleOpenGlControl. Vom seta dimensiunile formei la 300x300 px.Pentru SimpleOpengGlControl îi vom seta proprietatea de dock pe valoare de fill. În acest moment daca lansam în executie aplicatia vom primi o eroare ca cea din imaginea de mai jos :

Imagine:Eroarecontext.JPG

Aceasta eroare apare deoarece contextul OpenGl înca nu a fost initializat. Daca nu am fi folosit SimpleOpenGlControl ar fi trebuit sa ne declaram o serie de functii care fac acest lucru. Aici trebuie doar sa apelam imediat dupa creerea controalelor copil de pe forma, dupa apelul InitializeComponent() din constructorul formei functia InitializeContexts(). Pâna acum codul asociat formei ar trebui sa arate ca mai jos :

using System;
using Tao.OpenGl; 
using System.Drawing;
using System.Windows.Forms;

namespace tutorial1
{
    public partial class frmTaoTutorial : Form
    {
        public frmTaoTutorial()
        {
            InitializeComponent();
            mSimpleOpenGlView.InitializeContexts(); // initializam contextul OpenGl
        }

    }
}

Desenarea efectiva o vom face în momentul în care forma principala îsi face repaint, adica va trebui sa suprascriem functia onPaint a formei. Toate functiile de desenare le vom grupa într-o functie proprie pe care o vom numi DrawScene si care va fi apelata în onPaint.

using System;
using Tao.OpenGl; 
using System.Drawing;
using System.Windows.Forms;

namespace tutorial1
{
    public partial class frmTaoTutorial : Form
    {
        public frmTaoTutorial()
        {
            InitializeComponent();
            mSimpleOpenGlView.InitializeContexts(); // initializam contextul OpenGl
            Gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            Gl.glShadeModel(Gl.GL_FLAT);
            Gl.glEnable(Gl.GL_DEPTH_TEST);
            Gl.glEnable(Gl.GL_CULL_FACE); 
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            this.DrawScene();   // redesenam si scena OpenGl
        }

        // aici desenam toate obiectele din scena
        private void DrawScene()
        {
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);  
        }
    }
}


Înafara de functia de desenare a scenei am mai adaugat dupa initializarea contextului OpenGl apelul la alte 4 functii.

Gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 

va seta culoarea cu care este stearsa scena, în cazul nostru aceasta fiind negru.

Gl.glShadeModel(Gl.GL_FLAT);

Aceasta functie seteaza modul în care vor fi colorate obiectele. Poate primi ca parametru GL_FLAT caz în care obiectele vor fi colorate uniform cu aceeasi culoare, sau GL_SMOOTH când culorile fiecarui vârf sunt interpolate si obiectul este colorat cu spectrul de culori astfel obtinut.

Gl.glEnable(Gl.GL_DEPTH_TEST);

Activând GL_DEPTH_TEST activam folosirea Z-Buffer-ului si astfel ne asiguram ca poligoane care se suprapun în adâncime sunt afisate corect.

glEnable(GL_CULL_FACE);

Pentru a mari viteza de desenare activarea "back-face culling" face ca doar fetele poligoanelor care au normala înspre ecran sa fie desenate.

Decamdata obtinem doar o suprafata neagra. Pentru a vedea ca merg seterile facute vom desena axele de coordonate de culori diferite. În functia DrawScene() vom adauga codul :

           Gl.glPushMatrix();

            const float viewAngle = 103.0f;
            Gl.glRotatef(viewAngle, 1.0f, 0.2f, 0.0f);
           
            const float axisSize = 25.0f;

            // desenarea axei z - culoarea rosu
            Gl.glColor3f(1.0f, 0.0f, 0.0f);
            Gl.glBegin(Gl.GL_LINES);
                Gl.glVertex3f(0.0f, 0.0f, -axisSize);
                Gl.glVertex3f(0.0f, 0.0f, axisSize);
            Gl.glEnd();

            // desenarea axei y - culoarea verde 
            Gl.glColor3f(0.0f, 1.0f, 0.0f);
            Gl.glBegin(Gl.GL_LINES);
                Gl.glVertex3f(0.0f, -axisSize, 0.0f);
                Gl.glVertex3f(0.0f, axisSize, 0.0f);
            Gl.glEnd();

            // desenarea axei x - culoarea albastra 
            Gl.glColor3f(0.0f, 0.0f, 1.0f);
            Gl.glBegin(Gl.GL_LINES);
                Gl.glVertex3f(-axisSize, 0.0f, 0.0f);
                Gl.glVertex3f(axisSize, 0.0f, 0.0f);
            Gl.glEnd();

            Gl.glPopMatrix();
            Gl.glFlush(); 

Rezultatul obtinut este :

Imagine:RezultatAxe.png

Axele sunt desenate corect, rotite cu un unghi de 103 grade dupa vectorul de coordonate (1.0, 0.2, 0.0 ) am dar atunci când facem resize la forma apare o problema. Scena se deplaseaza iar obiectele desenate dispar. Pentru a remedia acest "bug" trebuie ca în cazul în care forma se redimensioneaza sa redimensionam si viewport-ul OpenGl la noile dimensiuni. Vom mai adauga o functie denumita HandleResize() care va fi apelata imediat dupa initializarea controlului OpenGl în constructor si în functia de resize a formei.

        private void HandleResize(int height, int width)
        {
            Gl.glViewport(0, 0, width, height); 
 
            Gl.glMatrixMode(Gl.GL_PROJECTION); 
            Gl.glLoadIdentity(); 
 
            const float nRange = 80.0f; 
 
            if (height == 0) 
            {
                height = 1;
            }
            if (width <= height)
            {
                Gl.glOrtho(-nRange, nRange, -nRange * height / width,
                          nRange * height / width, -nRange, nRange);
            }
            else
            {
                Gl.glOrtho(-nRange * width / height,
                    nRange * width / height,
                    -nRange, nRange, -nRange, nRange);
            }
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity(); 
        }

Observam ca resize-ul nu se face corect. Atunci când facem resize forma ramâne neagrea uneori ca si cum desenarea scenei nu s-ar fi executat.Acest lucru se întâmpla deoarece noi facem redesenarea scenei pe onPaint-ul formei. Normal ar fi ca scena sa fie redesenata în evenimentul de onPaint al SimpleOpenGlControl. Mutam apelul catre DrawScene din onPaint-ul formei în handler-ul pentru evenimentul de paint al controlulu OpenGl si totul functioneaza corect. Vom adauga la scena un cub care se va roti în jurul unei axe care o putem schimba de la tastatura cu ajutorul tastelor x,y sau z. Pentru aceasta vom modifica functia care deseneaza scena.În ea vom desena 6 quad-uri care formeaza cubul si vom roti aceste obiecte în functie rotatia activa la un moment dat. Vom avea nevoie de 3 variabile în care vom memora unghiurile de rotatie fata de cele trei axe de la un moment dat si trei flag-uri care ne vor spune daca pentru o anumita axa trebuie sa facem rotatie sau nu. Alta variabila adauga este cubeSize care reprezinta marimea laturii cubului care va fi desenat.

        private bool flgRotateX = false;
        private bool flgRotateY = false;
        private bool flgRotateZ = false;

        private float cubeSize = 10;

        private float angleX = 0.0f;
        private float angleY = 0.0f;
        private float angleZ = 0.0f;

La proiect vom mai adauga un obiect de tip timer care va avea setata proprietatea interval setata la 500, adica evenimentul tick al acestui obiect se va lansa odata la 500 de milisecunde. În handler-ul pentru evenimentul tick în functie de valoarea flag-ului pentru fiecare axa vom recalcula valoarea unghiului de rotatie pentru acea axa sau nu.În constructorul formei dupa initializarea OpenGL vom apela si metoda start a timer-ului. Codul asociat evenimentului tick al timer-ului îl prezentam mai jos :

        private void mTimer_Tick(object sender, EventArgs e)
        {
            if (flgRotateX)
            {
                angleX += 10;
                if (angleX == 360.0)
                    angleX = 0.0f;
            }
            if (flgRotateY)
            {
                angleY += 10;
                if (angleY == 360.0)
                    angleY = 0.0f;
            }
            if (flgRotateZ)
            {
                angleZ += 10;
                if (angleZ == 360.0)
                    angleZ = 0.0f;
            }
            this.mSimpleOpenGlView.Invalidate();
           
        }

Dupa ce am recalculat unghiurile trebuie sa redesanam scena si pentru a realiza acest lucru apelam metoda Invalidate() a obiectului mSImpleOpenGlView care forteaza redesenarea acestui obiect. Pe evenimentul de keypress al mSimpleOpengGlView trebuie sa vedem ce tasta a fost apasata si daca s-a apasat una din tastele care controleaza activarea sau dezactivarea rotatiei dupa o axa sa setam flag-ul corespunzator.Codul corespunzator acestui handler este :

        private void mSimpleOpenGlView_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 'x' || e.KeyChar == 'X')
                flgRotateX = !flgRotateX;
            if (e.KeyChar == 'y' || e.KeyChar == 'y')
                flgRotateY = ! flgRotateY;
            if (e.KeyChar =='z' || e.KeyChar == 'Z')
                flgRotateZ = !flgRotateZ;
        }

În el verificam daca s-a apasat x sau X si daca da setam valoarea flag-ului corespunzator cu valoarea negata a acestuia, astfel o apasare a tastei x activeaza rotatia dupa axa x si la a doua apasare o dezactivam. Va trebuie sa rescriem si codul de desenare a scenei pentru a desena si roti cubul.

        // aici desenam toate obiectele din scena
        private void DrawScene()
        {
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT |Gl.GL_DEPTH_BUFFER_BIT);
            Gl.glPushMatrix();

            
            Gl.glRotatef(angleX, 1.0f, 0.0f, 0.0f);

            Gl.glRotatef(angleY, 0.0f, 0.1f, 0.0f);

            Gl.glRotatef(angleZ, 0.0f, 0.0f, 1.0f);

            Gl.glBegin(Gl.GL_QUADS);		// Draw The Cube Using quads
                Gl.glColor3f(0.0f, 1.0f , 0.0f);	// Color Blue
                Gl.glVertex3f(cubeSize, cubeSize, -cubeSize);	// Top Right Of The Quad (Top)
                Gl.glVertex3f(-cubeSize, cubeSize, -cubeSize);	// Top Left Of The Quad (Top)
                Gl.glVertex3f(-cubeSize, cubeSize, cubeSize);	// Bottom Left Of The Quad (Top)
                Gl.glVertex3f(cubeSize, cubeSize, cubeSize);	// Bottom Right Of The Quad (Top)
                
                Gl.glColor3f(1.0f, 0.5f, 0.0f);	// Color Orange
                Gl.glVertex3f(cubeSize, -cubeSize, cubeSize);	// Top Right Of The Quad (Bottom)
                Gl.glVertex3f(-cubeSize, -cubeSize, cubeSize);	// Top Left Of The Quad (Bottom)
                Gl.glVertex3f(-cubeSize, -cubeSize, -cubeSize);	// Bottom Left Of The Quad (Bottom)
                Gl.glVertex3f(cubeSize, -cubeSize, -cubeSize);	// Bottom Right Of The Quad (Bottom)
            
                Gl.glColor3f(1.0f, 0.0f, 0.0f);	// Color Red	
                Gl.glVertex3f(cubeSize, cubeSize, cubeSize);	// Top Right Of The Quad (Front)
                Gl.glVertex3f(-cubeSize, cubeSize, cubeSize);	// Top Left Of The Quad (Front)
                Gl.glVertex3f(-cubeSize, -cubeSize, cubeSize);	// Bottom Left Of The Quad (Front)
                Gl.glVertex3f(cubeSize, -cubeSize, cubeSize);	// Bottom Right Of The Quad (Front)
            
                Gl.glColor3f(1.0f, 1.0f, 0.0f);	// Color Yellow
                Gl.glVertex3f(cubeSize, -cubeSize, -cubeSize);	// Top Right Of The Quad (Back)
                Gl.glVertex3f(-cubeSize, -cubeSize, -cubeSize);	// Top Left Of The Quad (Back)
                Gl.glVertex3f(-cubeSize, cubeSize, -cubeSize);	// Bottom Left Of The Quad (Back)
                Gl.glVertex3f(cubeSize, cubeSize, -cubeSize);	// Bottom Right Of The Quad (Back)
            
                Gl.glColor3f(0.0f, 0.0f, 1.0f);	// Color Blue
                Gl.glVertex3f(-cubeSize, cubeSize, cubeSize);	// Top Right Of The Quad (Left)
                Gl.glVertex3f(-cubeSize, cubeSize, -cubeSize);	// Top Left Of The Quad (Left)
                Gl.glVertex3f(-cubeSize, -cubeSize, -cubeSize);	// Bottom Left Of The Quad (Left)
                Gl.glVertex3f(-cubeSize, -cubeSize, cubeSize);	// Bottom Right Of The Quad (Left)

                Gl.glColor3f(1.0f, 0.0f, 1.0f);	// Color Violet
                Gl.glVertex3f(cubeSize, cubeSize, -cubeSize);	// Top Right Of The Quad (Right)
                Gl.glVertex3f(cubeSize, cubeSize, cubeSize);	// Top Left Of The Quad (Right)
                Gl.glVertex3f(cubeSize, -cubeSize, cubeSize);	// Bottom Left Of The Quad (Right)
                Gl.glVertex3f(cubeSize, -cubeSize, -cubeSize);	// Bottom Right Of The Quad (Right)
            Gl.glEnd();			// End Drawing The Cube

            Gl.glPopMatrix();
            Gl.glFlush(); 
         
        }

În începutul functie stergem toata scena apelând metoda glClear() si salvam matricea curenta de transformare prin apelul metodei glPush(). În continuare vom realiza rotatiile dupa cele 3 axe în functie de unghiul calculat în momentul respectiv dupa care vom desena cubul. Se oberva ca pentru a realiza o rotatie dupa axa x, trebuie ca vectorul dupa care se face rotatia sa fie situat pe axa x, adica sa fie vectorul cu coordonate (1.0f,0.0f,0.0f), pentru axa y vectorul de rotatie este (0.0f,1.0f,0.0f) iar pentru axa z avem (0.0f,0.0f,1.0f). Dupa ce am facut desenarile restauram matricea anterior activa prin apelul glPopMatrix() si fortam efectuarea tutoror apelurilor de desenare prin apelul functiei glFlush(). Rezultatul obtinut ar trebuie sa arate ca cel din figura de mai jos :

Imagine:RezultatFinal.png

Se observa ca folosind acest control pot fi create foarte usor aplicatii cu grafica 3D. Daca nu am fi folosit acest control ar fi trebuit ca noi sa facem initializarea pentru OpenGl, sa ne declaram noi functii de callback pentru evenimentele de tastatura si sa ne folosim de funtii ajutatoare din glut pentru a realiza animatia. Astfel putem folosi unelte de programare (evenimente si handler-uri de evenimente) deja existente si pe care le cunoastem pentru a realiza o aplicatie de acest fel singurule dezajantaje fiind poate o viteza de executie putin mai mica si faptul ca nu putem trece la modul fullscreen.

Alternative

  • C# OpenGL Framework este o aplicatie de grafica 3D scrisa in intregime in C#. Ofera suport pentru toate functiile standard oferite de produsele comerciale. Este o mare diferenta intre acesta si TAO. TAO face legatura intre functiile din OpenGl, mapand functiile din opengl32.dll si limbajul C#. C# OpenGL Framework ofera suport pentru o larga serie de comenzi standard 3D deja implementate precum : ZPR, Imaging, export DXF, IGES etc. O lista cu toate facilitatile poate fi gasita aici
  • C# Graphics library - un proiect open source dar a carei dezvoltare a fost oprita.

Referinte

Unelte personale
în prim plan