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
				 
				 
			  
			  
			   | 
                          
1 Das erste Dreieck
 
     >>>> Direkt zum Beispiel <<<< 
	   
	>>>Quellcode zum Beispiel <<<
   
Ich gehe jetzt davon aus, dass du schon WebGL-Beispiele die andere Leute 
programmiert haben in deinem Browser gesehen hast und damit die 
Grundvoraussetzung zum Start in die eigene Entwicklung gegeben ist. Die 
Softwareanforderungen sind damit auch schon erfüllt. Zum Erstellen von 
WebGL-Seiten benötigst du außer einem Browser 
(get.webgl.org 
gibt Auskunft über geeignete Browser) nur noch einen Texteditor wie z.B. 
Notepad++.  
Da WebGL im Internet-Browser dargestellt wird, muss es in HTML-Dateien 
eingebettet werden. Wenn du nicht weißt was HTML ist, bis du hier falsch. 
HTML ist zwar nicht schwierig zu erlernen und man muss auch kein Experte sein 
um in WebGL einzusteigen, aber ein paar Grundkenntnisse sollten schon da sein.
 Das gleiche gilt für JavaScript und allgemeine Grundlagen der 
 Programmierung.
  
  
 
 1.1  Was ist WebGL und was ist 
es nicht ?
WebGL ist die Integration von OpenGL (genaugenommen OpenGL ES 2.0) in JavaScript. 
  Somit bietet WebGL eine Möglichkeit, rechenintensive Grafiken 
(i.d.R. 3D-Szenen) innerhalb von Webseiten zu verwenden. 
WebGL ist NICHT eine beschreibende Sprache, in der du so etwas wie  
<3DWuerfel x=1.0 y=1.0 z=1.0> 
schreiben kannst und dann einen 3dimensionalen Würfel siehst. Zumindest 
nicht ohne Hilfsmittel, aber dazu später mehr. 
Ich möchte damit ausdrücken, dass WebGL ziemlich weit unten 
anfängt. Wer WebGL programmieren will, sollte wissen was eine Matrix ist 
(in der Mathematik, nicht im Kino!), wie man in der räumlichen Geometrie 
mit Vektoren rechnet und Trigonometrische Funktionen anwendet. 
 
Hallo ? Ist da noch jemand ? 
 
Gut, ich werde natürlich auf die mathematischen Aspekte eingehen, aber 
mit pMathe = 0x0000000 wird's sehr viel Copy und Paste für dich. 
 
 1.2  Auf ins kalte Wasser
Als allererstes Beispiel wird hier die kleinste "halbwegs sinnvolle" 
WebGL-Seite vorgestellt. Da auch die komplexesten 3D-Szenen letztlich aus 
einzelnen Dreiecken zusammengesetzt sind, stellt diese erste Anwendung ein 
einzelnes weißes Dreieck dar.
Das erste Dreieck mit WebGL
  
   Abbildung 1.1 : Screenshot des ersten Dreiecks
 
 
Der Quelltext ist so 
angelegt, dass er komplett ohne zusätzliche JavaScript-Biblotheken oder 
sonstige externe Resourcen auskommt. Alles passiert nacheinander innerhalb 
einer Datei. Üblicherweise und in den nachfolgenden Kapiteln werden die 
Standardoperationen wie die Initialisierung in eigene .js-Dateien ausgelagert, 
was jedoch gerade für den Einstieg die übersicht erschwert. 
 			
Jetzt springen wir ans Ende der Quellcodedatei in Zeile 110. Dort sind wir 
mitten im HTML-Umfeld das Dir einigermaßen bekannt sein sollte. 
 
| 110 | <canvas id="meineWebGLCanvas" width="500" height="500"></canvas> 
 |  
  
Diese Zeile platziert eine Zeichenfläche ("Canvas") auf der HTML-Seite. 
Mit minimalen Englischkenntnissen ist es nicht schwer zu erahnen, wie hoch und 
breit diese Zeichnungsfläches sein wird... Innerhalb dieser Fläche 
wird die WebGL-Szene dargestellt.
Die id="meineWebGLCanvas" benötigen wir später noch, um Javascript 
mitzuteilen, wo das WebGL-Geraffel dargestellt werden soll. 
 
