eπi +1 = 0
i² = j² = k² = ijk = -1
CPU bad (slower) (click to zoom) | GPU good (faster) (mouse-wheel to zoom) |
Refresh page if panel(s) blank |
“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
Web standard | |
Cross-device | |
Cross-platform | |
Combination of JavaScript API | |
and GLSL language |
Canvas based | |
GPU access from browser | |
Rasteriser - 2D and 3D graphics and more |
|
Control via shader programs |
JavaScriptCreate Context/Canvas Compile shaders Data control Draw commands GLSLShader program Vertex shader transform vertices Fragment (pixel) shader transform pixels |
<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
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);
}
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);
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");
Render pipeline (simplified)
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]);
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);
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);
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);
}
Fragment shader - run for each pixel
precision mediump float;
varying vec3 vColour;
void main() {
gl_FragColor = vec4(vColour, 1.0);
}
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)
attribute
uniform
varying
Variables initialised
Out of bounds
Nothing is free
You have to work for it
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);
}
Libraries to the rescue
mat4.perspective
mat4.ortho
So, life is hard in vanilla WebGL land
APIs to the rescue
Two of the best known
Plenty of support
Big communities
On-line editors of varying utility
Three.js is a bit... unstable
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();
meetup.com/WebGL-Workshop-London | |
[email protected] | |
@CarlBateman | |
linkedin.com/in/dcbateman |