Historie: Web & 3D
Snaha dostat 3D do webového prohlížeče není nová.
- VRML: značkovací jazyk založený na XML
- Nutné instalovat do prohlížeče dodatečný plugin
- Většinou pouze procházení statické scény
- Blender Player: interaktivní aplikace (hry)
- Nutné instalovat do prohlížeče dodatečný plugin
- Plně interaktivní aplikace skriptovatelné v Pythonu
- Obsah nutné vytvářet v jedné aplikaci
- Flash: snad ani není nutné komentovat
Důvody neúspěchu?
- Nutné použít plugin
- Paměťové náročné
- Špatný návrh
Podpora ve webových prohlížečích
V současnosti (2016) podporují WebGL 1.0.4 všechny hlavní prohlížeče (jejich poslední verze). Statisticky 92% uživatelů má zapnutou podporu WebGL. Aktuální statistika je dostupná na webglstats.com (průkaznost statistických dat?).
Podpora pro WebGL 2.0 (založeno na OpenGL ES 3.0) je experimentálně dostupná pouze v některých prohlížečích a musí být explicitně povolená.
Důvod nefunkčnosti WebGL
- Stará verze prohlížeče.
- Neaktualizovaný a nebezpečný ovladač grafické karty (blacklisting).
- Vypnutá podpora WebGL v nastavení prohlížeče.
Khronos Conformance testy
Khronos Group vytvořil sadu testů podobných ACID testům od W3C. Svůj prohlížeč si můžete otestovat na adrese:
https://goo.gl/LMJfC6
Canvas & Javascript & WebGL
- Nutná je nejprve podpora por HTML5 elelement
canvas
- Následně je nezbytné, aby Javascript API mělo přístup k 3D kontextu canvasu ("webgl"/"experimental-webgl")
Minimální WebGL aplikace
<!DOCTYPE html>
<html><body>
<canvas id="c01"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("c01");
var gl = canvas.getContext("webgl");
gl.clearColor(0.3, 0.8, 0.3, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
</body></html>
Zobrazovací řetězec WebGL
- WebGL nepoužívá pevně danou zobrazovací řetězec jako má OpenGL (viz. předchozí přednáška)
- WebGL vyžaduje použití vertex a fragment shaderů
Vertex & Fragment Shader
- Speciální programovací jazyk podobný jazyku C
- Kompilovaný na grafické kartě
- Vykonávaný na grafické kartě
- Grafická karta (2016): masivně paralelní stroj (stovky až tisíce výpočetních jader) - velmi rychlý běh
Vertex Shader
- Každý vrchol vstupující do vertex shaderu je tímto shaderem transformován. Zde mohou být prováděny například tyto operace:
- Transformace (rotace, zvětšní, posun, atd.)
- Transformace do souř. systému kamery
- Promítání (perspektivní/roviné)
- Mnohé další
- Výstupem je čtyřprvkový vektor
gl_Position
, velikost bodu gl_PointSize
a případně nastavení hodnot pro proměnné typu varying.
Minimální Vertex Shader
<!DOCTYPE html>
<html><body>
<canvas id="c02"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("c02");
var gl = canvas.getContext("webgl");
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader,
'void main(void) { gl_Position = vec4(0, 0, 0, 1);}');
gl.compileShader(vertexShader);
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.linkProgram(program); gl.useProgram(program);
gl.clearColor(0.3, 0.3, 0.8, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
</body></html>
Příklad minimálního Vertex Shaderu
Demonstrační program má drobnou nevýhodu: vertex shader nic nedělá, protože nemá žádná vstupní data a jeho výstup není vstupem do fragment shaderu.
Tvorba primitiv a rasterizace
- Po transformaci vertexů dojde k vytvoření jednotlivých primitiv (základních elementů):
- Následně dojde k rasterizaci těchto základních elementů do rastru fragmentů (pixelů).
Fragment Shader
- Fragment shader je aplikovaný na každý fragment (pixel) rasterizovaných elementů.
- Výstup fragment shaderu se uloží do framebufferu a zobrazí
Miniální Fragment Shader
<!DOCTYPE html>
<html><body>
<canvas id="c03"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("c03");
var gl = canvas.getContext("webgl");
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader,
'void main(void) {gl_FragColor = vec4(1, 1, 1, 1);');
gl.compileShader(fragmentShader);
var program = gl.createProgram();
gl.attachShader(program, fragmentShader);
gl.linkProgram(program); gl.useProgram(program);
gl.clearColor(0.8, 0.3, 0.3, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
</body></html>
Příklad minimálního Fragment Shaderu
Demonstrační program má opět drobnou nevýhodu: fragment shader nic nedělá, protože nemá žádné vstupní fragmenty.
Shadery a datové typy
- Jednoduché datové typy: void, bool, int, float
- Vektory floatů: vec2, vec3, vec4
- Vektory booleanů: bvec2, bvec3, bvec4
- Vektory integerů: ivec2, ivec3, ivec4
- Čtvercové matice floatů: mat2, mat3, mat4
- Textury: sampler2D, samplerCube
- Jednorozměrné pole a struktury
Konstrukce pro řízení shaderů
for(;;) {}
while() {}
do {} while()
Podmínky:
if() {}
if() {} else {}
Skoky:
break, continue, return, discard
V shader si můžete definovat vlastní funkce:
float add(float a, float) {return a + b};
Vestavěné funkce shaderů
Je jich cca 60. Příklady nejpoužívanějších: sin(), asin(), cos(), acos(), tan(), atan(), pow(), exp(), log(), exp2(), log2(), sqrt(), abs(), floor(), ceil(), mod(), min(), max(), atd.
Kompletní seznam je například v Quick Reference Card
Double-buffering
“WebGL: double-buffering batteries included!”
- Framebuffer není ve skutečnosti jeden, ale jsou dva: přední a zadní.
- Do zadního se postupně vykresluje
- Přední je zobrazován uživateli
- Při dokončení vykreslování se buffery prohodí
- Zamezuje problikávání a vzniku rušivých artefaktů
Z-Buffer alias paměť hloubky
Vstupní data: vertexy & indexy
- Nejprve je nutné říci, že vertex shader bude používat nějaká vstupní data a jak je bude používat:
attribute vec3 pos;
void main(void) { gl_Position = vec4(pos, 1); }
Vstupní data je nutné předat do vertex shaderu pomocí ArrayBufferu
ArrayBuffer & typové pole
- Data jsou mezi aplikací psanou v Javascriptu a shadery předávána v tzv. ArrayBufferech
- ArrayBuffer se vytváří pomocí tzv. typového pole (novinka v HTML5).
- Typové pole může obsahovat data pouze jednoho typu.
- Uint8Array, Uint16Array, Uint32Array
- Int8Array, Int16Array, Int32Array
- Float32Array, Float64Array
Předáváme data pomocí ArrayBuffer
- Nejprve řekneme, do jakého atributu budeme data předávat
var posLoc = gl.getAttribLocation(program, "pos");
gl.enableVertexAttribArray(posLoc);
Následně vytvoříme ArrayBuffer, naplníme ho obsahem z typového pole a propojíme ho s atributem pos
var posBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer); // aktivní buffer
var vertices = [0.0, 0.0, 0.0];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),
gl.STATIC_DRAW);
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0)
Předávaná data
- Data předávaná do vertex shaderu nemusí a nejsou být pouze souřadnice vertexů.
- Může jít o barvu vertexů, UV souřadnice, matice, normálové vektory, atd.
Vykreslení elementů
- Elementy (v tomto případě body) následně vykreslíme pomocí (vykreslujeme data z aktivního bufferu):
gl.drawArrays(gl.POINTS, 0, vertices.length/3);
Podporované typy elementů: POINTS, LINE_STRIP, LINE_LOOP, LINES, TRIANGLE_STRIP, TRIANGLE_FAN, TRIANGLES
Elementy a ArrayBuffer
- Složitější útvary než vertexy (hrany a trojúhelníky) je vhodné vykreslovat pomocí indexů do bufferu vertexů.
- K tomu slouží ElementArrayBuffer:
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
var indices = [0, 1, 2];
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
new Uint8Array(indices), gl.STATIC_DRAW);
Vykreslení elementů
- Elementy pomocí indexů vykreslujeme následovně (opět se data berou z aktuálního bufferu)
gl.drawElements(gl.LINES, 6, gl.UNSIGNED_BYTE, 0);
Podporované typy elementů jsou stejné: POINTS, LINE_STRIP, LINE_LOOP, LINES, TRIANGLE_STRIP, TRIANGLE_FAN, TRIANGLES
Souřadné systémy
- Je dobré si povšimnout, že hodnoty, které vystupují z vertex shaderu jsou v homogenních souřadnicích a následně jsou převedeny ho kartézské soustavy souřadné.
- Pohledové souřadnice mají počátek soustavy souřadné v prostředku a v pravém horním rohu je bod [1, 1, 0].
- V neposlední řadě je třeba zmínit soustavu souřadnou canvasu. Počátek soustavné je v levém horním rohu. Bod [width, height] je v pravém dolním rohu.
Varying proměnné
- Pokud je nějaká promenná označena ve vertex shaderu klíčovým slovem varying, tak během rasterizaci elementů dochází k její lineární interpolaci. Pro každý fragment elementu (úsečka, trojúhelník) je spočítána hodnota.
varying vec3 color;
Uniform proměnné
- Proměnné označené klíovým slovem unform jsou neměné pro celý vertex nebo fragment shader.
- Nejčastěji se používají pro matice