JavaScript in 3D: eine Einführung in Three.js

Hallo habr Ich präsentiere Ihnen die Übersetzung des Artikels "JavaScript in 3D: eine Einführung in Three.js" von Bret Cameron.

Einleitung


Three.js ist ein mächtiges Werkzeug. Es ist hilfreich, 3D-Design in einem Browser mit akzeptabler Leistung zu verwenden. Erste Schritte Three.js kann schwierig sein, insbesondere wenn Sie noch nie zuvor in die 3D-Programmierwelt eingetaucht sind.

Ich habe einige grundlegende Erfahrungen mit der Unity- und C # -Spiel-Engine, aber dennoch sind viele der Konzepte für mich neu. Ich bin zu dem Schluss gekommen, dass es für Anfängerentwickler nur sehr wenige Ressourcen gibt, und habe mich daher entschlossen, diesen Artikel zu schreiben. Darin werden die Grundelemente von Three.js Szenen von Polygonnetzen und Materialien über Geometrie bis hin zu Ladern und vielem mehr betrachtet.

Am Ende dieses Artikels erhalten Sie ein solides Verständnis der grundlegenden Aspekte, die erforderlich sind, um Ihrem zukünftigen Webprojekt eine zusätzliche Dimension hinzuzufügen.

Drei Beispiele von Ben Houston , Thomas Diewald und StrykerDoesAnimation .

Vektoren und Container - Grundbausteine


Oft gibt es zwei Hauptklassen in Three.js - Vector3 und Box3 . Wenn Sie 3D noch nicht kennen, klingt dies vielleicht ein bisschen abstrakt, aber Sie werden sie öfter treffen.

Vector3


Die grundlegendste 3D-Klasse mit drei Zahlen: x, y und z . Zahlen sind die Koordinaten eines Punktes im 3D-Raum oder die Richtung und Länge. Zum Beispiel:

const vect = new THREE.Vector3(1, 1, 1); 

Die meisten Konstruktoren in Three.js akzeptieren Objekte vom Typ Vector3 als Eingabeargumente, z. B. Box3

Box3


Diese Klasse repräsentiert Quader (3D-Container). Seine Hauptaufgabe besteht darin, einen Container um andere Objekte zu erstellen - und das ist alles, der kleinste Quader, in den ein 3D-Objekt passt. Jede Box3 ist um die x-, y- und z- Achse ausgerichtet . Ein Beispiel zum Erstellen eines Containers mit Vector3 :

 const vect = new THREE.Vector3(1, 1, 1); const box = new THREE.Box3(vect); 

Ein Beispiel zum Erstellen eines Containers um ein vorhandenes 3D-Objekt:

 const box = new THREE.Box3(); box.setFromObject(object); 

Sie können Grids ohne dieses fundierte Wissen erstellen. Sobald Sie jedoch mit der Entwicklung oder Änderung Ihrer Modelle beginnen, sind diese Klassen auf jeden Fall hilfreich. Jetzt werden wir uns von Abstraktionen zu sichtbareren Dingen entfernen.

Polygonnetz


In Three.js ist Mesh das wichtigste visuelle Element auf der Bühne. Dies ist ein 3D-Objekt aus dreieckigen Rechtecken (Polygonnetz). Es besteht aus zwei Objekten:
Geometrie - bestimmt die Form, Material - bestimmt das Aussehen.

Ihre Definitionen wirken möglicherweise etwas verwirrend (z. B. enthält die Geometrieklasse möglicherweise Informationen zu Farben), aber der Hauptunterschied besteht genau darin.

Geometrie


Abhängig von der gewünschten Aufgabe können Sie die Geometrie in Three.js definieren oder eine andere aus einer Datei importieren.

Mit Funktionen wie THREE.TorusKnotGeometry können wir komplexe Objekte mit einer einzigen Codezeile erstellen. Wir werden bald darauf eingehen, aber zunächst über einfachere Formen nachdenken.
Die einfachste 3D-Figur, Quader oder Container, kann durch die Parameter Breite , Höhe und Tiefe angegeben werden.

 const geometry = new THREE.BoxGeometry( 20, 20, 20 ); 


Für eine Kugel ist der Wert der Parameter radius , widthSegments und heightSegments minimal . Die letzten beiden Variablen geben an, mit wie vielen Dreiecken das Modell die Kugel darstellen soll: Je größer die Zahl, desto glatter wird sie.

 const geometry = new THREE.SphereGeometry( 20, 64, 64 ); 


Wenn wir scharfe oder dreieckige Formen herstellen möchten, können wir einen Kegel verwenden. Seine Argumente sind eine Kombination der Argumente der beiden vorhergehenden Figuren. Nachfolgend geben wir Radius , WidthSegments und RadialSegments vor .

  const geometry = new THREE.ConeBufferGeometry( 5, 20, 32 ); 


