OpenGL.org 3Dsource.de: Infos rund um 3D-Computergrafik, OpenGL und VRML
space Telefon-Tarife space VDI - Arbeitskreis Fahrzeugtechnik space Beschallungsanlagen und Elektroakustik space Heise-News space

4 GLU

4.010 Was ist GLU ? Wo ist der Unterschied zu OpenGL ?

Man kann sich die OpenGL Basisbibliothek als Low Level 3D Grafikbibliothek vorstellen (prozedural, als Grundkörper sind nur Punkte und Linien bekannt). Darauf aufsetzend bildet die GLU den ersten Schritt zum deskritiven Grafiksystem, da hiermit schon komplexere Grundkörper definiert werden (Kugel, Zylinder etc.). Einige Funktionen der GLU sind:

  • Skalierung von 2D Bildern und automatisches Anlegen von MipMaps (Texturen werden in verschiedenen Auflösungen generiert),
  • Umrechnung von Modell- in Bildschirmkoordinaten bzw. umgekehrt,
  • NURBS-Oberflächen,
  • Tesselation (Umwandlung konkaver Flächen in konvexe, von der OpenGL darstellbare Flächen),
  • Leichter handhabbare Funktionen für Zentral- und Paralleltransformation, Kamerapositionierung und Objektselektierung,
  • Kugeln, Zylinder und Scheiben als einfach nutzbare Grundkörper,
  • Umwandlung der OpenGL Fehlermeldungen in ASCII Text,

Die besten Informationen über die GLU bieten das Red Book und Blue Book sowie die GLU Spezifikation ( auf der OpenGL Webseite).

4.020 Wie zeichnet die GLU Kugeln, Zylinder und Scheiben ?

Eigentlich gibt es hierbei keine Besonderheiten. Man kann die GLU Körper auch problemlos selbst nachbilden. Z. B. im Mesa Source Code kann die dort gefundene Lösung nachvollzogen werden.

Die GLU nutzt die Grundfunktionen der OpenGL, wie Triangle oder Quad Strips, um ihre Körper darzustellen. Die endgültige Form und der damit verbundene Aufwand kann über Parameter eingestellt werden, die interne Berechung der Punkte erfolgt dann über sinf() und cosf() der Mathebibliothek.

Um Zylinder oder offene Rohre zu zeichnen, kann auch die GLE Bibliothek als Hilfe genutzt werden. Sie ist Teil der GLUT Distribution.

4.030 Wie arbeitet gluPickMatrix() ?

Diese Funktion rechnet über Translate und Scale die selektierte Region so um, dass diese den gesamten Viewport ausfüllt.

    gluPickMatrix(centerX, centerY, breite, hoehe, viewport);

Wird die Funktion auf den Projektionsstack ( glMatrixMode(GL_PROJECTION); ) angewendet und vor dem Projektionsbefehl ( gluPerspective(), glFrustum() ) aufgerufen, wird das aktuelle Viewing Volumen auf den selektierten Bereich festgelegt. In diesem Fall wird jedes Objekt, das auch nur teilweise in den selektierten Bereich fällt, als innerhalb des Viewing Volumens angesehen. Mit glRenderMode(GL_SELECT) werden diese Objekte an den Aufrufer zurückgegeben.

4.040 Wie kann ich die Funktionen der Tesselation nutzen ?

Die GLU bietet Funktionen, um konkave Polygone (die Verbindungslinien zweier beliebiger Eckpunkte schneiden eine andere Verbindungslinie) und Polygone mit einem inneren Hohlraum richtig darzustellen. Die Grundfunktionen der OpenGL benötigen in jedem Fall konvexe Polygone. Die Tesselation Funktion der GLU bricht diese Polygone so auf, dass sie korrekt durch die OpenGL dargestellt werden können (hinzufügen von Punkten, Aufsplitten in mehrere Polygone etc.). Dies geschieht, indem man Tesselation Callbacks für die entsprechenden Primitive festlegt, die automatisch aufgerufen werden und die vereinfachten Daten an die OpenGL übergeben. Im Quellcode selbst werden normale OpenGL Funktionen zum Zeichnen genutzt.

