INTRO TO WEBGL

A crash course in 3D

RENDERING FOR THE AGES

WebGL is how Javascript does 3D.
Comes from OpenGL (Graphics Library).
OpenGL has been around for a while.

Fun fact...

...we're not learning WebGL!

WHAT?! You lied!

3D WebGL is much too complicated.

Ever heard of 15-462?

You need an absurd amount of code.

IT CAN't be that bad...

window.onload = main;

function main() {
  var canvas = document.getElementById("canvas");
  var gl = getWebGLContext(canvas);
  if (!gl) {
    return;
  }
  vertexShader = createShaderFromScriptElement(gl, "2d-vertex-shader");
  fragmentShader = createShaderFromScriptElement(gl, "2d-fragment-shader");
  program = createProgram(gl, [vertexShader, fragmentShader]);
  gl.useProgram(program);
  var positionLocation = gl.getAttribLocation(program, "a_position");
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]), gl.STATIC_DRAW);
  gl.enableVertexAttribArray(positionLocation);
  gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
  gl.drawArrays(gl.TRIANGLES, 0, 6);
}
var setupWebGL = function (canvas, opt_attribs) {
  function showLink(str) {
    var container = canvas.parentNode;
    if (container) {
      container.innerHTML = makeFailHTML(str);
    }
  };
  if (!window.WebGLRenderingContext) {
    showLink(GET_A_WEBGL_BROWSER);
    return null;
  }
  var context = create3DContext(canvas, opt_attribs);
  if (!context) {
    showLink(OTHER_PROBLEM);
  }
  return context;
};
var create3DContext = function (canvas, opt_attribs) {
  var names = ["webgl", "experimental-webgl"];
  var context = null;
  for (var ii = 0; ii < names.length; ++ii) {
    try {
      context = canvas.getContext(names[ii], opt_attribs);
    } catch (e) {}
    if (context) {
      break;
    }
  }
  return context;
}
var updateCSSIfInIFrame = function () {
  if (isInIFrame()) {
    document.body.className = "iframe";
  }
};
var getWebGLContext = function (canvas) {
  if (isInIFrame()) {
    updateCSSIfInIFrame();
    canvas.width = canvas.clientWidth;
    canvas.height = canvas.clientHeight;
  } else {
    var title = document.getElementsByTagName("title")[0].innerText;
    var h1 = document.createElement("h1");
    h1.innerText = title;
    document.body.insertBefore(h1, document.body.children[0]);
  }
  var gl = setupWebGL(canvas);
  return gl;
};
var loadShader = function (gl, shaderSource, shaderType, opt_errorCallback) {
  var errFn = opt_errorCallback || error;
  var shader = gl.createShader(shaderType);
  gl.shaderSource(shader, shaderSource);
  gl.compileShader(shader);
  var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  if (!compiled) {
    lastError = gl.getShaderInfoLog(shader);
    errFn("*** Error compiling shader '" + shader + "':" + lastError);
    gl.deleteShader(shader);
    return null;
  }
  return shader;
}
var loadProgram = function (gl, shaders, opt_attribs, opt_locations) {
  var program = gl.createProgram();
  for (var ii = 0; ii < shaders.length; ++ii) {
    gl.attachShader(program, shaders[ii]);
  }
  if (opt_attribs) {
    for (var ii = 0; ii < opt_attribs.length; ++ii) {
      gl.bindAttribLocation(program, opt_locations ? opt_locations[ii] : ii, opt_attribs[ii]);
    }
  }
  gl.linkProgram(program);
  var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  if (!linked) {
    lastError = gl.getProgramInfoLog(program);
    error("Error in program linking:" + lastError);
    gl.deleteProgram(program);
    return null;
  }
  return program;
};
var createShaderFromScript = function (gl, scriptId, opt_shaderType, opt_errorCallback) {
  var shaderSource = "";
  var shaderType;
  var shaderScript = document.getElementById(scriptId);
  if (!shaderScript) {
    throw ("*** Error: unknown script element" + scriptId);
  }
  shaderSource = shaderScript.text;
  if (!opt_shaderType) {
    if (shaderScript.type == "x-shader/x-vertex") {
      shaderType = gl.VERTEX_SHADER;
    } else if (shaderScript.type == "x-shader/x-fragment") {
      shaderType = gl.FRAGMENT_SHADER;
    } else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) {
      throw ("*** Error: unknown shader type");
      return null;
    }
  }
  return loadShader(gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType, opt_errorCallback);
};
this.createProgram = loadProgram;
this.createShaderFromScriptElement = createShaderFromScript;
this.getWebGLContext = getWebGLContext;
this.updateCSSIfInIFrame = updateCSSIfInIFrame;
window.requestAnimFrame = (function () {
  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback, element) {
    return window.setTimeout(callback, 1000 / 60);
  };
})();
window.cancelRequestAnimFrame = (function () {
  return window.cancelCancelRequestAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame || window.clearTimeout;
})(); 
135 lines of code.

Oh... well then


dimensional confusion


WebGL is actually a 2D library.
Same with OpenGL!


libraries instead


Smart people have thought about this.

That's why we use libraries!


Intro to three.js

A crash course in practical 3D

WHy three.js?


Three.js was a much easier pick.

Everyone uses Three.js!

THREE.js BASICS

Three.js is composed of a scene.
The scene is our 3D space.

Important Three.js terms:
  • camera: the player's viewpoint
  • controls: handle camera movement
  • geometry: a set of vertices
  • material: image or shader
  • mesh: a geometry + a material
  • renderer: scene -> canvas

sample scene


SCENES

 var scene = new THREE.Scene();
Scenes are parents--they have children.

Methods/properties that matter:
  • scene.add
  • scene.remove
  • scene.children
  • scene.getChildByName

cameras

var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
Cameras show a slice of 3D space.

They can either be perspective
or orthographic.

Perspective cameras

Perspective is like normal human vision.

Closer things are bigger, farther are smaller.

Orthographic

All objects are perceived as the same size,
regardless of distance.


Camera comparison

Result

renderers

var renderer = new THREE.WebGLRenderer()
Renderers do the heavy lifting in Three.js.
They translate your 3D space to 2D pixels.

Renderers can also be:
  • CanvasRenderer
  • CSS3Renderer
  • SVGRenderer
  • AsciiRenderer

no, really


why webgl?


WebGL uses your graphics card.

GPU >>>>>>>>>>>>>>> CPU

(see: shaders)

geometries

var cubeGeometry = new THREE.CubeGeometry(10, 10, 10);
Geometries are collections of vertices.

Default geometries include:
  • Cubes
  • Spheres
  • Cones
  • Toruses
  • Knots

sample geometries


materials

var cubeMaterial = new THREE.MeshBasicMaterial({color: 0xff0000});
Materials are the "skins" of objects--
they shade planes between vertices.

Materials can be created/displayed in
a number of different ways.

materialist


  • MeshBasicMaterial: flat color
  • MeshDepthMaterial: color by distance
  • MeshNormalMaterial: color by face vector normal
  • MeshFaceMaterial: different material per face
  • MeshLambertMaterial: diffuse lighting
  • MeshPhongMaterial: shiny lighting
  • ShaderMaterial: h4x material in GLSL

Meshes

var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
Mesh = geometry + material

Also has useful properties:
  • Scale
  • Rotation
  • Position

what's missing?


Number of common things missing:
  • Render loop
  • Lighting
  • Skybox
  • Camera controls
  • Particles

and now it's time

To dive into the code.

98-232: WebGL

By Will Crichton

98-232: WebGL

  • 1,482