WebGL - 3D im Browser

siehe auch WebGPU!
 Home
 WebGL Api Spickzettel
 WebGL Sicherheit

Tutorial
 0 : WebGL Browser
 1 : Das erste Dreieck
 2 : 3D-Mathematik
 3 : Farbe
 4 : Animation
 5 : Interaktion I
 6 : Texturen
 7 : Beleuchtung I
 8 : Interaktion II

Links
 WebGL Beispiele
 WebGL Frameworks
 ext. WebGL Tutorials


 Kontakt / Impressum
 webgl ([ät)] peter-strohm Punkt de

3 Farbe

>>>> Direkt zum Beispiel <<<<     >>>Quellcode zum Beispiel <<<



Nachdem Kapitel 2 sehr mathematiklastig wird es jetzt wieder mehr um die Javascript-Architektur und schöne bunte Farben gehen.

Der Quellcode zum Beispiel aus Kapitel 1 hatte bewusst keine nennenswerte Strukturierung. Alle erforderlichen Schritte zur Darstellung des Dreiecks wurden nacheinander innerhalbe einer großen Funktion abgearbeitet.

Das wird sich jetzt ändern. Obwohl funktional "nur" die Farbe hinzukommt, sieht ist der Quellcode nicht wiederzuerkennen. Ich habe den Ablauf in einzelne in sich zusammengehörige Funktionen unterteilt und einen Hauch von Objektorientierung verwendet, soweit Javascript dies zulässt und es mir sinnvoll erscheint.

Was die Funktionalität des Beispiels betrifft, werden das Beispiel aus Kapitel 1 so erweitern, dass jedem Eckpunkt des Dreiecks ein eigener Farbwert zugeordnet werden kann.
Die Fläche zwischen den Eckpunkten (also die Dreiecksfläche) wird mit interpolierten Farbwerten gefüllt.

So sieht's aus:
Farbiges Dreieck

Bild 3.1 : Screenshot zum Beispiel 3

hier geht's zur Live-Version (WebGL-fähiger Browser erforderlich)

Zunächst habe ich den Quellcode im Vergleich zum ersten Beispiel etwas umstrukturiert:
Am Anfang werden zwei zusätzliche Javascript-Dateien eingebunden:
6<script src="webgl-utils.js" ></script>
7<script src="gl-matrix.js" ></script>

webgl-utils.js wird freundlicherweise von Google zur Verfügung gestellt und hilft bei der browserunabhängigen Initialisierung des WebGL-Contexts.

gl-matrix.js ist eine speziell für WebGL angelegte Bibliothek von Matrix- und Vektorfunktionen. Darin findest Du auch eine Kapselung der in Tutorial zwei beschriebenen Perspektiv- und Projektionsmatrix, die ich verwenden werde.

In kapitel3.html erstelle ich das globale Objekt WebGLApplication mit zunächst sechs Properties (Eigenschaften, Elementen):

