Dreidimensionale Grafiken und Animationen mit Three.js

Three.js ist eine JavaScript Bibliothek die uns hilft 3D-Grafiken ohne zusätzliche Programme im Browser darzustellen.

Diese 3D-Grafiken können auch animiert werden. Möglich ist das Ganze über die Programmierschnittstelle WebGl (Web Graphic Library).


Bestandteile der Grafik

Um eine Grafik darstellen zu können, müssen wir folgende Programmbestandteile einbeziehen.

Programmbestandteile für 3-D Grafiken
  • Zeichenfläche in HTML
  • Grafikszene zum Anordnen der Objekte
  • Beliebig viele 3D-Objekte
  • Eine Kamera die das Bild einfängt
  • Eine Lichtquelle zur Beleuchtung

Der 3D Raum

Um Objekte in 3D darstellen zu können, benötigen wir auch einen dreidimensionalen Raum. Jeder Punkt dieses Raumes kann durch seine X,Y und Z-Koordinaten erreicht werden. Die Punkte x=0, y=0 und z=0 bilden den Mittelpunkt des Raumes oder vom Bildschirm.


Die X-Achse verläuft von links nach rechts. Links von der Mitte werden die Werte negativ beziffert, rechts von der Mitte sind die Werte positiv.


Die Y-Achse verläuft senkrecht durch den Raum, der Teil oberhalb der Mitte wird mit positiven Werten beschrieben und der untere Teil der Achse wird dagegen negativ beschrieben.


Die Z-Achse verläuft sozusagen vom Betrachter weg. Beim Betrachter wird der Wert positiv angegeben und umso weiter weg der Punkt vom Betrachter weg ist, wird der Punkt mit negativen Werten angegeben.


Praktischer Teil

Um etwas mit der Bibliothek Three.js zu experimentieren, stelle ich die Datei "three.min.js" aus der Revision 121 zum Kopieren bereit. Einfach den Text der nachfolgenden Datei kopieren und unter den Namen: "three.min.js" im Projektordner abspeichern.

Das gesamte Projekt und auch die three.min.js-Datei gibt es unter: https://threejs.org

Zur Einführung erstellen wir einen Würfel, der im Webbrowser erscheinen soll. Dazu importieren wir die "three.min.js" Datei aus demselben Verzeichnis wie unsere "main.js" (Projektordner)

three.min.js importieren

<body>
  ... 
 <script src="three.min.js"></script>
 <script src="main.js"></script>
</body>
Reihenfolge beachten

Wir laden zuerst die Datei "three.min.js" und danach die Datei "main.js"

Der Browser muss zuerst die Bibliothek laden, bevor wir darauf zugreifen können.


Die Zeichenfläche

Als Erstes erstellen wir eine Zeichenfläche, in der unsere Szene gerendert werden kann. Entweder als externe Datei, die nach der "three.min.js" geladen wird (main.js) oder einfach innerhalb der <script>-Markups in HTML

Zeichenfläche erstellen

let zeichnung = new THREE.WebGLRenderer();

Mit der Methode "setSize()" bestimmen wir die Größe der Zeichenfläche.

Zeichenfläche dimensionieren

zeichnung.setSize(window.innerWidth, window.innerHeight);

Mit der Methode "setClearColor()" bestimmen wir die Hintergrundfarbe.

Hintergrundfarbe generieren

zeichnung.setClearColor(0x8DEEEE);

Wenn keine gültige Farbe angegeben wird, dann wird schwarz als Standard Hintergrundfarbe verwendet. Die Eigenschaft "domElement" fügt den Zeichenbereich zum Dokument hinzu.

Zeichenbereich hinzufügen

document.body.appendChild(zeichnung.domElement);

Das ThreeJs-Object vom Typ "Scene" stellt die Grafikszene bereit, in der die Objekte angeordnet werden.

Grafikszene bereitstellen

let szene = new THREE.Scene();

Das 3D Objekt mit Geometrie und Material

Jedes 3D-Objekt besteht aus einer Form, die als Geometrie bezeichnet wird.

Diese Geometrie besteht aus vielen einzelnen Dreiecksflächen. Die Bibliothek Three.js stellt viele Formen wie Kugeln, Quader, Kegel usw. als Geometrie zur Verfügung, damit wird viel Arbeit abgenommen. Einen Quader erstellen wir mit dem ThreeJs-Objekt BoxGeometrie. Dazu übergeben wir die 3 Parameter, in der sich unser Quader über die X,Y und Z-Achse erstrecken soll.

Würfel erstellen

let geometrie = new THREE.BoxGeometry(200, 200, 200);

Wenn alle 3 Achsen den gleichen Wert erhalten, dann erhalten wir einen Würfel, bei dem bekanntlich alle 3 Achsen die gleiche Größe haben. Die Objekte haben neben der Geometrie auch eine Oberfläche, die aus verschiedenen Materialien bestehen und somit auch unterschiedlich auf Lichteinflüsse reagieren. Das ThreeJs-Object "MeshLambertMaterial" steht für eine eher matte Oberfläche, auf der wir je nach Lichteinfall gut den räumlichen Eindruck vermittelt bekommen.

Material erzeugen

let material = new THREE.MeshLambertMaterial( {color:0xEE00EE} );

Mit dem ThreeJs-Objekt "Mesh" erzeugen wir das gewünschte geometrische Objekt mit der gewünschten Oberfläche.

Objekt erzeugen

let objekt = new THREE.Mesh(geometrie, material);

Die Methode "add" fügt das 3D-Objekt zur Szene hinzu. Erst dann kann das Objekt dargestellt werden.

Objekt erzeugen

szene.add(objekt);

Die Kamera

Es gibt verschiedene Kameratypen. Das ThreeJS-Objekt "PerspectiveCamera" ist eine Kamera, die einen perspektivischen Blick auf die Szene wirft. Die Kamera steht auf der Spitze einer Pyramide mit einer rechteckigen Grundfläche.

Kameramann zeigt die Anzeigefläche von Grafiken

Die Kamera liegt hier in der Hand des Kameramännchens und die Grundfläche ist die grüne Fläche auf der linken Seite. Nur die Sachen, die innerhalb der Pyramide sind, können dargestellt werden.

Kamera erzeugen

let kamera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 100000);

Der Konstruktor von PerspectiveCamera benötigt 4 Parameter. Der erste Parameter ist "fov" (field of view) durch ihn wird der Sichtwinkel in Grad angegeben. Der zweite Parameter ist "aspect" (aspect ration) er beschreibt das Seitenverhältnis von Höhe und Grundfläche der Pyramide. Das Seitenverhältnis vom Browserfenster eignet sich dafür gut. Der dritte Parameter ist "near" (near plane), das ist die nahe Ebene, alles was vor dieser Ebene (gelbe Fläche) liegt, wird nicht angezeigt. Der vierte Parameter ist "far" (far plane), das ist die ferne Ebene, alles was hinter dieser Ebene liegt wird nicht mit angezeigt. Mit dem ThreeJs-Objekt "position" bestimmen wir die Position der Kamera vom Nullpunkt oder Mittelpunkt aus über die X,Y und Z-Achse.

Kamera positionieren

kamera.position.set(300, 350, 500);

Die Kamera würde einfach geradeaus schauen, wir können aber mit der Methode "lookAt()" sagen, dass die Blickrichtung zum Nullpunkt geht.

Kamera positionieren

kamera.lookAt(objekt.position);

Lichtquelle und Darstellung

Eine von mehreren Lichtquellen ist das ThreeJs-Objekt "PointLight", es stellt eine Lichtquelle dar, die wie eine Glühlampe in alle Richtungen ihr weißes Licht ausstrahlt.

Licht erzeugen

let licht = new THREE.PointLight();

Eine Positionierung in der Nähe der Kamera ist sicherlich die beste Entscheidung. Wenn die Lichtquelle hinter dem Objekt wäre, wird das Objekt als schwarz dargestellt, weil es im Schatten steht und kein Licht auf der Oberfläche reflektieren kann.

Licht positionieren

licht.position.set(450, 350, 250);

Mit der Methode "add()" wird das Licht zur Szene hinzugefügt, um Auswirkungen auf das Objekt zu haben.

Licht zur Szene hinzufügen

szene.add(licht);

Zu guter Letzt wird die Grafikszene gemeinsam mit der Kamera gerendert.

Gesamte Szene rendern

zeichnung.render(szene, kamera);

Nun erscheint ein Würfel, der schräg angeleuchtet wird und in etwa so wie in der Abbildung aussehen sollte.

Würfel wird im Browser dargestellt
Gesamter Code für die Ausgabe

