WebGL Grundlagen 2 – Farben hinzufügen.
Willkommen zum unserem zweiten WebGL Grundlagenteil. Dieses Mal werden wir uns anschauen, wie wir Farbe in die Szene bringen können.
Das folgende Video veranschaulicht das Hinzufügen von Farben:
Jetzt erfahren Sie genauer, wie das Ganze funktioniert:
Ein kurzer Hinweis: Dieser Teil richtet sich an Personen mit fortgeschrittenen Programmierkenntnissen, aber ohne wirkliche Erfahrungen in 3D. Sie werden den Code verstehen und es zum Laufen bringen, so dass nichts mehr der Erstellung ihrer Website in 3D im Wege steht. Wenn Sie den ersten Teil noch nicht gelesen haben, sollten Sie dies vorher tun.
Es existieren genaue zwei Möglichkeiten den Code für dieses Beispiel zu generieren. Zum Einen können Sie einfach „View Source“ verwenden, während Sie sich die Live-Version ansehen. Alternativ könnten Sie GitHub verwenden und den Code aus dem dortigen Repository kopieren. Unabhängig von der verwendeten Methode können Sie den Code mit ihrem bevorzugten Texteditor laden und sich genauer anschauen.
Das meiste davon wird starke Ähnlichkeiten zum ersten Teil aufweisen. Wir beginnen wieder von oben nach unten:
- Definition von Vertex– und Fragment-Shadern mittels HTML script-Tags mit den Typen „x-shader/x-vertex“ und „x-shader/x-fragment“.
- Initialisieren eines WebGL-Kontextes in initGL.
- Laden der Shader mit getShader und initShaders in ein WebGL-Programmobjekt.
- Definieren Sie die Model-View-Matrix mvMatrix und die Projektionsmatrix pMatrix sowie die Funktion setMatrixUniforms, um Sie über die JavaScript/WebGL-Teilung zu schieben, damit Sie die Shader sehen können.
- Laden von Puffern mit Informationen über die Objekte in der Szene mit initBuffers.
- Zeichnen der Szene in der entsprechend benannten DrawScene.
- Definieren Sie eine Funktion webGLStart, um alles von vornherein einzurichten.
- Schließlich stellen wir ihnen das minimale HTML zur Verfügung, welches Sie benötigen um alles darzustellen.
Die einzigen Veränderungen, die sich in diesem Code verändert haben sind die Shader, initShader und die DrawScene-Funktion. Um zu erklären, wie die Änderungen funktionieren, müssen Sie etwas mehr über die WebGL-Rendering–Pipeline erfahren.
Wir werden im Folgenden aufzeigen, wie die an JavaScript-Funktionen in drawScene übergebenen Daten in Pixel umgewandelt werden, die in dem WebGL-Canvas auf dem Bildschirm angezeigt werden. Es werden lediglich die notwendigen Schritte veranschaulicht. Detailliertere Informationen werden in kommenden Beiträgen folgen.
Dies geschieht dadurch, dass der Vertex-Shader für jeden Vertex einmal aufgerufen wird, jeden Mal mit den Attributen, die für den Vertex entsprechend eingestellt sind. Die einheitlichen Variablen werden ebenfalls übergeben, ändern Sie sich nicht von Aufruf zu Aufruf. Der Vertex-Shader beeinflusst die Daten in der Weise und setzt seine Ergebnisse in Dinge um, die als variierende Variablen bezeichnet werden. Es kann eine Reihe von Variablen ausgeben werden. Eine Variable mit der Bezeichnung gl_Position ist obligatorisch, die die Koordinaten des Scheitelpunktes enthält, sobald der Shader damit fertig ist.
Sobald der Vertex-Shader fertig ist, wandelt WebGL das 3D-Bild aus diesen Variablen in ein 2D-Bild um und ruft dann den Fragment-Shader einmal für jedes Pixel im Bild auf. Dies bedeutet natürlich, dass es den Fragment-Shader für diejenigen Pixel aufruft, die keine Ecke in ihnen haben, d.h. für diejenigen, die zwischen den Pixeln liegen und auf denen die Ecken enden. Dazu füllt er Punkte in die Positionen zwischen den Eckpunkten über einen Prozess, der lineare Interpolation genannt wird. Für die Eckpunkte, die unser Dreieck bilden, füllt dieser Prozess den von den Eckpunkten abgegrenzten Raum mit Punkten aus, um ein sichtbares Dreieck zu bilden. Der Zweck des Fragmentshaders ist es die Farbe für jeden dieser interpolierten Punkte zurückzugeben und zwar in einer Variable mit der Bezeichnung gl_FragColor.
Sobald der Fragment-Shader fertig ist, werden seine Ergebnisse mit ein wenig mehr von WebGL herumgespielt und Sie werden in den Frame Buffer gelegt, was letztendlich auf dem Bildschirm angezeigt wird.
Im Kern möchten wir in diesem Beitrag ihnen den Trick veranschaulichen, wie man die Farbe für die Eckpunkte vom JavaScript-Code bis zum Fragment-Shader bekommt, wenn kein direkter Zugriff von einem zum anderen möglich ist.
Die Art und Weise, wie wir das tun können ist der Tatsache geschuldet, dass wir eine Reihe von Variablen aus dem Vertex-Shader herausgeben können und Sie dann im Fragment-Shader abrufen können. Wir geben die Farbe also an den Vertex-Shader weiter, der Sie dann direkt in eine Variable einfügen kann, die der Fragment-Shader aufnimmt.
Praktischerweise erhalten wir dadurch kostenlos Farbverläufe. Alle vom Vertex-Shader gesetzten Variablen werden linear interpoliert, wenn die Fragmente zwischen den Knoten erzeugt werden und nicht nur die Positionen. Die lineare Interpolation der Farbe zwischen den Eckpunkten ergibt glatte Farbverläufe, wie Sie im Dreieck im Bild oben zu sehen sind.
Schauen wir uns den Code an. Wir werden die Änderungen zu unserem ersten Grundlagenteil durcharbeiten. Im neuen Code sehen wir einige Veränderungen insbesondere in Bezug auf den Vertex-Shader:
Wir haben zwei Attribute. Was bedeutet das jetzt genau? Wir haben mit aVertexPosition und aVertexColor zwei Konstanten, die uMVMatrix und uPMatrix genannt werden und eine Ausgabe in Form einer Variable, die vColor genannt wird.
Im Körper des Shaders berechnen wir die gl_Position auf genau die gleiche Weise wie im ersten Grundlagenteil. Um Farbe hinzuzufügen müssen wir Sie direkt vom Input-Attribut zur Ausgabevariablen weiterleiten.
Sobald dies für jeden Vertex ausgeführt wurde, wird die Interpolation durchgeführt, um die Fragmente zu erzeugen und diese werden an den Fragment-Shader weitergeleitet.
Hier nehmen wir die floating-point precision boilerplate die Eingangsvariable vColor, die aus der linearen Interpolation hervorgegangene, glatt gemischte Farbe enthält und geben Sie sofort als Farbe für dieses Fragment zurück.
Das sind alle Unterschiede in den Shadern zwischen diesem Teil und der Letzten. Es gibt aber noch zwei weitere Änderungen. Die erste ist sehr klein. In initShaders erhalten wir jetzt Verweise auf zwei Attribute statt auf ein Attribut. Die zusätzlichen Zeilen sind unten rot hervorgehoben.
Dieser Code, um die Attributpositionen zu erhalten, die wir im ersten Grundlagenteil bis zu einem gewissen Grad verfeinert haben, sollte jetzt ziemlich klar sein. Sie sind es, die wir an den Vertex-Shader für jeden Vertex übergeben möchten. Im ersten Grundlagenteil haben wir gerade das Attribut für die Vertex-Positionen erhalten. Jetzt bekommen wir auch das Farbattribut.
Der Rest der Änderungen in diesem Teil betrifft initBuffers, das nun Puffer für die Vertexpositionen und die Vertexfarben einrichten muss. DrawScene hingegen muss beides an WebGL übergeben.
Wenn wir uns die initBuffers zuerst ansehen, definieren wir neue globale Variablen, die die Farbpuffer für das Dreieck und das Quadrat enthalten:
Nachdem wir den Vertex-Positionspuffer des Dreiecks erstellt haben, geben wir seine Vertex-Farben an:
Die Werte, die wir für die Farben angeben, befinden sich in einer Liste. In dieser Liste befinden sich auch die Werte für jeden Scheitelpunkt und die Positionen. Es gibt jedoch einen interessanten Unterschied zwischen den beiden Array-Puffern. Während die Positionen der Eckpunkte als jeweils drei Zahlen für die X-, Y- und Z-Koordinaten angegeben sind, werden ihre Farben als jeweils vier Elemente angegeben – Rot, Grün, Blau und Alpha. Alpha ist ein Wert für die Undurchsichtigkeit und wird in späteren Abschnitten nützlich sein. Diese Änderung der Elemente pro Item im Puffer erfordert eine Änderung der ItemSize, die wir mit ihr verknüpfen.
Als nächstes machen wir den äquivalenten Code für das Quadrat. Dieses Mal verwenden wir die gleiche Farbe für jeden Scheitelpunkt, also generieren wir die Werte für den Puffer mit Hilfe einer Schleife:
Jetzt haben wir alle Daten für unsere Objekte in einem Satz von vier Puffern, so dass die nächste Änderung darin besteht, dass drawScene die neuen Daten verwendet. Der neue Code ist wieder rot und sollte leicht verständlich sein:
Das war alles, was nötig war, um unserer WebGL-Szene Farbe zu verleihen. Hoffentlich sind Sie jetzt auch mit den Grundlagen von Shadern vertraut und wissen, wie Daten zwischen ihnen ausgetauscht werden.