WebGL-Grundlagen Teil III

Willkommen zur dritten und letzten Folge unserer WebGL Essentials-Miniserie. In dieser Lektion betrachten wir die Beleuchtung und fügen 2D-Objekte zu Ihrer Szene hinzu. Es gibt viele neue Informationen hier, also tauchen wir gleich ein!


Licht

Beleuchtung kann der technischste und schwierigste Aspekt einer 3D-Anwendung sein. Ein gutes Verständnis der Beleuchtung ist absolut unerlässlich.

Wie funktioniert Licht??

Bevor wir uns mit den verschiedenen Arten von Licht und Codetechniken beschäftigen, ist es wichtig zu wissen, wie Licht in der realen Welt funktioniert. Jede Lichtquelle (z. B. eine Glühbirne, die Sonne usw.) erzeugt Partikel, die als Photonen bezeichnet werden. Diese Photonen springen um Objekte herum, bis sie schließlich in unsere Augen gelangen. Unsere Augen wandeln die Photonen in ein visuelles "Bild" um. So sehen wir. Licht ist auch additiv, dh ein Objekt mit mehr Farbe ist heller als ein Objekt ohne Farbe (Schwarz). Schwarz ist das völlige Fehlen von Farbe, während Weiß alle Farben enthält. Dies ist ein wichtiger Unterschied, wenn Sie mit sehr hellen oder "übermäßig gesättigten" Leuchten arbeiten.

Die Helligkeit ist nur ein Prinzip mit mehreren Zuständen. Reflexion kann zum Beispiel verschiedene Ebenen haben. Ein Objekt kann wie ein Spiegel vollständig reflektieren, während andere Objekte eine matte Oberfläche haben können. Transparenz bestimmt, wie Objekte das Licht biegen und eine Brechung verursachen. Ein Objekt kann vollständig transparent sein, während andere undurchsichtig sein können (oder eine beliebige Stufe dazwischen)..

Die Liste geht weiter, aber ich denke man kann schon sehen, dass Licht nicht einfach ist.

Wenn Sie auch nur eine kleine Szene wünschen, um echtes Licht zu simulieren, läuft sie etwa 4 Frames pro Stunde, und das auf einem leistungsstarken Computer. Um dieses Problem zu umgehen, verwenden Programmierer Tricks und Techniken, um halbrealistische Beleuchtung mit einer angemessenen Bildrate zu simulieren. Sie müssen einen Kompromiss zwischen Realismus und Geschwindigkeit finden. Schauen wir uns einige dieser Techniken an.

Bevor ich mich mit verschiedenen Techniken befasse, möchte ich Ihnen einen kleinen Haftungsausschluss geben. Es gibt viele Kontroversen über die genauen Namen der verschiedenen Beleuchtungstechniken, und verschiedene Personen erklären Ihnen, was "Ray Casting" oder "Light Mapping" ist. Bevor ich also die Hasspost bekomme, möchte ich sagen, dass ich die Namen verwenden werde, die ich gelernt habe. Einige Leute stimmen möglicherweise nicht mit meinen genauen Titeln überein. In jedem Fall ist es wichtig zu wissen, was die verschiedenen Techniken sind. Also lass uns ohne weiteres anfangen.

Sie müssen einen Kompromiss zwischen Realismus und Geschwindigkeit finden.

Raytracing

Raytracing ist eine der realistischeren Beleuchtungstechniken, aber auch eine der kostspieligeren. Raytracing emuliert echtes Licht; Es sendet "Photonen" oder "Strahlen" von der Lichtquelle aus und wirft sie herum. Bei den meisten Ray-Tracing-Implementierungen kommen die Strahlen von der "Kamera" und prallen in entgegengesetzter Richtung auf die Szene auf. Diese Technik wird normalerweise in Filmen oder Szenen verwendet, die vorab gerendert werden können. Dies bedeutet nicht, dass Sie Ray-Tracing nicht in einer Echtzeitanwendung verwenden können, sondern zwingt Sie dazu, andere Dinge in der Szene abzuschwächen. Beispielsweise müssen Sie möglicherweise die Menge an "Bounces" reduzieren, die die Strahlen ausführen sollen, oder Sie können sicherstellen, dass sich keine Objekte mit reflektierenden oder brechenden Oberflächen befinden. Ray Tracing kann auch eine praktikable Option sein, wenn Ihre Anwendung nur wenige Lichter und Objekte enthält.

