So generieren Sie ein 3D-Handymodell im Browser

Anleitung für ein 3D-Handymodell im Browser mit Three.js und CSG: Körper mit Bevel, Ausschnitte für Tasten und Lautsprecher, Web Worker, schnelles Bohren.

Zwei generierte 3D-Handymodelle, eines von vorne mit dunklem Display, eines von hinten

Ein 3D-Modell eines Smartphones generieren Sie im Browser, indem Sie ein Rechteck mit abgerundeten Ecken in die Tiefe extrudieren und daraus mit booleschen Operationen (CSG) Vorderseite, Tasten, Lautsprecher und Anschluss ausschneiden. Das ganze Gerät wird dann durch ein paar Zahlen in Millimetern beschrieben, und ein neues Modell bedeutet, diese Zahlen zu ändern, nicht in einem 3D-Programm zu modellieren.

Das lohnt sich überall dort, wo Sie viele ähnliche Geräte brauchen oder Nutzer sie anpassen sollen: Konfiguratoren, Mockups, Produktvorschauen. Ein Beispiel aus der Praxis ist Ecranify, ein Tool für 3D-Mockups für Screenshots im App Store und bei Google Play, das kein einziges fertiges 3D-Modell enthält. Unten finden Sie den Ablauf Schritt für Schritt, samt einer Messung, die zeigt, wo sich bei der Berechnung am meisten sparen lässt.

Was Sie brauchen

  • Three.js für Szene, Kameras, Lichter und vor allem Geometrie. Geometrie ist in Three.js ein Mesh, also eine Menge von Dreiecken.
  • Die Bibliothek three-bvh-csg für boolesche Operationen auf Meshes. Sie beschleunigt sie mit einem räumlichen Index (BVH, Bounding Volume Hierarchy) und testet so nicht jedes Dreieck gegen jedes andere.
  • Eine Beschreibung des Geräts als Daten, zum Beispiel JSON mit Gehäusemaßen, Eckenradius, Bevel-Größe und einer Liste der Elemente an den Seiten. Geben Sie alle Positionen in Millimetern ab der Gerätemitte an.

Schritt 1: Ein Rechteck in 3D extrudieren

Zeichnen Sie ein 2D-Rechteck mit abgerundeten Ecken in Originalgröße, zum Beispiel 163,4 × 78 mm, und extrudieren Sie es auf eine Tiefe von 8,75 mm. Sie erhalten eine Platte mit scharfen Kanten. So sieht kein Handy aus, deshalb schalten Sie beim Extrudieren auch einen Bevel ein, also eine abgerundete Kante. Three.js erledigt beides in einem Schritt.

Nur ExtrusionKantige Platte ohne abgerundete Ecken und Kanten
+ Ecken und BevelDieselbe Platte mit abgerundeten Ecken und Kanten
Dieselbe Form, zwei andere Zahlen: Eckenradius und Bevel-Größe.

Achten Sie auf eines: Der Bevel wird nach außen hinzugefügt. Wenn Sie exakte Außenmaße brauchen, verkleinern Sie die 2D-Form und die Tiefe um die Bevel-Größe.

b = min(Bevel, Tiefe / 2)
Form = abgerundetes Rechteck(Breite - 2b, Höhe - 2b, Eckenradius - b)
Körper = extrudiere(Form, Tiefe - 2b, Bevel b)

Wie glatt die Rundungen sind (Tessellation), hängt von der Zahl der Segmente ab, in die ein Bogen zerlegt wird. Mehr Segmente bedeuten eine glattere Form und mehr Dreiecke. Diese Zahl entscheidet später über die Geschwindigkeit der ganzen Berechnung, deshalb kommen wir in Schritt 7 darauf zurück.

Schritt 2: Eine Mulde für das Display ausschneiden

CSG (Constructive Solid Geometry) setzt Körper aus einfacheren Körpern mit drei Operationen zusammen: Vereinigung, Differenz und Schnitt. Die Differenz funktioniert wie ein Bohrer: Ein Körper nimmt Material aus einem anderen weg.

Das Display eines Handys liegt nicht bündig mit dem Rahmen, sondern in einer flachen Mulde. Dafür erstellen Sie eine zweite, um 1,4 mm kleinere Kopie des Körpers, behalten davon nur den vorderen, 0,7 mm dicken Teil (Schnitt mit einem großen Quader) und ziehen diesen Teil vom Körper ab. In die entstandene Mulde legen Sie dann eine dunkle Displayfläche.

