Szenegraph

In WebGL gibt es keine eingebaute Möglichkeit für Szenegraphen. Daher müssen wir uns eigene Hilfsfunktionen schreiben, um einen Szenegraphen darzustellen.

Struktur erstellen

Zuerst benötigen wir eine Datenstruktur, mit deren Hilfe wir den Szenegraphen aufbauen können. Dazu definieren wir einen Knoten:

var Node = function() {
  this.children = [];
  this.localMatrix = math.eye(4);
  this.worldMatrix = math.eye(4);
};

Nun benötigen wir eine Funktion, mit der man den Parent setzen kann:

Node.prototype.setParent = function(parent) {
  if (this.parent) {
    var i = this.parent.children.indexOf(this);

    if (i >= 0) {
      this.parent.children.splice(i, 1);
    }
  }

  if (parent) {
    parent.children.append(this);
  }

  this.parent = parent;
};

Jetzt brauchen wir die Möglichkeit, die Weltmatrix anhand der Lokalmatrizen der Kinder zu aktualisieren:

Node.prototype.updateWorldMatrix = function(parentWorldMatrix) {
  if (parentWorldMatrix) {
    this.worldMatrix = math.multiply(this.localMatrix, parentWorldMatrix);
  } else {
    this.localMatrix = this.worldMatrix;
  }

  var worldMatrix = this.worldMatrix;
  this.children.forEach(function(child) {
    child.updateWorldMatrix(worldMatrix);
  });
};

Beispiel

Als einfaches Beispiel nehmen wir uns ein Rechteck, welches um 45 Grad rotiert und an zwei unterschiedlichen Positionen verschoben wird. Dazu:

// Ursprungsrechteck mit Rotation
var rectangle0 = new Node();
rectangle0.localMatrix = math.multiply(math.matrix([
    [math.cos(math.unit(45, 'deg')), -math.sin(math.unit(45, 'deg')), 0.0, 0.0],
    [math.sin(math.unit(45, 'deg')),  math.cos(math.unit(45, 'deg')), 0.0, 0.0],
    [0.0, 0.0, 1.0, 0.0],
    [0.0, 0.0, 0.0, 1.0]
]), rectangle0.localMatrix);

// Rechteck 3 Einheiten vom Ursprungsrechteck entfernt
var rectangle1 = new Node();
rectangle1.localMatrix = math.multiply(math.matrix([
    [1.0, 0.0, 0.0, 3.0],
    [0.0, 1.0, 0.0, 0.0],
    [0.0, 0.0, 1.0, 0.0],
    [0.0, 0.0, 0.0, 1.0]
]), rectangle1.localMatrix);

// Rechteck 1 Einheit vom Ursprungsrechteck entfernt
var rectangle2 = new Node();
rectangle2.localMatrix = math.multiply(math.matrix([
    [1.0, 0.0, 0.0, 1.0],
    [0.0, 1.0, 0.0, 0.0],
    [0.0, 0.0, 1.0, 0.0],
    [0.0, 0.0, 0.0, 1.0]
]), rectangle2.localMatrix);

rectangle1.setParent(rectangle0);
rectangle2.setParent(rectangle0);

rectangle0.updateWorldMatrix();

Nun befinden sich in den Rechtecken die jeweiligen Weltmatrizen, die man dann später an den Shader übergibt, wie im vorherigen Tutorial beschrieben.