Dies ist nur ein Teil der häufigsten Zahlen. Three.js enthält viele Formen, die in der Dokumentation zu finden sind. In diesem Artikel werden weitere interessante Formulare vorgestellt, die auf der TorusKnotGeometry- Methode basieren .

Warum sehen diese Formen genau so aus, wie sie aussehen? Diese Frage würde den Rahmen dieses Artikels sprengen, aber ich rate Ihnen dringend, mit Parameterwerten zu experimentieren, da Sie mit einer Codezeile sehr interessante Formen erhalten können!

 const geometry = new THREE.TorusKnotGeometry(10, 1.3, 500, 6, 6, 20); 

https://codepen.io/BretCameron/pen/gOYqORg

Material


Geometrie definiert die Form unserer 3D-Objekte, nicht jedoch deren Erscheinungsbild. Um dies zu beheben, benötigen wir Materialien.

Three.js bietet 10 Materialien an, von denen jedes seine eigenen Vorteile und anpassbaren Parameter hat. Wir werden nur einen Teil der nützlichsten betrachten.



MeshNormalMaterial


Nützlich für den schnellen Start und Start.

Wir beginnen mit MeshNormalMaterial , dem mehrfarbigen Material, das wir in den obigen Beispielen verwendet haben. Sie entspricht den normalen Vektoren im RGB-Bedienfeld, dh, die Position des Vektors im 3D-Raum wird anhand von Farben bestimmt.

 const material = new THREE.MeshNormalMaterial(); 

Beachten Sie, dass Sie den CSS-Filter verwenden und die Sättigung ändern können, wenn Sie die Farbe des Materials ändern möchten:
  filter: hue-rotate(90deg) . 


Meiner Erfahrung nach ist dieses Material für eine schnelle Schicht und den Start nützlicher. Für eine bessere Kontrolle Ihrer Objekte ist es besser, etwas anderes zu verwenden.

Meshbasismaterial


Nützlich, wenn nur das Skelett angezeigt wird.

Wenn Sie der Figur eine einzelne Farbe geben möchten, können Sie MeshBasicMaterial nur verwenden, wenn keine Beleuchtung angewendet wird. Ich fand es nützlich, dieses Material beim Rendern des Modellskeletts zu verwenden. Um nur das Skelett zu zeichnen, müssen Sie {wireframe: true} als Parameter übergeben.

 const material = new THREE.MeshBasicMaterial({ wireframe: true, color: 0xdaa520 }); 

Der Hauptnachteil dieses Materials ist, dass Informationen über die Tiefe des Materials vollständig verloren gehen. Jedes Material hat die Option, nur das Skelett anzuzeigen, aber nur ein Material löst das Problem der fehlenden Tiefe - MeshDepthMaterial


Meshhambertmaterial


Nützlich für hohe Leistung bei geringer Genauigkeit.

Dies ist das erste Material, das Licht berücksichtigt, daher müssen Sie unserer Szene etwas Licht hinzufügen. Im folgenden Code fügen wir Scheinwerfer mit einem gelben Farbton hinzu, um einen wärmeren Effekt zu erzielen.

 const scene = new THREE.Scene(); const frontSpot = new THREE.SpotLight(0xeeeece); frontSpot.position.set(1000, 1000, 1000); scene.add(frontSpot); const frontSpot2 = new THREE.SpotLight(0xddddce); frontSpot2.position.set(-500, -500, -500); scene.add(frontSpot2); 

Fügen Sie nun Material für unsere Figur hinzu. Da unsere Figur wie eine Dekoration ist, schlage ich vor, eine goldenere Farbe hinzuzufügen. Ein weiterer emittierender Parameter ist die Farbe des Objekts, das vom Objekt selbst stammt (ohne Lichtquelle). Oft funktioniert dies besser als dunkle Farbe - zum Beispiel als dunkle Graustufen, wie im folgenden Beispiel

 const material = new THREE.MeshLambertMaterial({ color: 0xdaa520, emissive: 0x111111, }); 


Wie Sie im folgenden Beispiel sehen können, ist die Farbe mehr oder weniger korrekt, aber die Art und Weise, wie sie mit dem Licht interagiert, trägt nicht zum Realismus bei. Um dies zu beheben, müssen Sie MeshPhongMaterial oder MeshStandardMaterial verwenden.

MeshPhongMaterial


Nützlich für mittlere Leistung und mittlere Genauigkeit.

Dieses Material bietet einen Kompromiss zwischen Leistung und Rendering-Genauigkeit. Daher ist dieses Material eine gute Option für eine Anwendung, die produktiv sein muss und ein genaueres Rendering als MeshLambertMaterial bietet.