Bevor wir mit der Theorie weitermachen (bzw. richtig anfangen) verrate ich dir 
die Stellen, an denen du schon jetzt direkt Einfluss auf das dargestellte 
Dreieck nehmen kannst: 
In Zeilen 88-90 stehen sechs Zahlen. Diese geben die Eckpunkte des Dreiecks in 
karthesischen Koordinaten an (siehe Kommentare im Quellcode).  
Da in diesem rudimentär-Beispiel noch keine echte 3D-Projektion verwendet 
wird, können die Z-Koordinaten alle auf 0.0 gesetzt sein. Später 
wird die Z-Koordinate die Tiefe im Raum angeben, wobei die negative z-Achse 
"in den Bildschirm hinein" zeigt. (Du brauchst nicht zu versuchen, in diesem 
Beispiel die Z-Werte zu ändern; da passiert noch nichts!)  
Jetzt kannst du es wagen: Falls noch nicht geschehen, lade die Datei 
"kapitel1.html" in ein lokales Verzeichnis auf 
deinem Rechner und experimentiere mit den Koordinaten. Setze das Dreieck nach 
links oder rechts, mache es spitzer oder stumpfer. 
 
Fertig? Ich hoffe du hast nun ein Gefühl für den Zusammenhang 
zwischen der Canvas-Zeichenfläche und den drei Dreieckspunkten. 
Die zweite Stelle im Quellcode, an der du jetzt schon Einfluss nehmen kannst 
ist in Zeile 65 der Datei: 
 
| 65 | gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n\
 |  
  
 
Hier kannst du die Farbe des Dreiecks ändern. Die vier 1.0-Werte stehen 
für RGBA (Rot,Grün,Blau,Alpha). Es würde zu weit (zurück) 
führen hier dieses Farbschema zu erklären. Alpha beschreibt 
Opazität (Gegenteil von Transparenz); Alpha=0 --> Dreieck ist 
unsichtbar. 
 
 1.3  Initialisierung und 
Struktur
In diesem Beispiel beziehe ich mich nur auf die Browser FireFox ab Version 4.0 
und Google Chrome ab Version 9.0. Zukünftig sollte es auch für 
weitere Browser funktionieren. 
Wir wollen uns nun anschauen, was im Quellcode passiert bevor das Dreieck auf 
dem Bildschirm erscheint. 
Die Zeichenfläche ("Canvas") habe ich ja im letzten Absatz bereits 
erwähnt. Sie ist im Body-Teil des HTML Dokuments eingebettet. 
Nachdem die Datei vom Browser geladen wurde, wird die Javascript-Funktion 
meinWebGLStart() aufgerufen: 
 
| 103 | window.onload = function () {
 |  | 104 |     meinWebGLStart();
 |  | 105 | };
 |  
  
 
meinWebGLStart() ist ein paar Zeilen darüber implementiert. Die 
Funktion ist in diesem Beispiel das Ein und Alles. Alle Schritte von der 
Initialisierung von WebGL bis zum Zeichnen des Dreiecks werden nacheinander 
ausgeführt: 
 
Ich fange vorne an: 
 
| 26 | canvas = window.document.getElementById("meineWebGLCanvas");
 |  | 27 |  |  | 28 | try {
 |  | 29 |     // Falls der Browser es unterstuetzt, wird hier WebGL 
 |  | 30 |     // erstmalig  angesprochen und der "WebGL-Context" in 
 |  | 31 |     // dem Objekt gl gespeichert.
 |  | 32 |     gl = canvas.getContext("experimental-webgl");
 |  | 33 | } catch (e) {}
 |  | 34 | if (!gl) {
 |  | 35 |     window.alert("Fehler: WebGL-Context nicht gefunden");
 |  | 36 | }
 |  
  
In der lokalen Variable canvas wird zunächst über das globale 
document-Objekt von Javascript das Element mit dem Namen 
"meineWebGLCanvas" gesucht und damit lokal (in dieser Funktion 
verfügbar).  
Danach wird die Objektmethode getContext("experimental-webgl")  
aufgerufen und der Rückgabewert dem ebenfalls lokalen Objekt  gl 
zugewiesen.  
Mit dieser Zeile wird geprüft, ob der verwendete Browser den 
"experimental-webgl" -Context unterstützt und falls dies so ist, steht 
das Objekt gl ab sofort für alle WebGL-Befehle bereit. Die 
Verknüpfung zwischen der oben beschriebenen Canvas und WebGL ist damit 
ebenfalls hergestellt. Die Initialisierung ist in einen try-catch-Block 
eingeschlossen um alle Arten von Fehler abzufangen, die auftreten falls der 
verwendete Browser WebGL (noch) nicht unterstützt. 
Direkt danach finden noch eine überprüfung statt, ob die 
Initialisierung geklappt hat. Wenn kein WebGL-context gefunden wurde, wird mit 
dem alert-Befehl eine Fehlermeldung im Browser angezeigt. 
In den darauffolgenden Codezeilen wird ein WebGL-Program-Objekt mit zwei 
Shadern erzeugt: 
 