Wenn Sie eine Echtzeitanwendung haben, können Sie möglicherweise Teile Ihrer Szene vorkompilieren.

Wenn sich die Leuchten in Ihrer Anwendung nicht oder nur in einem kleinen Bereich bewegen, können Sie die Beleuchtung mit einem sehr fortschrittlichen Ray-Tracing-Algorithmus vorkompilieren und einen kleinen Bereich um die sich bewegende Lichtquelle neu berechnen. Wenn Sie beispielsweise ein Spiel erstellen, in dem sich die Lichter nicht bewegen, können Sie die Welt mit allen gewünschten Lichtern und Effekten vorkompilieren. Dann kannst du einfach einen Schatten um deinen Charakter hinzufügen, wenn er sich bewegt. Dies ergibt ein sehr hochwertiges Aussehen bei minimalem Verarbeitungsaufwand.

Ray Casting

Ray Casting ist dem Ray Tracing sehr ähnlich, aber die "Photonen" prallen nicht von Objekten ab und interagieren nicht mit verschiedenen Materialien. In einer typischen Anwendung würden Sie grundsätzlich mit einer dunklen Szene beginnen und dann Linien von der Lichtquelle zeichnen. Alles, was das Licht trifft, ist beleuchtet. alles andere bleibt dunkel. Diese Technik ist wesentlich schneller als die Strahlverfolgung und bietet dennoch einen realistischen Schatteneffekt. Das Problem beim Ray Casting ist jedoch seine Beschränkung. Sie haben nicht viel Platz zum Arbeiten, wenn Sie Effekte wie Reflexionen hinzufügen möchten. Normalerweise müssen Sie einen Kompromiss zwischen Ray Casting und Ray Tracing finden, bei dem Geschwindigkeit und visuelle Effekte aufeinander abgestimmt werden.

Das Hauptproblem bei diesen beiden Techniken besteht darin, dass Sie mit WebGL keinen Zugriff auf andere Scheitelpunkte als den gerade aktiven haben.

Das bedeutet, dass Sie entweder alles auf der CPU (wie bei der Grafikkarte) ausführen müssen, oder Sie haben einen zweiten Shader erstellt, der die gesamte Beleuchtung berechnet und die Informationen in einer falschen Textur speichert. Sie müssten dann die Texturdaten wieder in die Beleuchtungsinformationen dekomprimieren und sie den Scheitelpunkten zuordnen. Grundsätzlich ist die aktuelle Version von WebGL dafür nicht sehr gut geeignet. Ich sage nicht, dass es nicht geht, ich sage nur, dass WebGL Ihnen nicht helfen wird.

Schattenabbildung

Ray Tracing kann auch eine praktikable Option sein, wenn Ihre Anwendung nur wenige Lichter und Objekte enthält.

Eine viel bessere Alternative zum Ray Casting in WebGL wird als Shadow-Mapping bezeichnet. Sie haben den gleichen Effekt wie Ray Casting, verwenden jedoch einen anderen Ansatz. Die Schattenzuordnung löst nicht alle Ihre Probleme, aber WebGL ist dafür halboptimiert. Man kann sich das als eine Art Hack vorstellen, aber Schatten-Mapping wird in echten PC- und Konsolenanwendungen verwendet.

Also, was fragst du??

Sie müssen verstehen, wie WebGL seine Szenen darstellt, um diese Frage beantworten zu können. WebGL schiebt alle Scheitelpunkte in den Scheitelpunkt des Scheitelpunkts, der die endgültigen Koordinaten für jeden Scheitelpunkt berechnet, nachdem die Transformationen angewendet wurden. Um Zeit zu sparen, verwirft WebGL dann die Scheitelpunkte, die hinter anderen Objekten verborgen sind, und zeichnet nur die wesentlichen Objekte. Wenn Sie sich erinnern, wie Ray Casting funktioniert, wirft es nur Lichtstrahlen auf die sichtbaren Objekte. Also setzen wir die "Kamera" unserer Szene auf die Koordinaten der Lichtquelle und richten sie in die Richtung, in die das Licht zeigen soll. WebGL entfernt dann automatisch alle Scheitelpunkte, die nicht sichtbar sind. Wir können diese Daten dann speichern und verwenden, wenn wir die Szene rendern, um zu wissen, welche Scheitelpunkte beleuchtet sind.