Jetzt können wir die Spiegeleigenschaft ändern, die die Helligkeit und Farbe der Oberflächenreflexion beeinflusst. Wenn die Emissionseigenschaft normalerweise dunkel ist, eignet sich Specular am besten für helle Farben. Unten verwenden wir hellgrau.

 const material = new THREE.MeshPhongMaterial({ color: 0xdaa520, emissive: 0x000000, specular: 0xbcbcbc, }); 


Optisch reflektiert das Bild von oben das Licht überzeugender, aber immer noch nicht perfekt. Weißes Licht ist zu hell und das Material sieht eher gerippt als metallisch aus (und wir bemühen uns darum). Mit MeshStandardMaterial können wir ein besseres Ergebnis erzielen.

MeshStandartMaterial


Nützlich für hohe Genauigkeit bei geringer Produktivität.

Dies ist das genaueste Material von allen, obwohl seine Verwendung die Kosten eines höheren Stromverbrauchs mit sich bringt. MeshStandartMaterial wird mit zusätzlichen Metall- und Rauheitsparametern verwendet , die jeweils einen Wert zwischen 0 und 1 annehmen .

Der Metalness- Parameter beeinflusst, wie das Objekt reflektiert und nähert sich der Natur des Metalls an. Dies liegt daran, dass leitende Materialien wie Metalle im Gegensatz zu Dielektrika wie Keramik unterschiedliche Reflexionseigenschaften aufweisen.

Rauheit fügt eine zusätzliche Ebene für die Anpassung hinzu. Sie können sich das Gegenteil von Glanz vorstellen: 0 - sehr glänzend, 1 - sehr matt.

 const material = new THREE.MeshStandardMaterial({ color: 0xfcc742, emissive: 0x111111, specular: 0xffffff, metalness: 1, roughness: 0.55, }); 


Dies ist das realistischste Material von allen in Three.js vorgestellten, aber auch das ressourcenintensivste

Die Materialien, die wir unten besprochen haben, sind die, die ich am häufigsten getroffen habe, und Sie können alle Optionen im Dock sehen.

Lader


Wie oben erläutert, können Sie Geometrie- und Polygonnetze manuell definieren. In der Praxis laden Benutzer ihre Geometrien häufig aus Dateien. Glücklicherweise hat Three.js nur wenige unterstützte Downloader, die viele 3D-Formate unterstützen.

Der Haupt- ObjectLoader lädt die JSON-Datei im JSON-Objekt- / Szenenformat . Die meisten Bootloader müssen manuell importiert werden. Hier finden Sie eine vollständige Liste der unterstützten Bootloader, die Sie importieren können. Nachstehend finden Sie eine kleine Liste der importierbaren Elemente.

 // GLTF import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; // OBJ import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'; // STL import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js'; // FBX import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'; // 3MF import { 3MFLoader } from 'three/examples/jsm/loaders/3MFLoader.js'; 

Das empfohlene Format für die Online-Anzeige ist GLTF, da das Format darauf abzielt, "Assets zur Laufzeit bereitzustellen, kompakt zu übertragen und schnell herunterzuladen".