| 40 | webGLProgramObject = gl.createProgram();
 |  | 41 |  |  | 42 | // Der folgende String enthaelt den kompletten Quellcode
 |  | 43 | //  fuer einen minimalistischen Vertex-Shader:  
 |  | 44 | vShaderQuellcode =
 |  | 45 |     'attribute vec4 vPosition; \n\
 |  | 46 |     void main() \n\
 |  | 47 |     { \n\
 |  | 48 |         gl_Position = vPosition; \n\
 |  | 49 |     } \n';
 |  | 50 | // Das Vertex-Shader-Objekt wird angelegt:                                 
 |  | 51 | vShader = gl.createShader(gl.VERTEX_SHADER);
 |  | 52 | //           - mit seinem Quelltext verknuepft:
 |  | 53 | gl.shaderSource(vShader, vShaderQuellcode);
 |  | 54 | //           - kompiliert:
 |  | 55 | gl.compileShader(vShader);
 |  | 56 | //           - dem Shader-Program-Objekt hinzugefuegt:
 |  | 57 | gl.attachShader(webGLProgramObject, vShader);
 |  | 58 |  |  | 59 | // Nochmal das gleiche Vorgehen wie fuer den Vertex-
 |  | 60 | // Shader; analog fuer den Fragment-Shader:                
 |  | 61 | fShaderQuellcode =
 |  | 62 |     'precision mediump float;\n\
 |  | 63 |     void main()  \n\
 |  | 64 |     {     \n\
 |  | 65 |         gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n\
 |  | 66 |     } \n';
 |  | 67 | fShader = gl.createShader(gl.FRAGMENT_SHADER);
 |  | 68 | gl.shaderSource(fShader, fShaderQuellcode);
 |  | 69 | gl.compileShader(fShader);
 |  | 70 | gl.attachShader(webGLProgramObject, fShader);
 |  | 71 | // Das Shader-Program-Objekt ist vollstaendig und muss
 |  | 72 | // gelinkt werden.
 |  | 73 | gl.linkProgram(webGLProgramObject);
 |  | 74 | // Da theoretisch mehrere Shader-Program-Objekte moeglich  
 |  | 75 | // sind, muss angegeben werden, welches benutzt werden soll.
 |  | 76 | gl.useProgram(webGLProgramObject);
 |   Shaders sind ein Kapitel für sich. In der OpenGL Literatur tauchen sie 
üblicherweise erst in den hinteren Kapiteln auf. In WebGL (bzw. OpenGL ES 
2.0) kommt man jedoch von Anfang an mit Shaders in Berührung.  
 
Für diese Kapitel 1 würde es zu weit führen, auf die Shader 
detailiert einzugehen und es ist auch erstmal nicht notwendig. Das 
Grundkonzept will ich trotzdem versuchen mit einfachen Worten zu 
erklären. 
 
Was WebGL von herkömmlichen Javascript (2D) Grafikfunktionen 
unterscheidet ist, dass ein Großteil der erforderlichen Berechnungen auf der 
Grafikarte statt im Hauptprozessor ausgeführt werden (GPU statt CPU). Die 
GPU (Graphics Processing Unit) ist für grafische Berechnungen ausgelegt 
(wer hätte das gedacht) und hat v.A. gegenüber der CPU den Vorteil 
dass sie viele Rechenoperationen GLEICHZEITIG berechnen kann. Wenn wir z.B. 
ein 2D-Bild (Bildschirm) aus einer 3D-Szene (WebGL) berechnen wollen, muss 
für jeden Pixel ein eingener Farbwert berechnet werden. D.h. jede Menge 
"kleine" Berechnungen die voneinander unabhängig sind und damit ideal auf 
der GPU berechnet werden können. 
 
SHADER sind Mini-Programme, die auf der GPU vielfach parallel laufen.  
(diese Definition sollte zumindest für Kapitel 1 ausreichen) 
 
Der Quellcode der beiden Shader (es gibt immer diese zwei Shader in jeder 
WebGL-Anwendung) ist als gewöhnlicher String in den JavaScript Quellcode 
eingebettet. Den Vertex-Shader findest du in Zeile 45-49, den Fragment-Shader 
in Zeile 62-66.
Möglicherweise hast du wie ich weiter oben vorgeschlagen habe, den 
Fragment-Shader bereits bearbeitet, wenn du die Farbe des Dreiecks 
geändert hast. 
 
