Computergrafik

Szenengraph Traversierung

Szenengraph Basisklasse | | Szenengraph Klassengraph

Wie wird nun aber insbesondere dieser Szenengraph-Traversal implementiert, so dass er für die jeweiligen traversierten Geometrie-Knoten die jeweils passende Matrix “ausspuckt”?

Er muss dazu während der Traversierung die die passende Abfolge an Transformationsänderungen und Render-Calls veranlassen:

Erklärung in 6 Schritten:

1) Zuersteinmal hat die Basisklasse lgl_Node aller Knoten als Instanzvariable ein dynamisches Array, indem die hinzugefügten Kinder gespeichert sind. Das dynamische Array wurde hierbei als std::vector in der Basisklasse aggregiert:

std::vector<lgl_Node*> children_;

2) Die Basisklasse enthält weiterhin eine virtuelle Methode render(), welche sich rekursiv für alle Kinder aufruft, was einer Tiefensuche entspricht:

virtual void render()
{
   for (unsigned int i=0; i<children_.size(); i++)
      children_[i]->render();
}

3) Ein Geometrie-Knoten (lgl_GeometryNode), der von der Basisklasse abgeleitet ist, rendert nun bei der obigen Traversierung die gekapselten und als Instanzvariable gespeicherten VBOs, indem er die virtuelle render() Methode spezialisiert:

class lgl_GeometryNode: public lgl_Node
{
public:

   //! ctor
   lgl_GeometryNode(lglVBO *vbo = NULL)
      : lgl_Node(),
        vbo_(vbo)
   {}

   //! render all nodes via depth first traversal
   virtual void render()
   {
      if (vbo_)
      {
         vbo_->lglRender(); // render vbo instance
      }

      lgl_Node::render(); // render all children by calling the base class
   }

protected:

   lglVBO *vbo_;
};

Geometrie, welche einer konkrete Form haben soll, wie z.B. lglCube oder lglSphere sind wiederum von lglVBO abgeleitet und konstruieren im Konstruktor die konkrete jeweilige Form des VBOs (siehe dazu hier). Dadurch kann jede beliebige Form als VBO in einem Geometrie-Knoten des Szenengraphen gespeichert werden.

C++ Recap: Wäre die render() Methode nicht virtuell, so würde in abgeleiteten Klassen nicht die tatsächliche Methode sondern die Methode der Basis-Klasse aufgerufen werden!

4) Ein Farb-Knoten (lgl_ColorNode), der wiederum von der Basisklasse abgeleitet ist, rendert nun farbige VBOs, indem er in der virtuellen render() Methode vorher die jeweilige Farbe spezifiziert, welche in einer Instanzvariable gespeichert wurde:

class lgl_ColorNode: public lgl_Node
{
public:

   //! ctor
   lgl_ColorNode(const vec4 &color)
      : lgl_Node(),
        color_(color)
   {}

   //! render colored geometry via depth first traversal
   virtual void render()
   {
      lglColor(color_); // specify rendering color

      lgl_Node::render(); // render all children by calling the base class
   }

protected:

   vec4 color_;
};

5) Die dazu passenden Transformationen werden dadurch berechnet, dass ein Transformations-Knoten (lgl_TransformationNode) wiederum die virtuelle render() Methode spezialisiert und die als Instanzvariable gespeicherte lokale Transformationsmatrix M anwendet:

class lgl_TransformatioNode: public lgl_Node
{
public:

   //! ctor
   lgl_TransformationNode(const mat4 &M = mat4())
      : lgl_Node(),
        M_(M)
   {}

   //! apply transformation of all nodes via depth first traversal
   virtual void render()
   {
      lglPushMatrix(); // remember actual transformation
      lglMultMatrix(M_); // apply local transformation

      lgl_Node::render(); // render all children by calling the base class

      lglPopMatrix(); // restore the previous transformation
   }

protected:

   mat4 M_:
};

6) Ein Knoten, der z.B. eine Translation oder Rotation repräsentiert, spezialisiert wiederum den obigen Transformations Knoten (lgl_TransformationNode) durch Ableiten:

class lgl_TranslationNode: public lgl_TransformationNode
{
public:

   //! ctor
   lgl_TranslationNode(const vec3 &v)
      : lgl_TransformationNode(mat4::translate(v))
   {}

};
class lgl_RotationNode: public lgl_TransformationNode
{
public:

   //! ctor
   lgl_RotationNode(double angle, const vec3 &axis)
      : lgl_TransformationNode(mat4::rotate(angle, axis))
   {}

};

Selbiges gilt für Kamera Knoten (lgl_CameraNode), Animations-Knoten (lgl_RotationAnimationNode) usw.

Die hier skizzierte Implementierung eines Szenengraphen ist vom Prinzip her auf alle Animations-Tools wie Blender oder Game-Engines wie Unreal, Unity oder Godot, die alle auf die eine oder andere Weise ebenfalls das Szenengraph-Prinzip einsetzen, übertragbar!

qed!

Szenengraph Basisklasse | | Szenengraph Klassengraph

Options: