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.
- 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)
<body>
...
<script src="three.min.js"></script>
<script src="main.js"></script>
</body>
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
let zeichnung = new THREE.WebGLRenderer();
Mit der Methode "setSize()
" bestimmen wir die Größe der Zeichenfläche.
zeichnung.setSize(window.innerWidth, window.innerHeight);
Mit der Methode "setClearColor()
" bestimmen wir die Hintergrundfarbe.
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.
document.body.appendChild(zeichnung.domElement);
Das ThreeJs-Object vom Typ "Scene
" stellt die Grafikszene bereit, in der die Objekte angeordnet werden.
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.
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.
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.
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.
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.
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.
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.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.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.
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.position.set(450, 350, 250);
Mit der Methode "add()
" wird das Licht zur Szene hinzugefügt, um Auswirkungen auf das Objekt zu haben.
szene.add(licht);
Zu guter Letzt wird die Grafikszene gemeinsam mit der Kamera gerendert.
zeichnung.render(szene, kamera);
Nun erscheint ein Würfel, der schräg angeleuchtet wird und in etwa so wie in der Abbildung aussehen sollte.
<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.
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
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.
zeichnung.render(szene, kamera);
Die Methode "requestAnimationFrame
" sorgt für einen weichen Übergang zwischen den Frames und ruft wiederum die Funktion "drehen" auf.
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.
drehen();
Hier noch mal der Quelltext in der Gesamtübersicht mit der Animation des drehenden Würfels.
<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.
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)