SchneidkörperHandykörper mit einem blauen, durchscheinenden Schneidkörper auf der Vorderseite
Nach der DifferenzKörper mit ausgeschnittener Mulde und dunklem Display darin
Der blaue Körper wird abgezogen. Rechts liegt das Display bereits in der Mulde.

Zwei praktische Details:

  • Der Quader für den Schnitt muss wirklich groß sein. Er muss nur die Tiefe beschneiden, in Breite und Höhe aber das ganze Gerät überragen. Leiten Sie ihn nicht von der Tiefe ab: Das 20-Fache der Tiefe ist bei einem dünnen, hohen Handy zum Beispiel weniger als seine Höhe, und oben und unten bleibt ein unbeschnittener Rahmen stehen.
  • Vergrößern Sie den Schneidkörper um ein paar Promille. Wenn zwei Körper exakt dieselbe Fläche haben, kann CSG nicht entscheiden, welche Seite innen liegt. Ein Maßstab von 1,002 verhindert das und ist mit bloßem Auge nicht zu sehen.

Schritt 3: Seitliche Elemente als Bohrer vorbereiten

Jedes Element an der Seite folgt demselben Rezept mit einem anderen Bohrer. Eine Taste entsteht zum Beispiel aus drei Körpern: Ein abgerundetes Rechteck, etwas tiefer als die Taste, wird vom Rahmen abgezogen, der Rand der Öffnung erhält eine kleine Fase, und in die Öffnung kommt ein etwas kleinerer Körper, der leicht aus dem Rahmen ragt.

BohrerSeite eines Handys mit zwei blauen Schneidkörpern für Tasten
Ausschnitt und TasteSeite eines Handys mit ausgeschnittenen Öffnungen und eingesetzten Tasten
Zwei Tasten auf der rechten Seite vor und nach der Differenz.

Lautsprecher sind eine Reihe kleiner Zylinder, der Anschluss ist ein abgerundetes Rechteck. Setzen Sie in jede Öffnung etwas Dunkles: eine dünne Scheibe ins Lautsprechergitter, eine Platte in den Anschluss. Ein leeres Loch in einer dünnen Schale wirkt wie ein Fehler, ein Loch mit dunkler Füllung wirkt wie Hardware.

Unterkante eines Handys mit zwei Reihen Lautsprecherlöchern und einem Anschluss in der Mitte
Lautsprecherlöcher und Anschluss. Alles abgezogene Zylinder und abgerundete Rechtecke.
für jedes Element in der Liste der Seitenelemente:
    Bohrer = Form des Elements (Zylinder, abgerundetes Rechteck, ...)
    setze den Bohrer an seiner Position in mm auf die Kante
    füge den Bohrer zur Liste der Schnitte hinzu
    wenn das Element eine Füllung hat: füge die Füllung zur Szene hinzu

Eine Taste hinzuzufügen ist dann ein Dateneintrag. Die Elemente summieren sich schnell: Ein iPad Pro 13″ hat 58 Lautsprecherlöcher, 4 Tasten, 2 Antennen und einen Anschluss, also über 60 Differenzen an einem einzigen Körper.

Schritt 4: Details auf der Vorderseite nicht bohren

Die Kameraaussparung, die Insel oben im Display und der Home-Indikator sind flach. Dafür Geometrie zu schneiden ist verschwendete Arbeit. Bringen Sie sie stattdessen als Decal an, also als Bild, das auf eine Oberfläche projiziert wird. Projizieren Sie es auf eine unsichtbare Ebene über die ganze Vorderseite und geben Sie die Position in denselben Millimetern an wie alles andere. Die Ausnahme ist der runde Home-Button älterer iPhones. Er hat echte Tiefe, die man von der Seite sieht, und geht deshalb durch CSG.

Schritt 5: Die Berechnung in einen Web Worker verlagern

JavaScript läuft im Browser in einem einzigen Thread. Derselbe Thread rechnet, zeichnet die Seite und reagiert auf Klicks. Solange CSG läuft, steht die Seite still, und auch ein Spinner hilft nicht, weil er denselben Thread braucht.

Starten Sie deshalb den gesamten Aufbau des Geräts in einem Web Worker, also in einem eigenen Hintergrund-Thread. Der Worker erhält die Gerätebeschreibung, gibt fertige Vertex-Arrays zurück, und die Seite bleibt flüssig. Wenn sich ein Parameter ändert, beenden Sie den alten Worker und starten einen neuen, damit er keinen veralteten Zustand zu Ende rechnet.

