Wenn es um Echtzeit-3D-Grafik im Browser geht, ist Three.js die erste Bibliothek, die einem in den Sinn kommt; in diesem Artikel bauen wir die Logik der Three.js-Animation von Grund auf, um eine lebendige Szene zu erstellen, die sich dreht, Licht einfängt und auf Benutzerinteraktion reagiert. Das Ziel ist nicht nur, einen Würfel zu drehen, sondern zu verstehen, wie die Render-Schleife funktioniert, warum die Zeit zwischen den Frames wichtig ist und wie man die Szene performant hält. Am Ende haben Sie eine Startvorlage, die in einer einzigen HTML-Datei läuft und nie Frames verliert.
Die drei Bausteine einer Three.js-Szene
Jedes Three.js-Projekt dreht sich um drei Objekte: die Scene, die Camera und den Renderer. Die Szene ist der Container, der alles Sichtbare enthält. Die Kamera bestimmt den Winkel, aus dem wir auf diese Szene blicken. Der Renderer nutzt WebGL, um das Ergebnis auf ein <canvas> zu zeichnen.
- Scene: die Wurzel des Grafikbaums, der Objekte, Lichter und Kameras enthält.
- PerspectiveCamera: eine Kamera, die wie das menschliche Auge entfernte Objekte kleiner erscheinen lässt und ein Tiefengefühl vermittelt.
- WebGLRenderer: läuft auf der GPU und zeichnet die Szene bei jedem Frame neu.
Die Bibliothek als Modul zu importieren ist der sauberste Ansatz. Moderne Browser können ein ES-Modul direkt über eine Import Map von einem CDN beziehen.
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
</script>
Die erste Szene einrichten
Erstellen wir nun die Szene, die Kamera und den Renderer. Die Kameraparameter sind der Reihe nach: Sichtfeld (fov, in Grad), Seitenverhältnis, nahe Clipping-Ebene und ferne Clipping-Ebene. Um den Renderer zur Seite hinzuzufügen, platzieren wir seine Eigenschaft domElement im DOM.
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0b1020);
const camera = new THREE.PerspectiveCamera(
60, window.innerWidth / window.innerHeight, 0.1, 100
);
camera.position.z = 4;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
Den Wert von setPixelRatio auf 2 zu begrenzen ist wichtig: Auf hochauflösenden Displays hält das Beschränken des Pixelverhältnisses die Auflösung im vernünftigen Rahmen, entlastet die GPU und verringert die Wärmeentwicklung auf Mobilgeräten.
Objekt, Material und Licht
In Three.js ist ein sichtbares Objekt (Mesh) die Kombination aus einer Geometrie (Form) und einem Material (Oberflächenaussehen). MeshStandardMaterial ist ein physikbasiertes Material, das Licht braucht, um realistisch auszusehen; deshalb bleibt der Bildschirm pechschwarz, bis Sie der Szene mindestens ein Licht hinzufügen.
const geometry = new THREE.IcosahedronGeometry(1, 0);
const material = new THREE.MeshStandardMaterial({
color: 0x4f9dde,
roughness: 0.3,
metalness: 0.6,
flatShading: true,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
const keyLight = new THREE.DirectionalLight(0xffffff, 2);
keyLight.position.set(3, 4, 5);
scene.add(keyLight);
scene.add(new THREE.AmbientLight(0x404060, 1.5));
Hier verwenden wir ein gerichtetes Licht (sonnenähnlich, mit klarer Richtung und harten Schatten) zusammen mit einem Umgebungslicht (gleichmäßig aus allen Richtungen, eine Aufhellung, die Schatten weicher macht). Dieses Duo ergibt eine einfache, aber ausgewogene Beleuchtung.
Die Render-Schleife und zeitbasierte Animation
Das Herz der Animation ist die Render-Schleife. requestAnimationFrame sagt dem Browser: „Ruf mich auf, wenn du für die nächste Darstellung bereit bist"; das geschieht meist 60 Mal pro Sekunde, synchron zur Bildwiederholrate des Bildschirms. Der naive Ansatz addiert in jedem Frame einen festen Wert, doch das ist falsch: Auf einem 60-Hz-Bildschirm dreht es sich langsam, auf einem 144-Hz-Bildschirm schnell. Richtig ist, mit der verstrichenen Zeit (Delta) zu multiplizieren.
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta(); // verstrichene Zeit in Sekunden
mesh.rotation.y += delta * 0.8; // 0,8 Radiant pro Sekunde
mesh.rotation.x += delta * 0.3;
renderer.render(scene, camera);
}
animate();
THREE.Clock liefert die tatsächlich zwischen den Frames verstrichene Zeit. Multipliziert man die Rotationsgeschwindigkeit mit diesem Wert, läuft die Animation auf jedem Gerät gleich schnell, unabhängig von der Bildwiederholrate des Bildschirms. Das ist einer der sichtbarsten Unterschiede zwischen professioneller und amateurhafter Arbeit.
An Fenstergröße und Interaktion anpassen
Damit das Bild nicht verzerrt wird, wenn der Benutzer das Fenster in der Größe ändert, müssen wir die Kamera und den Renderer aktualisieren. Wenn sich das Seitenverhältnis der Kamera ändert, ist ein Aufruf von updateProjectionMatrix zwingend erforderlich.
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
Für Interaktion ist der schnellste Weg das offizielle Add-on OrbitControls, mit dem sich die Szene per Maus drehen und zoomen lässt. Fügen Sie es einfach der Import Map hinzu und setzen Sie controls.update() in die Schleife. Wenn Sie ein leichteres Gefühl wünschen, können Sie die Mausposition mit einer dezenten Kameraverschiebung verknüpfen (ein Parallaxeffekt).
Tipps zu Performance und Aufräumen
- Pausieren, wenn der Tab im Hintergrund ist:
requestAnimationFrameverlangsamt sich bereits, wenn die Seite ausgeblendet ist, aber bei schweren Szenen schont das vollständige Stoppen der Schleife perdocument.hidden-Prüfung den Akku. - Geometrie und Material wiederverwenden: Wenn Sie Hunderte Kopien derselben Form brauchen, zeichnet
InstancedMeshsie alle in einem einzigen Draw Call. - Auf Speicherlecks achten: Wenn Sie ein Objekt dauerhaft entfernen, rufen Sie
geometry.dispose()undmaterial.dispose()auf; GPU-Speicher wird nicht automatisch freigegeben. - Schatten sparsam einsetzen: Echtzeit-Schattenkarten sind teuer; lassen Sie sie ausgeschaltet, wenn Sie sie nicht brauchen.
Häufige Fragen
Muss ich WebGL beherrschen, um Three.js zu lernen?
Nein. Three.js abstrahiert die komplexen Shader- und Buffer-Details von WebGL; für den Anfang genügen JavaScript und grundlegende 3D-Konzepte (Koordinaten, Vektoren, Kameras). WebGL-Kenntnisse werden später von Vorteil, wenn Sie eigene Shader schreiben wollen, sind aber nicht erforderlich.
Warum dreht sich meine Animation auf manchen Computern so schnell?
Höchstwahrscheinlich erhöhen Sie die Rotation um einen festen Wert. Auf Bildschirmen mit hoher Bildwiederholrate (120/144 Hz) läuft die Schleife häufiger und die Bewegung beschleunigt sich. Die Lösung ist, die Bewegung mit clock.getDelta() zu multiplizieren, also eine zeitbasierte Animation zu verwenden.
Funktioniert Three.js gut auf Mobilgeräten?
Ja, aber man muss vorsichtig sein. Begrenzen Sie setPixelRatio, halten Sie die Polygonzahl niedrig, vermeiden Sie aufwendige Post-Processing-Effekte und beschränken Sie Schatten. Mit diesen Maßnahmen lassen sich flüssige 3D-Szenen selbst auf Mittelklasse-Smartphones betreiben.
Träumen Sie von einem lebendigen 3D-Erlebnis im Browser? Vom Produktkonfigurator bis zum interaktiven Hintergrund gestalte ich Three.js-basierte Szenen und setze sie performant um. Um über Ihr Projekt zu sprechen, nehmen Sie Kontakt mit mir auf.