WebGL - Quake 3 w przeglądarce

Sebastian Poręba
27 październik, 2011

Ja

WTF is WebGL?

WebGL = OpenGL (ES 2.0)
dla przeglądarek

WebGL = WhateverVille + The Sims

Czemu powinno nas to obchodzić?

Flash musi odejść

Jak zabrać się do WebGL

JavaScript

JavaScript

JavaScript

JavaScript

Jak działa karta graficzna?

Grafika = mnóstwo danych do szybkiego przetworzenia

Architektura SIMD

Więcej mocy!

Teza 01: Ilość Ghz nie rośnie w nieskończoność

Więcej rdzeni!

Więcej rdzeni!

Więcej rdzeni?

Jaka ilość rdzeni jest optymalna?

Więcej rdzeni?

Teza 010: Procesor nie ogarnia jeśli ma za dużo rdzeni

Rozwiązanie? Dedykowany procesor do wielowątkowych obliczeń

Jak działa GPU?

Shader

Skalowanie na serio

Antyrasistowski disclaimer: shadery odwalają kawał dobrej roboty

Jak działa shader?

Od wydania OpenGL 2.0 (2004) można programować samodzielnie vertex i fragment shader

Vertex shader

Fragment shader

Od wydania OpenGL 1.5 (2003) OpenGL wprowadził bufory

          glBegin( GL_POLYGON );   /* Begin issuing a polygon */
          glColor3f( 0, 1, 0 );    /* Set the current color to green */
          glVertex3f( -1, -1, 0 ); /* Issue a vertex */
          glVertex3f( -1, 1, 0 );  /* Issue a vertex */
          glVertex3f( 1, 1, 0 );   /* Issue a vertex */
          glVertex3f( 1, -1, 0 );  /* Issue a vertex */
          glEnd();                 /* Finish issuing the polygon */
        

NNRVS™ - Nic Nie Robiący Vertex Shader

          in vec4 aVertexPosition;

          void main() {
              gl_Position = aVertexPosition;
          }
        

NNRFS™ - Nic Nie Robiący Fragment Shader

          void main() {
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
          }
        

Zróbmy z tego jakiś WebGL!

HTML boilerplate

<!DOCTYPE html>
<html>
  <head>
    <title>WebGL Basics part 1</title>
    <script type="text/javascript" src="my-awesome-code.js"></script>
  </head>

  <body onload="start();">
    <h1>WebGL context in canvas</h1>
    <canvas id="glcanvas" width="320" height="240">
          Y U no like WebGL?
    </canvas>
  </body>
</html>
        

Inicjalizacja context

var gl = null; // GL context
var program; // na później
var vCount; //to też

function start()
{
  // pobierz canvas
  var canvas = document.getElementById('glcanvas');

  // spróbuj zainicjalizować WebGL
  gl = canvas.getContext('experimental-webgl');

  // Wyczyść canvas na kolor czarny
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
}
        

Przykład 01 - pusty canvas

Y U no like WebGL?

Skompilujmy jakieś shadery

