WebGL, Three.js
and Shaders

Nikolai Janakiev @njanakiev

WebGL

  • JavaScript API for 3D Graphics rendering with the GPU
  • Based on OpenGL ES
  • Uses the HTML <canvas> element

WebGL Code


/*============== Creating a canvas ====================*/
var canvas = document.getElementById('canvas_triangle');
gl = canvas.getContext('experimental-webgl');

var vertices = [
   -0.5,0.5,0.0,
   -0.5,-0.5,0.0,
   0.5,-0.5,0.0, 
];

indices = [0,1,2];
var vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var Index_Buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

/*================ Shaders ====================*/
var vertCode =
   'attribute vec3 coordinates;' +
		
   'void main(void) {' +
      ' gl_Position = vec4(coordinates, 1.0);' +
   '}';
var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, vertCode);
gl.compileShader(vertShader);
var fragCode =
   'void main(void) {' +
      ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' +
   '}';
   
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, fragCode); 
gl.compileShader(fragShader);
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertShader);
gl.attachShader(shaderProgram, fragShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);

/*======= Associating shaders to buffer objects =======*/
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
var coord = gl.getAttribLocation(shaderProgram, "coordinates");
gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0); 
gl.enableVertexAttribArray(coord);

/*=========Drawing the triangle===========*/
gl.clearColor(0.5, 0.5, 0.5, 0.9);
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.viewport(0,0,canvas.width,canvas.height);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
					

Simple Scene


const canvas = document.getElementById("canvas");
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer({canvas: canvas, alpha: true});
const camera = new THREE.PerspectiveCamera( 35, canvas.width / canvas.height, 0.1, 1000 );
camera.position.z = 8;
camera.position.y = 3;
camera.lookAt(new THREE.Vector3(0,0,0));

const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

function render() {
	mesh.rotation.y += 0.02;

	requestAnimationFrame( render );
	renderer.render( scene, camera );
}
render();
					

Geometries and Materials


const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);

const pointLight = new THREE.PointLight(0xffffff, 0.5);
pointLight.position.z = 100;
scene.add(pointLight);

const geometry = new THREE.TorusGeometry( 1.6, 0.8, 20, 80 );
const material = new THREE.MeshPhongMaterial( { color: 0x55eeee, shininess: 80.0} );
const mesh = new THREE.Mesh( geometry, material );
scene.add(mesh);

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

render();
					
threejs.org/examples/webgl_geometries.html
threejs.org/examples/webgl_materials.html

Loading Meshes with Three.js


const loader = new THREE.PLYLoader();
loader.load( './code/models/bunny.ply', function ( geometry ) {
	geometry.computeVertexNormals();
	const material = new THREE.MeshNormalMaterial();
	const mesh = new THREE.Mesh( geometry, material );
	
	scene.add( mesh );

	console.log("Loaded Mesh");
} );
					

OpenGL graphics pipeline

(simplified)

OpenGL Shading Language (GLSL)

  • High-level shading language based on the C-language
  • Programming on the GPU
  • Vertex Shader / Fragment Shader

Vertex Shader


uniform mat4 u_modelViewProjMatrix; // Passed in from CPU
attribute vec4 vPosition; // Information from vertex

void main()
{
    gl_Position = u_modelViewProjMatrix * vPosition;
}
					

Fragment Shader


uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main() {
	vec2 st = gl_FragCoord.xy / u_resolution.xy;
	gl_FragColor = vec4(st.x, st.y, 0.0, 1.0);
}
					

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main(){
    vec2 st = gl_FragCoord.xy / u_resolution.xy;
    vec3 color = vec3(0.0);

    vec3 m_dist = vec3(0.0);
    const int n = 10;
    for(int i = 0; i < n; i++){
        vec2 pos = random2(vec2(float(i), 0.2)) * 0.2 + 0.5;

        m_dist += 1.0 / distance(st, pos);
    }

    color += smoothstep(0.49, 0.5, m_dist * 0.01);
    
    gl_FragColor = vec4(color, 1.0);
}
					

void main(){
    vec2 st = gl_FragCoord.xy / u_resolution.xy;
    vec2 p =  u_mouse.xy / u_resolution.xy;
    vec3 color = vec3(0.0);
    float m_dist = 100.0;
    vec2 m_point;

    const n = 5;
    for(int i = 0; i < n; i++){
        vec2 pos = random2(vec2(float(i), 0.2)) * 0.5 + 0.5;
        float dist = length(st, point);

        if( dist < m_dist ) {
            m_dist = dist;
            m_point = point;
        }
    }
    color += dot(m_point, vec2(0.3, 0.6));
    gl_FragColor = vec4(color, 1.0);
}
					

Resources

Nikolai Janakiev @njanakiev
Slides @ http://janakiev.com/webgl-presentation/