Diese Technik hört sich auf dem Papier gut an, hat aber einige Nachteile:

  • WebGL erlaubt nicht den Zugriff auf den Tiefenpuffer. Sie müssen im Fragment-Shader kreativ sein, wenn Sie versuchen, diese Daten zu speichern.
  • Selbst wenn Sie alle Daten speichern, müssen Sie sie immer noch den Scheitelpunkten zuordnen, bevor sie beim Rendern der Szene in das Scheitelpunkt-Array gelangen. Dies erfordert zusätzliche CPU-Zeit.

Alle diese Techniken erfordern einiges an Basteln mit WebGL. Aber ich zeige Ihnen eine sehr grundlegende Technik zur Erzeugung eines diffusen Lichts, um Ihren Objekten ein wenig Persönlichkeit zu verleihen. Ich würde es nicht als realistisches Licht bezeichnen, aber es gibt Ihren Objekten eine Definition. Diese Technik verwendet die Normalmatrix des Objekts, um den Winkel des Lichts im Vergleich zur Objektoberfläche zu berechnen. Es ist schnell, effizient und erfordert kein Hacken mit WebGL. Lass uns anfangen.


Licht hinzufügen

Beginnen wir mit der Aktualisierung der Shader, um die Beleuchtung zu integrieren. Wir müssen einen Booleschen Wert hinzufügen, der bestimmt, ob das Objekt beleuchtet werden soll oder nicht. Dann benötigen wir den eigentlichen Normalenscheitelpunkt und transformieren ihn so, dass er mit dem Modell übereinstimmt. Schließlich müssen wir eine Variable erstellen, um das Endergebnis an den Fragment-Shader zu übergeben. Dies ist der neue Vertex-Shader:

Wenn wir keine Lichter verwenden, übergeben wir einfach einen leeren Scheitelpunkt an den Fragment-Shader und seine Farbe bleibt gleich. Wenn Lichter eingeschaltet sind, berechnen wir den Winkel zwischen der Richtung des Lichts und der Oberfläche des Objekts mithilfe der Punktfunktion der Normalen und multiplizieren das Ergebnis mit der Farbe des Lichts als eine Art Maske, die auf das Objekt gelegt wird.

Oberflächennormalen von Oleg Alexandrov.

Dies funktioniert, weil die Normalen bereits senkrecht zur Oberfläche des Objekts sind und die Punktfunktion eine Zahl angibt, die auf dem Winkel des Lichts zur Normalen basiert. Wenn das Normal und das Licht nahezu parallel sind, gibt die Punktfunktion eine positive Zahl zurück, dh das Licht ist zur Oberfläche gerichtet. Wenn die Normale und das Licht senkrecht stehen, ist die Oberfläche parallel zum Licht und die Funktion gibt Null zurück. Alles, was höher als 90 Grad zwischen dem Licht und dem Normalen ist, führt zu einer negativen Zahl, aber wir filtern dies mit der Funktion "max zero" heraus.

Nun lassen Sie mich den Fragment-Shader zeigen:

Dieser Shader ist fast identisch mit früheren Teilen der Serie. Der einzige Unterschied besteht darin, dass wir die Farbe der Textur mit der Lichtstärke multiplizieren. Dies hebt oder verdunkelt verschiedene Teile des Objekts und gibt ihm Tiefe.

Das ist alles für die Shader, gehen wir jetzt zum WebGL.js Datei und ändern Sie unsere beiden Klassen.

Aktualisierung unseres Frameworks

Beginnen wir mit dem GLObject Klasse. Wir müssen eine Variable für das Normal-Array hinzufügen. Hier ist was der obere Teil Ihres GLObject sollte jetzt so aussehen:

Funktion GLObject (VertexArr, TriangleArr, TextureArr, ImageSrc, NormalsArr) this.Pos = X: 0, Y: 0, Z: 0; this.Scale = X: 1,0, Y: 1,0, Z: 1,0; this.Rotation = X: 0, Y: 0, Z: 0; this.Vertices = VertexArr; // Array für die Normaldaten this.Normals = NormalsArr; // Der Rest von GLObject wird hier fortgesetzt

Dieser Code ist ziemlich einfach. Gehen wir nun zurück zur HTML-Datei und fügen das Normal-Array zu unserem Objekt hinzu.