Das Ergebnis aus dem Worker dauert einen Moment, deshalb ergänzen Sie eine sofortige Vorschau. Zeigen Sie das bearbeitete Element als blauen, durchscheinenden „Geist“, einen einfachen Quader oder Zylinder mit ein paar Dutzend Dreiecken, der sich im Haupt-Thread sofort aufbauen lässt. Wenn der Worker fertig ist, verschwindet der Geist.

Schritt 6: Alle Löcher auf einmal abziehen

Am direktesten ist es, die Elemente nacheinander abzuziehen: Loch 1 vom Körper, dann Loch 2 vom Ergebnis und so weiter. Jede Differenz baut aber den räumlichen Index des ganzen Körpers neu auf, und das ist der teuerste Teil der Berechnung. Bei Dutzenden Elementen wächst die Zeit sehr schnell.

Besser ist es, zuerst alle Bohrer für einen Körper zu sammeln, sie zu einem Mesh zu verbinden und einmal abzuziehen.

Schnitte = []
für jedes Element: füge seinen Bohrer zu Schnitte hinzu
verbunden = verbinde alle Schnitte zu einem Mesh
Körper = Körper minus verbunden
wenn das Verbinden scheitert: ziehe die Schnitte einzeln ab

Gemessen an einer vereinfachten Kopie des Ablaufs (Three.js 0.170, three-bvh-csg 0.0.17, Node 22, Cloud-Server mit 2 Kernen Intel Xeon 2,1 GHz, Median aus 5 Läufen):

Körper Löcher Einzeln Auf einmal
Handy, 17.676 Dreiecke 25 2,47 s 0,26 s
Tablet, 17.676 Dreiecke 58 5,55 s 0,89 s
Tablet, 68.108 Dreiecke 58 22,8 s 3,1 s

Eine Bedingung: Verbinden und einmal Abziehen liefert nur dann dasselbe Ergebnis wie das schrittweise Abziehen, wenn sich die Bohrer nicht überlappen. Die Elemente sitzen an verschiedenen Stellen, also gilt das in der Regel. Schiebt ein Nutzer aber zwei Elemente übereinander, kann an der Überlappung ein kleiner Fehler entstehen.

Schritt 7: Die Segmentzahl als Parameter führen

Eine Tessellation, die für die Anzeige passt, ist für CSG oft unnötig dicht. Mit 64 Segmenten pro Ecke und 32 für den Bevel hat der Körper 68.108 Dreiecke. Die Hälfte (32 und 16) sieht man an einer 2-mm-Kante nicht, der Körper hat dann aber nur 17.676 Dreiecke, und die dritte Zeile der Tabelle zeigt, was das für die Zeit bedeutet.

64 und 32 SegmenteDrahtgittermodell einer Handyecke mit sehr dichtem Dreiecksnetz
32 und 16 SegmenteDrahtgittermodell derselben Ecke mit halb so vielen Segmenten
Die Ecke des Körpers von oben. Rechts ist es ein Viertel der Dreiecke.

Geben Sie kleinen Elementen mit einem Radius bis 2 mm (Lautsprecherlöcher, Schrauben) die Hälfte der Segmente, Vorschaubildern in der Galerie noch weniger. Legen Sie die Qualität deshalb nicht als Konstante im Code fest, sondern als Parameter des Aufbaus.

Rendern auf einem Server ohne Grafikkarte

Wenn Sie dieselbe Szene auch auf einem Server rendern müssen, etwa als Backup für Exporte, läuft sie in Chrome ohne Bildschirm (headless), das ohne Grafikkarte in Software zeichnet. Dort vervielfacht sich jede Ineffizienz. In Ecranify lief das schwerste Tablet ohne die genannten Anpassungen in das Limit von 15 Minuten. Mit verbundenen Schnitten, kleineren Texturen, geringerer Tessellation und einem Cache fertiger Geometrie, dessen Schlüssel ein Hash der Gerätebeschreibung ist, dauert ein Render etwa eine halbe Minute und ein wiederholter etwa 5 Sekunden. Die Details mit interaktiven Beispielen stehen im englischen Artikel auf ecranify.com.

Wenn Sie ein ähnliches 3D-Problem im Browser lösen oder eine App nach Maß brauchen, schreiben Sie uns.

← Mehr in Ecranify