Introduction to WebGL
Benoît Jacob (Mozilla Corporation)
FITC Spotlight Javascript, Toronto, March 24, 2012
Online copy of these slides:
http://people.mozilla.org/~bjacob/webgl-spotlight-js-2012
What is WebGL?
- WebGL is a context for the HTML canvas element, with an API closely following OpenGL ES 2.0.
- In other words: "OpenGL ES 2.0 for the Web"
- OpenGL (hence WebGL) is a low-level graphics API
- It's neither a "3D" nor a "2D" graphics API.
- Too low-level to care.
- Lets you do either.
WebGL demos
2D game:
http://www.phoboslab.org/xtype
3D cars:
http://alteredqualia.com/three/examples/webgl_cars.html
Ported 3D game:
http://dl.dropbox.com/u/6873971/data/cube2/index.html
Path tracing demo:
http://madebyevan.com/webgl-path-tracing
Pure shader demo:
http://iacopoapps.appspot.com/hopalongwebgl
Physics simulations:
http://madebyevan.com/webgl-water
http://www.ibiblio.org/e-notes/webgl/gpu/fluid.htm
Photo editing demo:
http://evanw.github.com/webgl-filter
WebGL browsers
Chrome and Firefox: WebGL is enabled by default
Opera 12 alpha: WebGL enabled by default
Safari 5.1: WebGL can be enabled in developer menu
Very good compatibility across browsers, thanks to extensive conformance test suite (thanks especially to Google!)
WebGL and blocked GPU drivers
Browsers block old drivers for security, stability
Blacklists wiki page
With Firefox, about 50% of attempts to view WebGL content are successful
Browsers may provide a non-accelerated fallback. Chrome 18; Mozilla plans to use Mesa LLVMpipe.
Some sites fall back to Canvas 2D, or DOM-based rendering. Example: Angry Birds (DOM), Google Maps (DOM), Three.js (Canvas 2D).
WebGL: what to expect in 2012
Increasing % of users who get WebGL, especially on Mobile
All-around browser improvements to run games better
Compressed textures
More extensions: cross-context sharing, depth textures, perhaps multiple render targets.
WebGL in web workers and/or asynchronous operations
More content creation tools.
More porting tools.
Getting a WebGL context
var gl;
try {
gl = canvas.getContext("experimental-webgl");
} catch(e) {}
Soon "experimental-webgl" will become "webgl". Prepare to try both.
Creating a WebGL context is slow (often > 5 ms, sometimes > 50 ms).
Runnable example
My first WebGL program
gl.clearColor(0.8, 0.2, 0.6, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
Runnable example
My first nontrivial WebGL program
A white triangle
A colored triangle
Understanding these examples is the goal of the rest of this presentation.
WebGL rendering pipeline
WebGL renders triangles
WebGL only draws points, line segments, and triangles.
In 99% of cases, that means triangles only.
For example, a rectangle can be drawn as two triangles.
WebGL native coordinate system
WebGL native coordinates go from -1 to +1.
X axis: -1 is the left end of the canvas, +1 is the right end.
Y axis: -1 is the bottom of the canvas, +1 is the top.
The Z axis is used for depth testing. -1 is nearest, +1 is farthest.
Vertex shaders
In WebGL, you don't say "Here are some vertex coordinates".
Instead, you say "Here is a program that computes each vertex's native coordinates".
That program is called the vertex shader.
The GPU iterates over vertices, runs the vertex shader on each.
Vertex attributes and uniforms
A vertex shader typically reads some data to compute a vertex's native coordinates.
Some of that data is specific to each vertex. Such vertex-dependent variables are called vertex attributes. Examples:
- The world coordinates of each vertex
- The color of each vertex
- The texture coordinates of each vertex
- The normal vector at each vertex
Some other variables are shared by all vertices. These are called uniforms. Examples:
- Properties of the viewpoint (camera position...)
- Properties of lights
A vertex shader for static 2D drawing
/* Declare a vertex attribute giving the vertex
* position in native coordinates.
*/
attribute vec2 vertexPosition;
void main() {
/* gl_Position is a built-in variable.
* Assigning to it sets the resulting vertex
* position in native coordinates.
*/
gl_Position = vec4(vertexPosition, 0.0, 1.0),
}
A vertex shader for a scrolling 2D game
attribute vec2 vertexPosition;
uniform vec2 cameraPosition;
void main() {
gl_Position = vec4(vertexPosition - cameraPosition, 0.0, 1.0),
}
A vertex shader for a 3D scene
attribute vec3 vertexPosition;
uniform mat4 modelview;
uniform mat4 projection;
void main() {
gl_Position = projection * (modelview * vec4(vertexPosition, 1.0)),
}
Fragment shaders
In WebGL, you don't say "Here is the color to use for this pixel".
Instead, you say "Here is a program that computes each pixel's color".
That program is called the fragment shader.
The GPU iterates over pixels, runs the fragment shader on each.
A fragment shader painting all in white
/* Have to specify default precision in fragment shaders
*/
precision mediump float;
void main(void) {
/* gl_FragColor is a built-in variable.
* Assigning to it sets the resulting pixel color.
*/
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
Passing data from the vertex to the fragment shader
Can be done by interpolating the vertex data for each fragment between vertices.
Example: each vertex has a color. Want to interpolate colors between vertices.
Such an interpolated variable is called a varying.
The vertex shader assigns to the varying. The fragment shader reads from it.
Vertex shader:
attribute vec2 vertexPosition;
attribute vec4 vertexColor;
varying vec4 varyingColor;
void main() {
varyingColor = vertexColor;
gl_Position = vec4(vertexPosition, 0.0, 1.0),
}
Fragment shader:
precision mediump float;
varying vec4 varyingColor;
void main() {
gl_FragColor = varyingColor;
}
Setting up shaders
From the point of view of JavaScript, shader sources are just strings.
These are passed to WebGL and compiled in opaque 'shader' objects.
One then links together the vertex and fragment shader into an opaque 'program' object.
The program is the set of shaders used for drawing at a given time.
var vertexShaderString =
'attribute vec2 vertexPosition; \n\
void main(void) { \n\
gl_Position = vec4(vertexPosition, 0.0, 1.0); \n\
} \n';
var fragmentShaderString =
'void main(void) { \n\
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); \n\
} \n';
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderString);
gl.compileShader(vertexShader);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderString);
gl.compileShader(fragmentShader);
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
Setting up vertex attributes
The data of vertex attributes, such as vertex positions, is stored in special arrays called WebGL buffers.
One first sets up a WebGLBuffer object, and one then specifies how a given vertex attribute sources it.
One refers to a vertex attribute by means of its location in a given program, which is determined when the program is linked.
var vertexPositionAttrLoc = gl.getAttribLocation(program, "vertexPosition");
gl.enableVertexAttribArray(vertexPositionAttrLoc);
vertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
var vertices = [ 0.0, 1.0,
-1.0, -0.5,
1.0, -0.5 ];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(vertexPositionAttrLoc, 2, gl.FLOAT, false, 0, 0);
The actual drawing
Once the program and its data sources (like vertex attributes) have been set up, drawing is simple:
gl.drawArrays(gl.TRIANGLES, 0, 3);
This completes the explanation of our White Triangle and Colored Triangle examples.
Continue learning
Famous tutorial:
http://learningwebgl.com/lessons/
Mozilla resources:
https://developer.mozilla.org/en/WebGL
Blogs:
http://planet-webgl.org
http://www.webgl.com
Mailing list:
http://groups.google.com/group/webgl-dev-list
Questions?