In dem Bereit() Wenn wir unser 3D-Modell laden, müssen wir den Parameter für das Normal-Array hinzufügen. Ein leeres Array bedeutet, dass das Modell keine Normaldaten enthielt, und wir müssen das Objekt ohne Licht zeichnen. Für den Fall, dass das Normal-Array Daten enthält, übergeben wir sie einfach an die GLObject Objekt.

Wir müssen auch das aktualisieren WebGL Klasse. Wir müssen Variablen direkt nach dem Laden der Shader mit den Shadern verknüpfen. Fügen wir den Scheitelpunkt der Normalen hinzu. Ihr Code sollte jetzt so aussehen:

// Vertex-Positionsattribut aus Shader verknüpfen this.VertexPosition = this.GL.getAttribLocation (this.ShaderProgram, "VertexPosition"); this.GL.enableVertexAttribArray (this.VertexPosition); // Texture Coordinate-Attribut aus Shader verknüpfen this.VertexTexture = this.GL.getAttribLocation (this.ShaderProgram, "TextureCoord"); this.GL.enableVertexAttribArray (this.VertexTexture); // Dies ist das neue Attribut "Normal". This.VertexNormal = this.GL.getAttribLocation (this.ShaderProgram, "VertexNormal"); this.GL.enableVertexAttribArray (this.VertexNormal);

Als nächstes aktualisieren wir die PrepareModel () Funktion und fügen Sie etwas Code hinzu, um die Normaldaten zu zwischenspeichern, wenn sie verfügbar sind. Fügen Sie den neuen Code direkt vor dem ein Model.Fertig Aussage unten:

if (false! == Model.Normals) Buffer = this.GL.createBuffer (); this.GL.bindBuffer (this.GL.ARRAY_BUFFER, Puffer); this.GL.bufferData (this.GL.ARRAY_BUFFER, neuer Float32Array (Model.Normals), this.GL.STATIC_DRAW); Model.Normals = Puffer;  Model.Ready = true;

Aktualisieren Sie nicht zuletzt das Aktual Zeichnen Funktion, um all diese Änderungen zu übernehmen. Hier gibt es ein paar Änderungen, also macht euch bitte mit mir aus. Ich werde die gesamte Funktion Stück für Stück durchgehen:

this.Draw = Funktion (Modell) if (Model.Image.ReadyState == true && Model.Ready == false) this.PrepareModel (Model);  if (Model.Ready) this.GL.bindBuffer (this.GL.ARRAY_BUFFER, Model.Vertices); this.GL.vertexAttribPointer (this.VertexPosition, 3, this.GL.FLOAT, false, 0, 0); this.GL.bindBuffer (this.GL.ARRAY_BUFFER, Model.TextureMap); this.GL.vertexAttribPointer (this.VertexTexture, 2, this.GL.FLOAT, false, 0, 0);

Bis hier ist das gleiche wie vorher. Nun kommt der Normals Teil:

 // Check For Normals if (false! == Model.Normals) // Verbinde den Normalpuffer mit dem Shader this.GL.bindBuffer (this.GL.ARRAY_BUFFER, Model.Normals); this.GL.vertexAttribPointer (this.VertexNormal, 3, this.GL.FLOAT, false, 0, 0); // Dem Shader mitteilen, dass er die Beleuchtung verwenden soll var UseLights = this.GL.getUniformLocation (this.ShaderProgram, "UseLights"); this.GL.uniform1i (UseLights, true);  else // Auch wenn unser Objekt keine normalen Daten hat, müssen wir noch etwas übergeben. // Also übergebe ich die Vertices stattdessen this.GL.bindBuffer (this.GL.ARRAY_BUFFER, Model.Vertices); this.GL.vertexAttribPointer (this.VertexNormal, 3, this.GL.FLOAT, false, 0, 0); // Dem Shader mitteilen, dass er die Beleuchtung verwenden soll var UseLights = this.GL.getUniformLocation (this.ShaderProgram, "UseLights"); this.GL.uniform1i (UseLights, false); 

Wir prüfen, ob das Modell Normaldaten enthält. Wenn ja, verbindet es den Puffer und setzt den Boolean. Ist dies nicht der Fall, benötigt der Shader immer noch Daten, oder es wird ein Fehler angezeigt. Stattdessen passierte ich den Vertices-Puffer und stellte das ein UseLight boolean zu falsch. Sie könnten dies umgehen, indem Sie mehrere Shader verwenden, aber ich dachte, das wäre einfacher für das, was wir versuchen.

 this.GL.bindBuffer (this.GL.ELEMENT_ARRAY_BUFFER, Model.Triangles); // Generate The Perspective Matrix var PerspectiveMatrix = MakePerspective (45, this.AspectRatio, 1, 1000.0); var TransformMatrix = Model.GetTransforms ();