<body>
  ... 
 <script src="three.min.js"></script>
 <script>
  let zeichnung = new THREE.WebGLRenderer();
  zeichnung.setSize(window.innerWidth, window.innerHeight);
  zeichnung.setClearColor(0x8DEEEE);
  document.body.appendChild(zeichnung.domElement);
  let szene = new THREE.Scene();
  let geometrie = new THREE.BoxGeometry(200, 200, 200);
  let material = new THREE.MeshLambertMaterial( {color:0xEE00EE} );
  let objekt = new THREE.Mesh(geometrie, material);
  szene.add(objekt);
  let kamera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 100000);
  kamera.position.set(300, 350, 500);
  kamera.lookAt(objekt.position);
  let licht = new THREE.PointLight();
  licht.position.set(450, 350, 250);
  szene.add(licht);
  zeichnung.render(szene, kamera);
 </script>
</body>

Animation

Um den Würfel in eine Drehbewegung zu versetzen, müssen wir den Code leicht verändern. Als Erstes entfernen wir die Zeile: "zeichnung.render(szene, kamera);" weil wir das rendern in der neuen Funktion mit Einbauen.

Animationsfunktion erstellen

function drehen() {
 ...
}

Die nachfolgenden Anweisungen werden innerhalb der Funktion "drehen" eingefügt!

Über das ThreeJS-Object "rotation" haben wir Zugriff auf den Drehwinkel des Objekts

Drehwinkel einstellen

objekt.rotation.y += 0.4 * Math.PI / 180;

Mit der Eigenschaft x,y oder z bestimmen wir die Achse, über die wir das Objekt bewegen möchten. Der Drehwinkel muss im Bogenmaß angegeben werden, also muss ein Winkel in Grad mit dem Faktor π/180 multipliziert werden. Der Wert des Drehwinkels wird um 0.4 grad erhöht. Bei 60 Bildern pro Sekunde dreht sich der Würfel dann also um 24 Grad in einer Sekunde. Dazu müssen wir aber erst die Szene neu rendern.

Szene hinzufügen

zeichnung.render(szene, kamera);

Die Methode "requestAnimationFrame" sorgt für einen weichen Übergang zwischen den Frames und ruft wiederum die Funktion "drehen" auf.

flüssige Animation

requestAnimationFrame(drehen);

In der Funktion wird der Würfel erneut um 0.4 Grad gedreht und das Bild wird neu gerendert usw. Um dahin zu gelangen, muss die Funktion drehen, aber einmalig aufgerufen werden.

flüssige Animation

drehen();

Hier noch mal der Quelltext in der Gesamtübersicht mit der Animation des drehenden Würfels.

Gesamter Code für die Ausgabe mit Animation

<body>
  ... 
 <script src="three.min.js"></script>
 <script>
  let zeichnung = new THREE.WebGLRenderer();
  zeichnung.setSize(window.innerWidth, window.innerHeight);
  zeichnung.setClearColor(0x8DEEEE);
  document.body.appendChild(zeichnung.domElement);
  let szene = new THREE.Scene();
  let geometrie = new THREE.BoxGeometry(200, 200, 200);
  let material = new THREE.MeshLambertMaterial( {color:0xEE00EE} );
  let objekt = new THREE.Mesh(geometrie, material);
  szene.add(objekt);
  let kamera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 100000);
  kamera.position.set(300, 350, 500);
  kamera.lookAt(objekt.position);
  let licht = new THREE.PointLight();
  licht.position.set(450, 350, 250);
  szene.add(licht);
  function drehen() {
   objekt.rotation.y += 0.4 * Math.PI / 180;
   zeichnung.render(szene, kamera);
   requestAnimationFrame(drehen);
  }
  drehen();
 </script>
</body>

Die Animation können wir einfach so steuern, dass der Würfel nur eine halbe Drehung macht und die Animation danach stoppt. Dazu schreiben wir zwischen der Zeile: "zeichnung.render(szene, kamera);" und der Zeile: "requestAnimationFrame(drehen);" einfach die Bedingung: "if (objekt.rotation.y <= 180 * Math.PI / 180)". Dadurch wird die "requestAnimationFrame" nur ausgeführt, wenn die Bedingung der "if-Verzweigung" "true" ist, also stoppt die Animation nach einer Drehung von 180 Grad auf der Y-Achse.

Stopp nach halber drehung

function drehen() {
 objekt.rotation.y += 0.4 * Math.PI / 180;
 zeichnung.render(szene, kamera);
 if (objekt.rotation.y <= 180 * Math.PI / 180)
 requestAnimationFrame(drehen);
}
drehen();

Weiteres

Es können mehrere Objekte über den gleichen Code erzeugt und an verschiedenen Stellen positioniert werden. Zudem gibt es noch weitere Formen wie Kegel, Kugel oder Torus, die wir abbilden können. Überhaupt können wir uns mit der Kamera durch den Raum bewegen und damit in einer 3D Welt eintauchen.


Weiter mit API (Application Programming Interface)