Natürlich kann es viele Gründe geben, einen bestimmten Dateityp zu bevorzugen (z. B. wenn Qualität Priorität hat oder Genauigkeit für den 3D-Druck erforderlich ist). Die beste Online-Leistung wird beim Importieren von GLTF erzielt.

 import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import model from '../models/sample.gltf'; let loader = new GLTFLoader(); loader.load(model, function (geometry) { // if the model is loaded successfully, add it to your scene here }, undefined, function (err) { console.error(err); }); 

Alles zusammen


Einer der Gründe, warum Three.js einschüchternd wirkt, ist, dass Sie mit nur wenigen Codezeilen etwas von Grund auf neu erstellen können. In jedem obigen Beispiel mussten wir eine Szene und eine Kamera erstellen. Der Einfachheit halber habe ich diesen Code außerhalb des Bereichs der Überprüfung belassen, aber im Moment werden wir sehen, wie er zusammen aussehen wird.

Die Art und Weise, wie Sie Ihren Code organisieren, liegt bei Ihnen. In einfacheren Beispielen, wie in diesem Artikel, ist es sinnvoll, den gesamten Code an einer Stelle zu schreiben. In der Praxis ist es jedoch sinnvoll, die einzelnen Elemente zu trennen, um die Codebasis und deren Verwaltung zu erweitern.

Der Einfachheit halber betrachten wir Elemente, die als ein einzelnes Objekt gezeichnet werden, sodass wir den gesamten Code in einer Datei speichern.

 // Import dependencies import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; //   const scene = new THREE.Scene(); scene.background = new THREE.Color(0x282c34); //  ,       const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000); camera.position.z = 5; //  ""      const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); //   DOM   renderer.domElement   document.getElementById('threejs').appendChild(renderer.domElement); //  ,      DOM  let controls = new OrbitControls(camera, document.getElementById('threejs')); controls.target.set(0, 0, 0); controls.rotateSpeed = 0.5; controls.update(); //  ( )   const geometry = new THREE.TorusKnotGeometry(10, 1.3, 500, 6, 6, 20); //    const material = new THREE.MeshStandardMaterial({ color: 0xfcc742, emissive: 0x111111, specular: 0xffffff, metalness: 1, roughness: 0.55, }); //   ,       const mesh = new THREE.Mesh(geometry, material); mesh.scale.x = 0.1; mesh.scale.y = 0.1; mesh.scale.z = 0.1; scene.add(mesh); //  ,       const frontSpot = new THREE.SpotLight(0xeeeece); const frontSpot2 = new THREE.SpotLight(0xddddce); frontSpot.position.set(1000, 1000, 1000); frontSpot2.position.set(-500, -500, -500); scene.add(frontSpot); scene.add(frontSpot2); //   ,           const animate = function () { requestAnimationFrame(animate); mesh.rotation.x += 0.005; mesh.rotation.y += 0.005; mesh.rotation.z += 0.005; renderer.render(scene, camera); }; //    animate(); 

Muss ich ein Framework verwenden?


Schließlich ist es Zeit zu diskutieren, ob Three.js mit Ihrem bevorzugten Framework verwendet werden soll. Im Moment gibt es ein gutes React -Three-Fibre- Paket für React. Für React-Benutzer bietet die Verwendung eines solchen Pakets offensichtliche Vorteile: Sie pflegen eine Struktur für die Arbeit mit Komponenten, mit der Sie Code wiederverwenden können.

Anfängern rate ich, mit dem üblichen Vanila JS zu beginnen , da die meisten Online-Materialien, die über Three.js geschrieben wurden, sich auf Three.js auf Vanila JS beziehen. Aufgrund meiner Lernerfahrung kann es verwirrend und schwierig sein, dies durch ein Paket zu lernen. Beispielsweise müssen Sie Three.js-Objekte und -Methoden in Komponenten und Requisiten übersetzen. (Sobald Sie Three.js lernen, können Sie jedes Paket verwenden).

Hinzufügen von Three.js zum Framework


Three.js bietet ein HTML-Objekt (am häufigsten als renderer.domElement bezeichnet ), das zu jedem HTML-Objekt in Ihrer Anwendung hinzugefügt werden kann. Wenn Sie zum Beispiel ein div mit id = ”threejs” haben , können Sie einfach den folgenden Code in Ihren Three.js-Code einfügen:

 document.getElementById('threejs').appendChild(renderer.domElement); 

Einige Frameworks haben bevorzugte Pfade, um auf die DOM-Knoten des Baums zuzugreifen. Zum Beispiel ref in React, $ ref in Vue oder ngRef in Angular und es sieht aus wie ein gewaltiges Plus vor dem Hintergrund des direkten Zugriffs auf DOM-Elemente. Schauen wir uns als Beispiel eine schnelle Implementierung für React an.

Strategie für die Reaktion


Wenn Sie Reagieren verwenden, gibt es eine Möglichkeit, Three.js-Dateien in eine Ihrer Komponenten einzubetten. In die Datei ThreeEntryPoint.js schreiben wir den folgenden Code:

 export default function ThreeEntryPoint(sceneRef) { let renderer = new THREE.WebGLRenderer(); // ... sceneRef.appendChild(renderer.domElement); } 

Wir exportieren dies als eine Funktion, die ein Argument akzeptiert: einen Verweis auf ein Element in unserer Komponente. Jetzt können wir unsere Komponente erstellen

 import React, { Component } from 'react'; import ThreeEntryPoint from './threejs/ThreeEntryPoint'; export default class ThreeContainer extends Component { componentDidMount() { ThreeEntryPoint(this.scene); } render() { return ( <> <div ref={element => this.scene = element} /> </> ); } } 

Die importierte ThreeEntryPoint- Funktion muss in der componentDidMount- Methode aufgerufen und das neue div unter Verwendung von Referenzen als Argument übergeben werden
Als Beispiel für diesen Ansatz können Sie das Repository klonen und selbst ausprobieren: https://github.com/BretCameron/three-js-sample .

Fazit


Ich kann noch viel über Three.js sagen, aber ich hoffe, dieser Artikel hat Ihnen genügend Informationen geliefert, um mit der Verwendung dieser leistungsstarken Technologie zu beginnen. Als ich anfing, Three.js zu lernen, konnte ich keine einzige Ressource wie diesen Artikel finden. Ich hoffe, ich habe dazu beigetragen, diese Technologie für Anfänger zugänglicher zu machen.

Source: https://habr.com/ru/post/de477956/


All Articles