Wieder ist dieser Teil der Funktion immer noch derselbe.

 var NormalsMatrix = MatrixTranspose (InverseMatrix (TransformMatrix));

Hier berechnen wir die Normalen-Transformationsmatrix. Ich werde das besprechen MatrixTranspose () und InverseMatrix () funktioniert in einer Minute. Um die Transformationsmatrix für das Normalen-Array zu berechnen, müssen Sie die inverse Matrix der regulären Transformationsmatrix des Objekts transponieren. Mehr dazu später.

 // Setze Slot 0 als aktive Textur this.GL.activeTexture (this.GL.TEXTURE0); // Lade die Textur in den Speicher this.GL.bindTexture (this.GL.TEXTURE_2D, Model.Image); // Aktualisieren Sie den Texture Sampler im Fragment-Shader, um Steckplatz 0 zu verwenden. This.GL.uniform1i (this.GL.getUniformLocation (this.ShaderProgram, "uSampler"), 0); // Setze die Perspektive und die Transformationsmatrizen var pmatrix = this.GL.getUniformLocation (this.ShaderProgram, "PerspectiveMatrix"); this.GL.uniformMatrix4fv (pmatrix, false, neuer Float32Array (PerspectiveMatrix)); var tmatrix = this.GL.getUniformLocation (this.ShaderProgram, "TransformationMatrix"); this.GL.uniformMatrix4fv (tmatrix, false, neuer Float32Array (TransformMatrix)); var nmatrix = this.GL.getUniformLocation (this.ShaderProgram, "NormalTransformation"); this.GL.uniformMatrix4fv (nmatrix, false, neuer Float32Array (NormalsMatrix)); // Draw The Triangles this.GL.drawElements (this.GL.TRIANGLES, Model.TriangleCount, this.GL.UNSIGNED_SHORT, 0); ;

Sie können die Quelle jeder WebGL-Anwendung einfach anzeigen, um mehr zu erfahren.

Dies ist der Rest der Zeichnen() Funktion. Es ist fast genauso wie zuvor, aber es gibt den hinzugefügten Code, der die Normalmatrix mit den Shader verbindet. Kommen wir nun zu den beiden Funktionen zurück, mit denen ich die Normalen-Transformationsmatrix erhalten habe.

Das InverseMatrix () Funktion akzeptiert eine Matrix und gibt ihre inverse Matrix zurück. Eine inverse Matrix ist eine Matrix, die, wenn sie mit der ursprünglichen Matrix multipliziert wird, eine Identitätsmatrix zurückgibt. Um dies zu verdeutlichen, betrachten wir ein einfaches Algebra-Beispiel. Die Umkehrung der Zahl 4 ist 1/4, weil 1/4 x 4 = 1. Das Äquivalent "Eins" in Matrizen ist eine Identitätsmatrix. deshalb, die InverseMatrix () Funktion gibt die Identitätsmatrix für das Argument zurück. Hier ist diese Funktion:

 Funktion InverseMatrix (A) var s0 = A [0] * A [5] - A [4] * A [1]; var s1 = A [0] * A [6] - A [4] * A [2]; var s2 = A [0] * A [7] - A [4] * A [3]; var s3 = A [1] * A [6] - A [5] * A [2]; var s4 = A [1] * A [7] - A [5] * A [3]; var s5 = A [2] * A [7] - A [6] * A [3]; var c5 = A [10] * A [15] - A [14] * A [11]; var c4 = A [9] * A [15] - A [13] * A [11]; var c3 = A [9] * A [14] - A [13] * A [10]; var c2 = A [8] * A [15] - A [12] * A [11]; var c1 = A [8] * A [14] - A [12] * A [10]; var c0 = A [8] * A [13] - A [12] * A [9]; var invdet = 1,0 / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0); var B = []; B [0] = (A [5] * c5 - A [6] * c4 + A [7] * c3) * invdet; B [1] = (-A [1] * c5 + A [2] * c4-A [3] * c3) * invdet; B [2] = (A [13] * s5 - A [14] * s4 + A [15] * s3) * invdet; B [3] = (-A [9] * s5 + A [10] * s4 - A [11] * s3) * invdet; B [4] = (-A [4] * c5 + A [6] * c2 - A [7] * c1) * invdet; B [5] = (A [0] * c5 - A [2] * c2 + A [3] * c1) * invdet; B [6] = (-A [12] * s5 + A [14] * s2 - A [15] * s1) * invdet; B [7] = (A [8] * s5 - A [10] * s2 + A [11] * s1) * invdet; B [8] = (A [4] * c4 - A [5] * c2 + A [7] * c0) * invdet; B [9] = (-A [0] * c4 + A [1] * c2 - A [3] * c0) * invdet; B [10] = (A [12] * s4 - A [13] * s2 + A [15] * s0) * invdet; B [11] = (-A [8] * s4 + A [9] * s2 - A [11] * s0) * invdet; B [12] = (-A [4] * c3 + A [5] * c1 - A [6] * c0) * invdet; B [13] = (A [0] * c3 - A [1] * c1 + A [2] * c0) * invdet; B [14] = (-A [12] * s3 + A [13] * s1 - A [14] * s0) * invdet; B [15] = (A [8] * s3 - A [9] * s1 + A [10] * s0) * invdet; Rückgabe B; 