function generateProgram() {
  var vshader = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(vshader, 
    'in vec4 aVertexPosition; 
    void main(void) 
    { gl_Position = aVertexPosition}');
  gl.compileShader(vshader);
                      
  var fshader = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(fshader, 
    'void main(void) 
    {gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);}');
  gl.compileShader(fshader);

  var program = gl.createProgram();
  gl.attachShader(program, fshader);
  gl.attachShader(program, vshader);
  gl.linkProgram(program);
  gl.validateProgram(program);

  return program;
}
        

Mówimy WebGLowi jak nazywa się zmienna pozycji wchodząca do shadera

program = generateProgram();

var aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
gl.enableVertexAttribArray(aVertexPosition);
        
  in vec4 aVertexPosition;

  void main() {
      gl_Position = aVertexPosition;
  }

Tworzymy bufor na nasze wierzchołki

var vbuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
        

Dodajemy do aktualnego bufora (maszyna stanów!) wierzchołki

var vertices = new Float32Array(...); // to będzie później
var verticeSize = 3;
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// drugi argument - rozmiar wierzchołka
gl.vertexAttribPointer(aVertexPosition, verticeSize, gl.FLOAT, false, 0, 0);
        

Rysowanie

vCount = vertices.length/verticeSize;
gl.drawArrays(gl.LINE_STRIP, 0, vCount);
gl.flush();          
        

Wierzchołki na piramidkę

var vertices = new Float32Array(
  [
    0.0,0.5,0.0,   //czubek
    -0.5,-0.5,0.5, // p1
    0.5,-0.5,0.5,  // p2
    0.0,0.5,0.0,   // czubek
    0.5,-0.5,0.5,  // p2
    0.5,-0.5,-0.5, // p3
    0.0,0.5,0.0,   // czubek
    0.5,-0.5,-0.5, // p3
    -0.5,-0.5,-0.5,// p4
    0.0,0.5,0.0,   // czubek
    -0.5,-0.5,-0.5,// p4
    -0.5,-0.5,0.5, // p1
    0.0,0.5,0.0    // czubek
  ]
);      
        

Przykład 010 - piramidka

Y U no like WebGL?

OpenGL rzutuje wszystkie obiekty znajdujące się w sześcianie
(-1..1,-1..1,-1..1) na płaszczyznę

Programista musi sam zdefiniować przekształcenia imitujące widok perspektywiczny

Let's roll!

Okropna matematyka przekształceń

// Gets a transformation matrix given the rotation angles
function getTransformationMatrix(rx, ry, rz)
{
  rx = rx*2*Math.PI/360;
  ry = ry*2*Math.PI/360;
  rz = rz*2*Math.PI/360;

  // Pre-computes trigonometric values (mainly for better readability)
  var cx = Math.cos(rx), sx = Math.sin(rx);
  var cy = Math.cos(ry), sy = Math.sin(ry);
  var cz = Math.cos(rz), sz = Math.sin(rz);

  // Returns matrix
  return new Float32Array([
    cy*cz, (sx*sy*cz-cx*sz), (sx*sz+cx*sy*cz), 0,
    cy*sz, (sx*sy*sz+cx*cz), (cx*sy*sz-sx*cz), 0,
    -sy,   sx*cy,            cx*cy,            0,
    0,     0,                0,                1
  ]);
}        
        

Dodajmy macierz obrotu do shadera

attribute vec3 aVertexPosition; 

uniform mat4 MVMatrix; 

void main(void) { 
  gl_Position = MVMatrix * vec4(aVertexPosition, 1.0);
}        
        

Dodajemy macierz obrotu

var mat = getTransformationMatrix(30, 30, 30);
var MVMatrix = gl.getUniformLocation(program, "MVMatrix");
gl.uniformMatrix4fv(MVMatrix, false, mat);        
        

Przykład 011 - piramidka 2.0

Y U no like WebGL?

Wywołania cykliczne

setInterval(tick, 1000.0/30); // 30fps
        

Ciekawostka: timery w JS są absolutnie bezużyteczne

Redraw

var rotX = 30, rotY = 30, rotZ = 0;
function tick() {
  rotY++;
  if(rotY > 360) rotY = 0;
  
  var mat = getTransformationMatrix(rotX,rotY,rotZ);
  var MVMatrix = gl.getUniformLocation(program, "MVMatrix");
  gl.uniformMatrix4fv(MVMatrix, false, mat);

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  
  gl.drawArrays(gl.LINE_STRIP, 0, vCount);
  gl.flush();  
}
        

Przykład 0100 - piramidka 2.0 lata!

Y U no like WebGL?

Frameworki są fajne - three.js

Init

function init() {
  // kąt, proporcje, near, far
  camera = new THREE.Camera( 70, 320/240, 1, 1000 );
  // (0,0,350)
  camera.position.z = 350;

  scene = new THREE.Scene();

  cube = new THREE.Mesh(
    new THREE.CubeGeometry( 200, 200, 200 ),
    new THREE.MeshNormalMaterial()
  );
  scene.addObject( cube );

  renderer = new THREE.WebGLRenderer();
  renderer.setSize(320, 240);
  var container = document.getElementById('glcanvas04');
  container.appendChild(renderer.domElement);
}

Animate & run

function animate() {
  render();
  requestAnimationFrame( animate );
}

function render() {
  cube.rotation.x += 0.02;
  cube.rotation.y += 0.0225;
  cube.rotation.z += 0.0175;
  renderer.render( scene, camera );
}

init();
animate();        
        

Przykład 0100 - cube w three.js

Co dostajemy za darmo?

Dema (wreszcie!)

Co czytać?

Matematyka

Metody matematyczne w grafice komputerowej
dr Marcin Szpyrka (AGH)



Shadery

Programowanie procesorów graficznych
dr Piotr Białas (UJ)

Tutoriale

Kto ogarnia?

Czyjego bloga czytać?
Kogo szukać na konferencjach?

Brandon Jones


http://blog.tojicode.com/

Bartek Drozdz


http://www.everyday3d.com/blog/

Jerome Etienne


http://learningthreejs.com/

Gregg Tavares

Erik Möller

WebGL Camp

Koniec!



www.smashinglabs.pl

Bonus