Ein Beispielprogramm findet man im GLUT Quellcode unter progs/redbook/tess.c.

Das Vorgehen zum Nutzen der Tesselation ist wie folgt:

  1. neues GLU Tesselation Objekt anlegen
          GLUtesselator *tess = gluNewTess();
  2. Callbacks festlegen
          gluTessCallback (tess, GLU_TESS_BEGIN, tcbBegin);
          gluTessCallback (tess, GLU_TESS_VERTEX, tcbVertex);
          gluTessCallback (tess, GLU_TESS_END, tcbEnd);
    Falls sich Verbindungslinien des Polygons schneiden, muss auch ein Callback zum Erzeugen zusätzlicher Eckpunkte festgelegt werden
          gluTessCallback (tess, GLU_TESS_COMBINE, tcbCombine);
  3. Das Senden der Daten an die GLU realisiert man so:
          // im Vorfeld angelegt und mit passenden Daten versehen wurde dieses Feld:
          // GLdouble data[numVerts][3];
          gluTessBeginPolygon (tess, NULL);
          gluTessBeginContour (tess);
          for (i=0; i<sizeof(data)/(sizeof(GLdouble)*3);i++)
            gluTessVertex (tess, data[i], data[i]);
          gluTessEndContour (tess);
          gluEndPolygon (tess);
  4. In den Callbacks sind folgende OpenGL Aufrufe vorzunehmen:
          void tcbBegin (GLenum prim);
          {
            glBegin (prim);
          }
    
          void tcbVertex (void *data)
          {
            glVertex3dv ((GLdouble *)data);
          }
    
          void tcbEnd ();
          {
            glEnd ();
          }
    
          void tcbCombine (GLdouble c[3], void *d[4], GLfloat w[4], void **out)
          {
            GLdouble *nv = (GLdouble *) malloc(sizeof(GLdouble)*3);
    
            nv[0] = c[0];
            nv[1] = c[1];
            nv[2] = c[2];
            *out = nv; 
          }

Die vorangegangene Liste stellt nur ein vereinfachtes Beispiel dar, demonstriert also noch nicht den vollen Funktionsumfang der OpenGL. Durch die Übergabe der Daten an gluTessBeginPolygon() und gluTessVertex() sowie die Festlegung der Callbacks kann das Programm diese Polygone wie jedes andere behandeln, also auch Farben und Texturen wie üblich nutzen.

4.041 Wie kann ich die Speicherverwaltung optimieren ?

Der nachfolgende Code stammt von Andre Gommlich, Forschungszentrum Rossendorf, ist also nicht Teil der originalen FAQ.

Er ist bei seiner Programmiertätigkeit auf ein Problem bei der Nutzung der GLU Tesselation gestoßen. Beim Rendern größerer Strukturen mit konkave Polygonen (~2000 Stück pro Renderpass) kamen schnell große Mengen herrenlosen Speichers zusammen. <Zitat> Alle fordern in der GLU_TESS_COMBINE Callbackfunktion munter Speicher an und keiner denkt daran diesen wieder freizugeben. Ich habe mir für meinen Fall nun einen kleinen Manager geschrieben, der die Sache jetzt gut regelt. Hier der Code (</Zitat>):

//Pointer Manager Klasse
template <class type>
class CPointerManager
{
 private:
  int   m_Size;
  int   m_Elements;
  int   m_Counter;
  type*   m_Array;
 public:
  CPointerManager()  
  {
    m_Array=NULL;
    m_Elements=1;
    m_Size=m_Counter=0;
  };

  ~CPointerManager()
  {
    if(m_Array) delete[] m_Array;
  };

  type* AllocMemory(int size=1)
  {
    if(m_Array) delete[] m_Array;
    if(size<1||!(m_Array=new type[m_Elements*size])) return NULL;
    else
    {
      m_Size=size;
      m_Counter=0;
      return m_Array;
    }
  };

  void SetNumOfElements(int elements)
  {
    if(m_Array||elements<1) return;
    else m_Elements=elements;
    if(m_Array) delete[] m_Array;
    m_Array=NULL;
  };
  
  void SetToBegin()
  {
    m_Counter=0;
  };