Die beiden Shader-Quellcode-Strings werden jeweils mit 
gl.compileShader(..) übersetzt (Kompilieren kennst du hoffentlich 
von anderen Programmiererfahrungen...) und mit attachShader(..) dem 
WebGL-Program-Objekt hinzugefügt. Sobald dem Program-Objekt beide Shader 
übergeben wurden, kann es gelinkt werden (Compiler + Linker ähnlich 
wie in C, Pascal,...etc.). 
Das Abschließende useProgram(..) "aktiviert" das gerade erstellte 
Program-Objekt. Theoretisch können mehrere Programm-Objekte verwendet 
werden, die mit useProgram jeweils ausgetauscht werden. 
 
Der Datenfluss geht üblicherweise folgendermaßen: 
1. im JavaScriptcode wird ein Rohdatenpuffer erzeugt (Raumkoordinaten, Farben, 
etc.) 
2. im Vertex-Shader werden die punktbezogenen Werte berechnet 
(Transformationen, Normalenvektoren, Texturkoordinaten,...) und die 
vordefinierte Variable $gl_Position$ belegt. 
3. der Fragment-Shader berechnet die pixelbezogenen Werte zwischen den 
Vertices (interpoliert z.B. Farben zwischen Vertices) und belegt die 
vordefinierte Variable $gl_FragColor$. 
 
Die folgenden zwei Zeilen sind leicht zu verstehen: 
| 78 | gl.clearColor(0.0, 0.0, 0.0, 1.0);
 |  | 79 | // Hintergrund loeschen
 |  | 80 | gl.clear(gl.COLOR_BUFFER_BIT);
 |  
  
Die beiden Funktion legen die Hintergrundfarbe der 3D-Szene als RGB-Wert fest 
und löschen die komplette Szene in dieser Farbe.
 
Jetzt kommt die Antwort auf die spannende Frage, wie Daten zwischen dem 
"normalen" JavaScript-Code und den WebGL-Shadern ausgetauscht werden. 
| 84 | vertexAttribLoc = gl.getAttribLocation(webGLProgramObject, "vPosition");
  |  
  
getAttribLocation(..) ist eine WebGL-Api-Funktion, die hier anhand des 
Variablennamens vPosition eine Zugriffsmöglichkeit auf eine 
Eingangsvariable des Vertex-Shaders liefert. 
 
Dass in Zeile 88-90 die Dreieckskoordinaten angelegt werden, ist leicht zu
erahnen. Datentyp ist das WebGL-spezifische Float32Array. 
Um die Berechnungen auf die GPU zu verlagern, müssen diese Daten in den 
Grafikspeicher übertragen werden. Hierzu wird mit createBuffer ein 
neuer Speicherbereich angelegt, der dann mit bindBuffer aktiviert wird 
und zuletzt mit bufferData befüllt wird. Der Inhalt von 
vVertices wird hier in den Grafikspeicher kopiert. 
Nun müssen wir WebGL noch mitteilen, wofür der gerade aktivierte und 
befüllte Puffer verwendet werden soll:
| 97 | gl.vertexAttribPointer(vertexAttribLoc, 3, gl.FLOAT, false, 0, 0);
 |  | 98 | gl.enableVertexAttribArray(vertexAttribLoc);
 |  
  
Der erste Parameter vertexAttribLoc verweist auf die Eingangsvariable 
des Vertex-Shaders, "3" ist die Anzahl der Werte pro Element (3 
Koordinatenwerte pro Punkt) und "gl.Float" ist der Datentyp. 
enableVertexAttribArray(..) aktiviert die Verwendung des Puffers 
für folgende Zeichenfunktionen. 
 
Der letzte Befehl bringt nun alles "auf den Schirm" um mit Cpt. Picard zu 
sprechen: 
| 100 | gl.drawArrays(gl.TRIANGLES, 0, 3);
 |  
  
Hiermit werden aus den zuletzt aktivierten Pufferdaten Dreiecke generiert (in 
diesem Fall nur eines). Den ersten Parameter gl.TRIANGLES kannst du 
z.B. auch in  gl.POINTS oder 
gl.LINE_STRIP
ändern und dich überraschen lassen, wie sich die Darstellung 
verändert. 
Der zweite und dritte Parameter geben Start- und Anzahl der Array-Elemente an, 
die gezeichnet werden sollen.
  Das soll es jetzt erstmal gewesen sein für Kapitel 1 meines 
WebGL-Tutorials. Ich hoffe es ist einigermaßen verständlich. Kommentare 
nehme ich gerne unter der u.A. Emailadresse entgegen. 
 
Im Kapitel zwei werden wir die räumliche Projektion einführen und 
ausführlich auf die Modelview- und Perspektivmatrizzen eingehen. 
   
    
                
			   |