Diese Funktion ist ziemlich kompliziert, und um die Wahrheit zu sagen, verstehe ich nicht ganz, warum die Mathematik funktioniert. Aber ich habe den Kern oben bereits erklärt. Ich habe mir diese Funktion nicht ausgedacht; Es wurde in ActionScript von Robin Hilliard geschrieben.

Die nächste Funktion, MatrixTranspose (), ist viel einfacher zu verstehen. Sie gibt die "transponierte" Version ihrer Eingabematrix zurück. Kurz gesagt, dreht es die Matrix nur auf ihrer Seite. Hier ist der Code:

Funktion MatrixTranspose (A) return [A [0], A [4], A [8], A [12], A [1], A [5], A [9], A [13], A [ 2], A [6], A [10], A [14], A [3], A [7], A [11], A [15]]; 

Anstatt in horizontalen Reihen (d. H. A [0], A [1], A [2] ...) zu gehen, geht diese Funktion vertikal nach unten (A [0], A [4], A [8] ...)..

Nachdem Sie diese beiden Funktionen hinzugefügt haben, können Sie dies tun WebGL.js Datei, und jedes Modell, das die Normaldaten enthält, sollte schattiert sein. Sie können mit der Richtung und der Farbe des Lichts im Vertex-Shader herumspielen, um verschiedene Effekte zu erzielen.

Es gibt ein letztes Thema, das ich behandeln möchte, und fügt der Szene 2D-Inhalte hinzu. Das Hinzufügen von 2D-Komponenten zu einer 3D-Szene kann viele Vorteile haben. So können Sie beispielsweise Koordinateninformationen, eine Minikarte und Anweisungen für Ihre App anzeigen, und die Liste wird fortgesetzt. Dieser Prozess ist nicht so einfach, wie Sie vielleicht denken, also schauen wir uns das mal an.


2D V.S. 2.5D

Mit HTML können Sie die WebGL-API und die 2D-API nicht von derselben Leinwand aus verwenden.

Sie denken vielleicht: "Warum nutzen Sie nicht einfach die in HTML5 2D API eingebaute Leinwand?" Nun, das Problem ist, dass Sie mit HTML die WebGL-API und die 2D-API nicht von derselben Leinwand aus verwenden können. Wenn Sie WebGL den Kontext der Zeichenfläche zugewiesen haben, können Sie ihn nicht mit der 2D-API verwenden. HTML5 kehrt einfach zurück Null wenn Sie versuchen, den 2D-Kontext abzurufen. Wie kommst du dann um? Nun, ich gebe dir zwei Möglichkeiten.

2.5D

Für diejenigen, die es nicht wissen, ist 2.5D, wenn Sie 2D-Objekte (Objekte ohne Tiefe) in eine 3D-Szene einfügen. Das Hinzufügen von Text zu einer Szene ist ein Beispiel für 2.5D. Sie können den Text aus einem Bild übernehmen und als Textur auf eine 3D-Ebene anwenden oder ein 3D-Modell für den Text erhalten und auf Ihrem Bildschirm rendern.

Der Vorteil dieses Ansatzes ist, dass Sie nicht zwei Leinwände benötigen. Das Zeichnen wäre schneller, wenn Sie in Ihrer Anwendung nur einfache Formen verwenden.