  type* GetPointer()
  {
    type* p;
    if(!m_Array) return NULL;
    if(m_Counter>=m_Size)
    {
      if(!(p=new type[m_Elements*m_Size*2])) return NULL;
      memcpy(p,m_Array,m_Elements*m_Size);
      delete[] m_Array;
      m_Array=p;
      m_Size*=2;
    }
    p=&m_Array[m_Counter*m_Elements];
    m_Counter++;
    return p;
  };
};

//Globale Variable
CPointerManager <float> pmanager;
//Initialisierung
pmanager.SetNumOfElements(3);
pmanager.AllocMemory(2000);

//Tesselation Callback Combine
void CALLBACK TessCallCombine(GLdouble coords[3],
                                            void* vertex_data[4],
                                            GLfloat weight[4],
                                            void** outData)
{
  float* vert;
  if(!(vert=pmanager.GetPointer())
  {
    //Speicherfehler
  }
  vert[0]=(float)coords[0];
  vert[1]=(float)coords[1];
  vert[2]=(float)coords[2];
  *outData=vert;
}

//Renderfunktion
void Render()
{
  pmanager.SetToBegin();
  // your code
  return;
};

Das Formatieren von Quellcode aus einer E-Mail ist nicht wirklich komfortabel... Hier deshalb als Code.

4.050 Warum werden meine Tesselation-Callbacks nicht aufgerufen ?

Normalerweise werden die Tesselation Callbacks nach gluEndPolygon() ausgeführt. Falls das nicht funktioniert, ist irgendwo ein Fehler aufgetreten. Eine typische Ursache liegt im Fehlen vom GLU_TESS_COMBINE* Callback, der für die Bearbeitung konkaver (self-intersecting) Polygone notwendig ist.

Es kann auch ein Callback für GLU_TESS_ERROR definiert werden, der auftretende Fehler meldet.

4.060 Wie kann ich die NURBS-Funktionen nutzen ?

Die GLU NURBS Schnittstelle konvertiert die B-Spline Basiskontrollpunkte in Kontrollpunkte für Bezierkurven und übergibt diese an die OpenGL Evaluator Funktionen. Diese zeichnen dann die Oberfläche.

Ein Beispielprogramm ist z.B. in der GLUT Distribution unter progs/redbook/surface.c verfügbar.

4.070 Wie arbeite ich mit gluProject und gluUnProject ?

Beide Funktionen nutzt die ModelView- und Projektionsmatrix und den OpenGL Viewport als vorzugebende Parameter.

    gluProject(objX, objY, objZ,
               modelMatrix, projMatrix, viewport,
               &winX, &winY, &winZ);

    gluUnProject(winX, winY, winZ,
                 modelMatrix, projMatrix, viewport,
                 &objX, &objY, &objZ);

gluProject() benönigt darüber hinaus auch eine XYZ-Koordinate des Modellkoordinatensystems (objX, objY, objZ), aus denen die XYZ-Bildschirmkoordinate berechnet und zurückgegeben wird (winX, winY, winZ).

gluUnProject() geht den umgekehrten Weg. Aus der übergebenen XYZ-Bildschirmkoordinate wird die entsprechende Modellkoordinate berechnet und zurückgegeben.

Das Konzept der Z-Bildschirmkoordinate ist auf den ersten Blick etwas verwirrend, da der Monitor ja ein nur zweidimensionales Ausgabegerät ist. Die Z-Koordinate stellt hier aber den Wert des Tiefenbuffers als GLdouble (zwischen 0.0 - 1.0) dar, zeigt also die Lage des Objektes relativ zum Viewport. Hat man mit glDepthRange() den Tiefenbuffer nicht beeinflusst, entspricht ein Z-Wert von 0.0 der Lage des Objekts am vorderen Rand des Viewports (zNear) bzw. ein Wert von 1.0 den hinteren Rand (zFar). Mit glReadPixels() lässt sich auch der Z-Wert an jeder beliebigen Koordinate ermitteln.

Seite durchsuchen nach:

Fragen oder Ideen an:
Thomas.Kern@3Dsource.de
Linux-Counter
(C) Thomas Kern