Workshop

Beyond 2D: Into the Shadows


Speaker: Carl Bateman

Caveat

How WebGL re-introduced me to JavaScript...
linking the possibilities of WebGL with a peek into some of the darker corners of JavaScript

Not intertwined with Graham, in any way

Carl Bateman

By day

  • Software Engineer (desktop and web)
  • C#, C++, VB, MySQL, .NET, Linq, blah, blah... blah...
  • JavaScript, CSS, HTML, WebGL, jQuery, Three.js, blah, blah... blah...

By night

  • Software Engineer (still)
  • OpenGL, Unity, JavaScript, PHP, CSS, HTML and, of course, WebGL

Carl Bateman

  • WebGL Workshop Organiser
  • Khronos London Chapter Coordinator
  • Self-appointed WebGL Evangelist
    • Nerd
      • Zealot
        • Looney

Basically

  • I love to code
  • I love to graphic
  • I love to math
    • eπi +1 = 0 and i² = j² = k² = ijk = -1 ... FUN!!!
  • I love to web

Caveat: I approach WebGL from a mathematical, progamming perspective rather than a web design perspective

the verb form is the only correct use of “math” all other forms are an abomination before Man and God

Carl Bateman

TL;DR

Never promise

Never deliver

!important

!!important

¡important!

WebGL Workshop
Khronos London Chapter

Learning
Networking
Easier than googling
Beer

Executive Summary

"There's a freaking supercomputer in your browser,
and nobody seems to have noticed!"

Steve Sanderson




Jasmine Kent

Executive Summary

CPU bad (slower) (click to zoom) GPU good (faster) (mouse-wheel to zoom)
Refresh page if panel(s) blank

Executive Summary

Executive Summary

Executive Summary

What is WebGL?

WebGL - OpenGL ES 2.0 for the Web

“WebGL is a royalty-free, cross-platform API that brings OpenGL ES 2.0 to the web as a 3D drawing context within HTML, exposed as low-level Document Object Model interfaces.
“It uses the OpenGL shading language, GLSL ES, and can be cleanly combined with other web content that is layered on top or underneath the 3D content.
“It is ideally suited for dynamic 3D web applications in the JavaScript programming language, and will be fully integrated in leading web browsers.”

Khronos.org

What is WebGL?

Web standard
Cross-device
Cross-platform
Combination of JavaScript API
and GLSL language

What is WebGL?

Canvas based
GPU access from browser
Control via shader programs
Rasteriser - 2D and 3D graphics
and more

How WebGL Works

JavaScript

Create Context/Canvas

Compile GLSL

Draw commands

Data control

GLSL

Shader program (compiled)

Vertex shader

   transform vertices

Fragment (pixel) shader

   transform pixels

How WebGL Works

Vanilla WebGL

Your html page will look something this...


<html>
  <head>
    <!-- place holders for glsl code -->
    <script id="vertex" type="x-shader">
    </script> 

    <script id="fragment" type="x-shader">
    </script>
  </head>

  <body>
    <!-- canvas to hold webgl context -->
    <canvas id="glCanvas">
  </body>
</html>
          

"x-shader" arbitrary name, no special meaning
<script> convenient place to put GLSL source

Obvs, JavaScript can go in separate .js file
Shaders can be loaded with XHR

Shaders

Simple vertex shader



          


Simple fragment shader


 
          

JavaScript

Get a WebGL context


var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
for (var i = 0; i < names.length; ++i)  {
  try {
    gl = canvas.getContext(names[i]);
  }
  catch (e) { }
  if (gl) break;
}
          

Error check


if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) 
  console.log(gl.getShaderInfoLog(vs));
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) 
  console.log(gl.getShaderInfoLog(fs));
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) 
  console.log(gl.getProgramInfoLog(program));
 gl.useProgram(shaderProgram);
          

JavaScript

Get source, build, compile, link and use the shader


var v = document.getElementById("vertex").firstChild.nodeValue;
var f = document.getElementById("fragment").firstChild.nodeValue;

var vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, v);
gl.compileShader(vs);
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, f);
gl.compileShader(fs);

shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vs);
gl.attachShader(shaderProgram, fs);
gl.linkProgram(shaderProgram);

if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS))
  console.log(gl.getShaderInfoLog(vs));
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS))
  console.log(gl.getShaderInfoLog(fs));
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS))
  console.log(gl.getProgramInfoLog(shaderProgram));

gl.useProgram(shaderProgram);
          

JavaScript

Get data location


shaderProgram.uColor = gl.getUniformLocation(shaderProgram, "uColor");	
shaderProgram.aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition");	
gl.enableVertexAttribArray(shaderProgram. aVertexPosition);
          

Define Geometry


var vertices = new Float32Array([-0.5,  0.5, 
                                  0.5, -0.5,
                                 -0.5, -0.5]);
cubeVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
cubeVertexPositionBuffer.itemSize = 2;
cubeVertexPositionBuffer.numItems = vertices.length / cubeVertexPositionBuffer.itemSize;	
          

JavaScript

Send data to GPU and draw


gl.uniform4fv(shaderProgram.uColor, [0.0, 1.0, 0.0, 1.0]);
gl.vertexAttribPointer(shaderProgram.aVertexPosition,
                       cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.clearColor(0, 0.5, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, cubeVertexPositionBuffer.numItems);
          


Ta da!

JavaScript Features

Adding methods / members to object


var obj = { a: 0 };
obj.b = 6;

JavaScript Feature... not

"Down to" "operator"


  var x = 10;
  while (x --> 0) // x goes to 0
  {
      console.log(x);
  }


Just spacing


  var x = 10;
  while ((x--) > 0) // x goes to 0
  {
      console.log(x);
  }

JavaScript Features

.bind and .call


  var imgProcessed = document.createElement("img");
  imgProcessed.src = imgMissingSrc;
  img.src = imgMissingSrc;
  handleImgLoaded.call(img, radius, offset);


  imgProcessed.onload = function (drillColour, count, missingData) {
    // do stuff
  }.bind(imgProcessed, radius, offset);

GLSL

Language features

C-like

Strongly typed

Optimised for geometry

Native support of vectors and matrices (no quaternions)

Built-in geometry functions e.g. cos, sin, dot, cross, reflect

Swizzle:

vec3 v1, v2;
v1[0] = v2.r;
v1.xyz = v2.rgb;
v1.zyx = v2.bbb;

Textures via sampler2D and texture2D (no 1D or 3D textures)

GLSL

Language features

Type modifiers

uniform

varying

attributes

Security

Variables initialised

Out of bounds

Examples

Simple site decoration

WebGL Paris 2015

Pixel Shaders

tamapolis : Javi Agenjo's personal blog

TWGL: A Tiny WebGL Helper Library

WebGL Fundamentals


Seriously ambitious site decoration

Hackery, Math & Design — Acko.net


OTT

Opus Live Wallpaper for Android


What the what?

WebGL Presentation Editor

APIs

WebGL is

 • long winded

 • complicated


BUT

 • scales well


Still, APIs

 • hide complexity

 • simplifies

APIs

Oh so very many...

Two of the best known

Both have plenty of support, big communities and on-line editors of varying utility

Three.js is a bit... unstable

Three.js


function init() {
  var camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
  camera.position.z = 400;

  var scene = new THREE.Scene();
  var texture = new THREE.TextureLoader().load('crate.jpg', function(texture) {
    texture.needsUpdate = true
  });
  var geometry = new THREE.BoxGeometry(200, 200, 200);
  var material = new THREE.MeshBasicMaterial({ map: texture });
  var mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  var renderer = new THREE.WebGLRenderer({ alpha: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  var render = function () {
    requestAnimationFrame(render);
    mesh.rotation.x += 0.005;
    mesh.rotation.y += 0.01;
    renderer.render(scene, camera);
  };
  render();
}
        

Babylon.js


function init() {
  var canvas = document.getElementById('renderCanvas');
  var engine = new BABYLON.Engine(canvas, true);
  var scene = new BABYLON.Scene(engine);
  scene.clearColor = new BABYLON.Color4(0, 0, 0.0, 0.0);

  var camera = new BABYLON.FreeCamera("FreeCamera", new BABYLON.Vector3(0, 1, -400), scene);
  camera.attachControl(canvas, false);

  var light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 1, 0), scene);
  light.groundColor = new BABYLON.Color3(0.4, 0.4, 0.4);
  var mesh = BABYLON.Mesh.CreateBox("mesh", 110, scene);

  var material = new BABYLON.StandardMaterial("texture", scene);
  material.diffuseTexture = new BABYLON.Texture("crate.jpg", scene);
  mesh.material = material;

  var render = function () {
    requestAnimationFrame(render);
    mesh.rotation.x += 0.005;
    mesh.rotation.y += 0.01;
    scene.render();
  };
  render();
}
            

Moar APIs

  • PEX
  • XTK (JS implementation of ITK / VTK)
  • SpiderGL
  • CopperLicht
  • A Tiny WebGL helper Library
  • etc.

On-line editors


Feature Support

Feature Support

Feature Support

Feature Support

Feature Support

Applications

Applications

Applications

  • Products and e-commerce - Futurgo
  • Advertising and media - Nike

Performance

SIMD


void main() {
  vUv = uv;
  float mag = max(length(position), uRadius);
  if(length(position) > uRadius) {
    vec3 t = normalize(position);
    t *= uRadius;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( t, 1.0 );
  } else {
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  }
}
             

Avoid if's that use internal state


void main() {
  vUv = uv;
  float mag = max(length(position), uRadius);
  vec3 t = max(normalize(position) * uRadius, position);
  gl_Position = projectionMatrix * modelViewMatrix 
              * vec4( mag * normalize(position), 1.0 );
}
          

Tools

FireFox WebGL Editor

Built in to FireFox

Tools

WebGL Inspector

benvanik.github.io/WebGL-Inspector/


WebGL as markup

Currently three candidates (that I know of)

GLAM

CSS


body{
  margin: 0px;
  background-color: #000;
}
#sun {
  width-segments: 64;
  height-segments: 64;
  vertex-shader: url(../../shaders/sun.vs);
  fragment-shader: url(../../shaders/sun.fs);
  shader-uniforms: time f 0 
                   texture1 t url(../../images/cloud.png) 
                   texture2 t url(../../images/lavatile.jpg);
}
  
