An Introduction to Programming 3D
with WebGL and HTML5

↓ ↓ ↓ Slides & Files ↓ ↓ ↓

WebGL.io/ldn-js

Carl Bateman

Software Engineer
    

Carl Bateman

Computer Graphics (3D)
    

Carl Bateman

Maths

   eπi +1 = 0
   i² = j² = k² = ijk = -1


Fun!!!

Carl Bateman

Software Engineer

Carl Bateman

     

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

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
Rasteriser - 2D and 3D graphics
and more
Control via shader programs

How WebGL Works

JavaScript

   Create Context/Canvas

   Compile shaders

   Data control

   Draw commands


GLSL

   Shader program

   Vertex shader

      transform vertices

   Fragment (pixel) shader

      transform pixels

How WebGL Works

Setup
and
2D

HTML

Your html page will look something this...
<html>
  <head>
    <!-- place holders for glsl code -- coming soon -->
    <script id="vertex" type="x-shader/x-vertex">
    </script> 
             
    <script id="fragment" type="x-shader/x-fragment">
    </script>
             
    <script type="text/javascript">
    </script>
  </head>
  <body>
    <canvas id="glCanvas"></canvas>
  </body>
</html>

"x-shader" type is arbitrary with no special meaning
<script> tag convenient place to put GLSL source

  Obvs, JavaScript can go in separate .js file

  Shaders can be loaded with XHR

Shaders

Simple vertex shader
attribute vec2 aVertex;
attribute vec3 aColour;
varying vec3 vColour;
             
void main() {
  vColour = aColour;
  gl_Position = vec4(aVertex, 0.0, 1.0);
}      
Simple fragment shader
precision mediump float;
varying vec3 vColour;
             
void main() {
  gl_FragColor = vec4(vColour, 1.0);
}         

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;
}

Compile, attach and link shader programs

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);
               
programInfo.id = gl.createProgram();
gl.attachShader(programInfo.id, vs);
gl.attachShader(programInfo.id, fs);
gl.linkProgram(programInfo.id);

JavaScript

Error check and use
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);

Get data location

programInfo.aVertex = gl.getAttribLocation(programInfo.id, "aVertex");
programInfo.aColour = gl.getAttribLocation(programInfo.id, "aColour");

How WebGL Works

Render pipeline (simplified)

How WebGL Works

Define shape data

var vertices = new Float32Array([ 0.5,  0.5,
                                 -0.5,  0.5,
                                 -0.5, -0.5]);
var colours = new Float32Array([0, 0, 1,
                                1, 0, 0,
                                0, 1, 0]);

How WebGL Works

Prep data and send to shader (position data)

bufferInfo.itemSize = 2;
bufferInfo.numItems = vertices.length / bufferInfo.itemSize;
bufferInfo.id = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.id);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(programInfo.aVertex, bufferInfo.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(programInfo.aVertex);
Do the same for colours!

How WebGL Works

Send "clear" and "draw" instructions

gl.clearColor(0.8, 0.8, 1.0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, bufferInfo.numItems);         

How WebGL Works

Vertex shader - run for each point

attribute vec2 aVertex;
attribute vec3 aColour;
varying vec3 vColour;
               
void main() {
  vColour = aColour;
  gl_Position = vec4(aVertex, 0.0, 1.0);
}         

How WebGL Works

Fragment shader - run for each pixel

precision mediump float;
varying vec3 vColour;
               
void main() {
  gl_FragColor = vec4(vColour, 1.0);
}         

GLSL

GLSL

Language features

C-like

Strongly typed

Optimised for geometry

Native support for 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

  attribute

  uniform

  varying

  Security

  Variables initialised

  Out of bounds

3D

3D

Nothing is free

You have to work for it

3D

With a bit of matrix magic

mat4 uModelViewMatrix = mat4(1.0, 0.0,  0.0,   0.0,
                             0.0, 1.0,  0.0,   0.0,
                             0.0, 0.0,  1.0,   0.0,
                             0.0, 0.0, -3.333, 1.0 );
             
mat4 uProjectionMatrix = mat4(2.41421, 0.0,      0.0,        0.0,
                              0.0,     2.41421,  0.0,        0.0,
                              0.0,     0.0,     -1.002002,  -1.0,
                              0.0,     0.0,     -0.2002002,  0.0 );
attribute vec3 aVertex;

void main(void) {
  gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertex, 1.0);
}            

3D

Libraries to the rescue

Libs like glMatrix provide functions
mat4.perspective
               
mat4.ortho 

APIs

APIs

So, life is hard in vanilla WebGL land

APIs to the rescue

APIs

Two of the best known

Plenty of support
Big communities
On-line editors of varying utility

Three.js is a bit... unstable

Moar APIs

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

APIs

Three.js

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(); 

Lighting

Models

Contact

 

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


Questions?