Aber für Dinge wie Text benötigen Sie entweder Bilder von allem, was Sie schreiben möchten, oder ein 3D-Modell für jeden Buchstaben (meiner Meinung nach etwas übertrieben)..

2D

Die Alternative ist, eine zweite Leinwand zu erstellen und diese auf der 3D-Leinwand zu überlagern. Ich bevorzuge diesen Ansatz, weil er für das Zeichnen von 2D-Inhalten besser geeignet ist. Ich werde nicht anfangen, ein neues 2D-Framework zu erstellen, sondern ein einfaches Beispiel, in dem wir die Koordinaten des Modells zusammen mit der aktuellen Drehung anzeigen. Fügen Sie der HTML-Datei direkt nach der WebGL-Zeichenfläche eine zweite Zeichenfläche hinzu. Hier ist die neue Leinwand zusammen mit der aktuellen:

  Ihr Browser unterstützt nicht die Leinwand von HTML5.   Ihr Browser unterstützt nicht die Leinwand von HTML5. 

Ich habe auch ein paar Inline-CSS hinzugefügt, um die zweite Leinwand über die erste zu legen. Im nächsten Schritt erstellen Sie eine Variable für die 2D-Leinwand und erhalten ihren Kontext. Ich werde das im tun Bereit() Funktion. Ihr aktualisierter Code sollte ungefähr so ​​aussehen:

var GL; var Gebäude; var Canvas2D; Funktion Ready () // Gl Deklaration und Modell laden Funktion Hier Canvas2D = document.getElementById ("2DCanvas"). getContext ("2d"); Canvas2D.fillStyle = "# 000"; 

Oben sehen Sie, dass ich eine globale Variable für die 2D-Leinwand hinzugefügt habe. Dann fügte ich zwei Zeilen am unteren Rand des hinzu Bereit() Funktion. Die erste neue Zeile erhält den 2D-Kontext und die zweite neue Zeile setzt die Farbe auf Schwarz.

Der letzte Schritt ist das Zeichnen des Textes in Aktualisieren() Funktion:

function Update () Building.Rotation.Y + = 0.3 // Die Leinwand von der vorherigen Zeichnung löschen Canvas2D.clearRect (0, 0, 600, 400); // Titeltext Canvas2D.font = "25px serifenlos"; Canvas2D.fillText ("Building", 20, 30); // Eigenschaften des Objekts Canvas2D.font = "16px serifenlos"; Canvas2D.fillText ("X:" + Building.Pos.X, 20, 55); Canvas2D.fillText ("Y:" + Building.Pos.Y, 20, 75); Canvas2D.fillText ("Z:" + Building.Pos.Z, 20, 95); Canvas2D.fillText ("Rotation:" + Math.floor (Building.Rotation.Y), 20, 115); GL.GL.clear (16384 | 256); GL.Draw (Gebäude); 

Zunächst drehen wir das Modell um seine Y-Achse und löschen dann die 2D-Leinwand aller vorherigen Inhalte. Als Nächstes legen wir die Schriftgröße fest und zeichnen für jede Achse etwas Text. Das fillText () Die Methode akzeptiert drei Parameter: den zu zeichnenden Text, die X-Koordinate und die Y-Koordinate.

Die Einfachheit spricht für sich. Das war vielleicht ein bisschen übertrieben, um einfachen Text zu zeichnen. Sie hätten den Text einfach in eine Position schreiben können

oder

Element. Wenn Sie jedoch etwas wie Zeichnen von Formen, Sprites, eine Gesundheitsleiste usw. tun, ist dies wahrscheinlich die beste Option.


Abschließende Gedanken

Im Rahmen der letzten drei Tutorials haben wir eine ziemlich schöne, wenn auch einfache 3D-Engine erstellt. Trotz seiner primitiven Natur bietet es Ihnen eine solide Basis, auf der Sie arbeiten können. Ich schlage vor, andere Frameworks wie Three.js oder Glge anzuschauen, um eine Vorstellung davon zu bekommen, was möglich ist. Darüber hinaus wird WebGL im Browser ausgeführt, und Sie können die Quelle jeder WebGL-Anwendung problemlos anzeigen, um weitere Informationen zu erhalten.

Ich hoffe, dass Ihnen diese Tutorial-Serie gefallen hat, und lassen Sie Ihre Kommentare und Fragen wie immer im Kommentarbereich weiter unten.