@-webkit-keyframes kfRotateY {
  from {
    -webkit-transform: rotateY(0deg);
  }  
  to {
    -webkit-transform: rotateY(360deg);
  }
}
.animRotateY {
  -webkit-animation-duration: 90s;
  -webkit-animation-name: kfRotateY;
  -webkit-animation-iteration-count: infinite;
  -webkit-animation-timing-function:linear;
}
            

GLAM

JavaScript


var theSun = null;
var startTime = Date.now();
window.addEventListener('load', function(){
    glam.ready();
    theSun = document.getElementById("sun");
    run();  
  },
  false);

function run() {
  requestAnimationFrame(run);
  if (theSun.material) {
    var now = Date.now();
    var elapsed = (now - startTime) / 1000;
     theSun.material.uniforms.time.value = elapsed;
  }
}
            

HTML



  
    
  

            

GLAM

Sunny side up

jQuery Three

Home sweet home

x3dom

HTML


 
   
     
       
         
       
       
     

     
       
         
           
         
         
       
     

     
       
         
           
         
         
       
     
   
            
             

x3dom

The future

WebGL 2

• experimental

• 3D texutres

• profiling and debugging


WebVR (Mozilla)

• expose VR devices, e.g. Oculus Rift, to web apps


Aframe (Mozilla)

• open-source framework for 3D and VR on the web

Contact

 

[email protected]
@CarlBateman
meetup.com/WebGL-Workshop-London
linkedin.com/in/dcbateman



Questions?