21WebGLApplication.Core = (function () {
22    var members = {
23        // Zeiger zum WebGLProgram-Objekt
24        programObject: 0,
25        canvas: null,
26        gl: null,
27        pMatrix: null,
28        model: null,
29        shaderVarLocations: null
30    };


Die Kapselung der Properties wird über die folgenden setMember und getMember Methoden realisiert. Die Technik ist einem Heise-Artikel von Jan Petzold entnommen.

31return {
32    getMember: function (key) {
33        if (typeof members[key] !== 'undefined') {
34            return members[key];
35        } else {
36            return false;
37        }
38    },
39    setMember: function (key, value) {
40        members[key] = value;
41    }
42};


Der Rest des Code Refactorings ist weniger aufregend als es vielleicht beim ersten Blick auf den Quellcode erscheinen mag.
Die Methoden
init
bufferModel
setupViewpoint
und
drawScene
werden nacheinander abgearbeitet und machen genau das, was ihr Name andeutet. :-D

Kommen wir nun endlich zur Farbe:

3.1 Der Weg der Farbe - rückwärts

Es sind mehrere Schritte zu programmieren bzw. Stufen zu durchlaufen, bevor die Farbe auf dem Dreieck ankommt. Ich werde hier diese Schritte "rückwärts" erläutern. D.h. wir beginnen beim letzten Schritt, dem Fragmentshader.

Wie kommen nun die Farben auf das Dreieck? :
Eine wichtige Rolle hierbei spielen wieder der Vertex- und der Fragment-Shader. (Du erinnerst dich: Shader sind die Mini-Programme, die in der Grafikkarte ausgeführt werden)
Sehen wir uns zunächst die Veränderung im Fragment-Shader an:

Neu : Fragmentshader mit Farbe kapitel3.html Alt : alles weiß kapitel1.html
248'precision mediump float;\n\
249 varying vec4 vFarbe; \n\
250 void main() \n\
251 { \n\
252     gl_FragColor = vFarbe;\n\
253 } \n';
62'precision mediump float;\n\
63void main() \n\
64{ \n\
65    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n\
66} \n';

Wie du im direkten Vergleich siehst, wurde die Variable vColor neu eingeführt. Sie ist als vec4 deklariert, hat also 4 Element: rot, grün, blau und Opazität. Die fest definierte WebGL-Variable gl_FragColor wird mit dem Wert von vColor belegt. Mit anderen Worten: vColor wird "durchgereicht".
Wie kommt nun der Farbwert in vColor? Zur Beantwortung dieser Frage müssen wir uns zunächst mit dem Schlüsselwort varying auseinandersetzen.
Variablen die mit dem Schlüsselwort varying gekennzeichnet sind bilden eine Schnittstelle zwischen Vertex- und Fragment-Shader. Sie sind im Vertexshader AUSGANGSvariablen und im Fragmentshader EINGANGSvariablen. Demzufolge müssen sie in beiden Shadern vom gleichen Typ sein und den gleichen Namen tragen wie du hier im neuen Vertex-Shader sehen kannst:

236'attribute vec4 av4Position; \n\
237 attribute vec4 av4VertexFarbe; \n\
238 uniform mat4 um4PerspektivMatrix; \n\
239 uniform mat4 um4ModelviewMatrix; \n\
240 varying vec4 vFarbe; \n\
241 void main() \n\
242 { \n\
243     gl_Position = um4PerspektivMatrix * um4ModelviewMatrix * av4Position; \n\
244     vFarbe = av4VertexFarbe; \n\
245 } \n';


Wie du siehst passiert auch im Vertex-shader mit der Farbe nichts aufregendes: Sie wird als attribute vec4 av4VertexFarbe in den Vertexshader hineingegeben und als vFarbe wie gerade beschrieben an den Fragmentshader weitergegeben. Kurz gesagt: die Farbe wird unverändert durchgereicht.

Dir fällt sicherlich auf, dass Zeile 1 und 2 des Vertexshaders sehr ähnlich aussehen. Position und Farbe der einzelnen Vertices werden hier praktisch gleich behandelt: Beides sind Vektoren die "von außen" mit Inhalt gefüllt werden.

Hier als kleiner Einschub die drei Schlüsselwörter vor Variablen in den Shader-scripten in der Übersicht:

attribute Attribute (attribute) sind Variablen, die sich speziell auf Vertices beziehen und nur im Vertex-Shader verwendet werden. Typisches Beispiel sind die Koordinaten (x,y,z). Ihr Wert unterscheidet sich i.d.R. von einem Vertex zum nächsten.
uniform Als uniform werden Shadervariablen gekennzeichnet, die außerhalb der Shader "einheitlich" (uniform) festgelegt werden. Sie können sowohl in Vertex- als auch in Fragmentshader verwendet werden. Ein Beispiele sind die Modelview oder Perspektivmatrix. Beide werden außerhalb der Shader berechnet und in den Shadern nicht mehr verändert.
varying "varying" Variablen bilden die Schnittstelle zwischen Vertex- und Fragmentshader. Ihr Inhalt wird vom Vertex-Shader an den Fragmentshader weitergegeben. Varyings müssen in beiden Shadern mit jeweils gleichem Namen und Typ deklariert werden.
Varying werden bei der Rasterung automatisch zwischen den Vertices interpoliert. Im Beispiel zu diesem Kapitel sieht man diese Interpolation sehr gut am Farbverlauf des Dreiecks: die drei Vertices haben festgelegte Farben, die Fläche dazwischen wird vom Fragmentshader mit interpolierten "Zwischenfarben" gefüllt.
Tabelle 3.1 : die drei Shader-Spezialvariablentypen

Die Schlüsselwörter in Tabelle 3.1 werden auch bei anderen Themen wieder auftauchen.

Verfolgen wir den Weg der Farbe weiter. Zuletzt hast du gesehen, dass der Farbwert durch den Vertex-Shader durchgereicht wird. Wie kommt der Wert nun in den Vertex-Shader hinein?
Die Antwort liegt in Zeile 68 von kapitel3.html. Dort wird mit der update()-Methode von ShaderVarLocations ermittelt, welche "Adressen" die Attribute und Uniforms im Vertex-Shader haben.
Mit diesen Adressen wird WebGL in der drawScene() Methode in den Zeilen 211 - 213 (für die Farbe) mitgeteilt, zu welcher Buffer zu welchem Attribut gehört und in welchem Datenformat der Buffer zu lesen ist (4 Element bilden eine Farbe, alle Elemente sind vom Typ gl.FLOAT).

211gl.bindBuffer(gl.ARRAY_BUFFER, Model.colorBuffer);
212gl.vertexAttribPointer(shaderVarLocations.colorAttribute, 4, gl.FLOAT, false, 0, 0);
213gl.enableVertexAttribArray(shaderVarLocations.colorAttribute);


Das zugehörige Float32Array mit den drei vierdimensionalen Farbwerten wurde bereits in den Zeilen 185 bis 192 erzeugt und als Buffer an die Shader übergeben.

185Model.colors = new Float32Array([
186    1.0, 0.0, 0.0, 1.0, //rot
187    0.0, 1.0, 0.0, 1.0, //grün
188    0.0, 0.0, 1.0, 1.0]); //blau
189
190Model.colorBuffer = gl.createBuffer();
191gl.bindBuffer(gl.ARRAY_BUFFER, Model.colorBuffer);
192gl.bufferData(gl.ARRAY_BUFFER, Model.colors, gl.STATIC_DRAW);


Wenn dir die RGBAlpha Farbdarstellung geläufig ist, kannst du hier die Farben der einzelnen Dreieckspunkte verändern. Die Farbwerte zwischen den Vertices wird der Fragmentshader automatisch für jeden Pixel interpolieren.
createBuffer() erzeugt einen neuen Puffer im Grafikspeicher. Die Daten werden mit bindBuffer und bufferData an WebGL bzw. die GPU übergeben.

Ich habe den Signalfluss der Farbe in dieseem Kapitel "von innen nach außen" beschrieben. Wenn Du das soweit nachvollzogen hast, kannst du dir die parallele Kette der VertexPOSITIONEN im Quellcode anschauen. Das Prinzip ist sehr ähnlich.

Im nächsten Kapitel werde ich mich dem Thema Animation zuwenden. Es bleibt spannend!
Thanks to Giles Thomas and his project LearningWebGL.com

<< Kapitel 2 <<    >> Startseite <<   ^ Seitenanfang ^     >> Kapitel 4 >>
Fehler? Kommentare? webgl ([ät)] peter-strohm Punkt de