WebGL
Category | Platforms |
---|
Overview
WebGL (Web Graphics Library)
WebGL is a JavaScript API for rendering real-time 3D and 2D graphics in a web browser.
- WebGL 1.0 supports OpenGL ES 2.0.
- WebGL 2.0 supports OpenGL ES 3.0.
WebGL rendering is done inside an HTML canvas
.
A WebGL program is made of JavaScript code and GLSL shader code executed on a GPU.
GLSL
GLSL (Graphics Library Shader Language)
Shader languages used by OpenGL and OpenGL ES to write vertex and fragment functions executed on a GPU.
Support
WebGL 1.0
https://caniuse.com/#feat=webgl
- Google Chrome 9+
- Firefox 4+
- Safari 5.1+
- Microsoft Edge
- Internet Explorer 11
- Opera 15+
WebGL 2.0
https://caniuse.com/#feat=webgl2
- Google Chrome 56+
- Firefox 51+
- Opera 43+
Loading
When running a local HTML file (file:///
), loading files from the file system will fail with a security exception.
This is caused by the same origin policy security restrictions in the browsers.
http://en.wikipedia.org/wiki/Same_origin_policy
There are several solutions:
- Set the browser to allow file access from files.
- Run the HTML file from a local web server.
Browser
Windows
chrome.exe --allow-file-access-from-files
macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --allow-file-access-from-files
Local web server
NPM
The easiest way to run a local web server is to install Node.js using the npm package manager.
npm install http-server -g
Then run http-server in a local directory.
http-server . -p 8000
Chrome
An alternative is to install a browser extension that hosts a local web server.
https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb
Shaders
Clip Space
In OpenGL ES, the clip space for the Z axis is in the range [-1, 1].
Attributes
Attributes are vertex data provided as input to the vertex shader function.
vec4 attributes default to [0, 0, 0, 1], which is a valid zero positions (w is set to 1).
A position attribute passed as a vec2 or vec3 will still be a valid position.
However, normals must ensure that w is set to 0.
Varyings
Varyings are variables that are declared by the vertex shader and used to pass interpolated data from the vertex shader to the fragment shader.
Uniforms
Uniforms are variables set by the JavaScript code and are available to both the vertex and fragment shaders.
Data Types
- Vectors: vec2, vec3, vec4.
- Matrices: mat4.
Vertex Shader
The responsibility of the vertex shader is to transform input vertices.
gl_Position
is a special variable that must be set by the vertex shader with the transformed vertex position.
Fragment Shader
The responsibility of the fragment shader is to determine the color of the pixels.
gl_FragColor
is a special variable that must be set by the fragment shader with the pixel's color.
Memory
Hardware memory needs to be released explicitly.
- Vertex and index buffers
- Uniforms
- Textures
- Render targets
- Shader programs
Tutorial
Initialization
- Create an HTML with a canvas.
- Get the WebGL drawing context from the canvas.
- Validate that WebGL is supported.
- Start the render loop.
Create an empty HTML page with an empty body
tag.
<!DOCTYPE html>
<html>
<body>
</body>
</html>
Add a canvas
tag inside the body
tag with a an ID.
Add an empty script
tag for the JavaScript code.
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas"></canvas>
<script>
</script>
</body>
</html>
In the script
tag, initialize WebGL by getting the canvas
from its ID in the document using getElementById(id)
. Alternatively, look up the canvas with querySelector(selectors)
.
const canvas = document.getElementById("canvas");
Get the drawing context for WebGL from the canvas by calling the getContext(contextType)
function with the "webgl" or "experimental-webgl" identifier.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
const gl = canvas.getContext("webgl");
Validate that the drawing context implements the WebGLRenderingContext
interface for WebGL 1.0.
if (!gl || !(gl instanceof WebGLRenderingContext)) {
alert("Unable to initialize WebGL.");
return;
}
After the WebGL initialization, we define a custom function, that registers itself as a callback with WebGL to render the next frame.
To render the following frame, the callback function calls requestAnimationFrame(callback)
on the window with a reference to itself to setup a render loop.
function render() {
requestAnimationFrame(render);
}
requestAnimationFrame(render);
Shader
- Create a shader program
- Create a vertex and fragment function from shader source code.
- Compile the shader functions.
- Attach the vertex and fragment functions to the shader program.
- Link the shader program.
- Retrieve the attributes and uniforms locations from the shader program.
The shader code can be written directly in JavaScript code as a string literal.
The back-tick character (`) can be used to write multiline string literals.
WebGLProgram
represents a shader program made of a vertex and a fragment shader function.
WebGLShader
represents a vertex or a fragment shader function.
Create a shader function
For both the vertex shader and fragment shader sources, we create a corresponding shader function.
- Create a shader with
createShader(type)
with typeVERTEX_SHADER
orFRAGMENT_SHADER
type.
- Provide the source string for the shader with
shaderSource(source)
.
- Compile the shader into binary data with
compileShader(shader)
.
- Verify that the shader was successfully compiled by calling
getShaderParameter(shader, pname)
with theCOMPILE_STATUS
value. The return value is a boolean that indicates the status of the compilation.
- If the compilation has failed, the
getShaderInfoLog(shader)
function returns the information about the compilation.
Create a shader program
- Create a shader program with
createProgram()
.
- Attach the vertex shader and fragment shader functions to the program with
attachShader(program, shader)
.
- Link the shader program with
linkProgram(program)
.
- Verify that the shader program was successfully linked by calling
getProgramParameter(program, pname)
with theLINK_STATUS
value. The return value is a boolean that indicates the status of the link.
- If the compilation has failed, the
getProgramInfoLog(program)
function returns the information about the link.
Get the location of shader attributes
The input data for the vertex function is stored in attributes. The location of attributes in a shader program is represents as an int number.
The constant data for the vertex and fragment functions is stored in uniforms. The location of a uniform variable in a shader program is represented by WebGLUniformLocation
.
- Get the location of attributes by name with the
getAttribLocation(program, name)
function.
- Get the location of uniforms by name with the
getUniformLocation(program, name)
function.
Buffers
- Create buffers.
- Copy vertex and index data to the buffers.
A memory buffer on the GPU is represented by WebGLBuffer
.
A buffer can store vertices, indices, textures and other data.
Create a vertex buffer
- Create a hardware buffer with
createBuffer()
.
- Bind the buffer with
bindBuffer(target, buffer)
. SpecifyARRAY_BUFFER
for the target parameter to indicate that the buffer contains vertices.
- Create the buffer data store with
bufferData(target, srcData, usage)
. Specify an array of position values for the srcData parameter. Typically, an array of float3 or float4 values (Float32Array
). SpecifySTATIC_DRAW
for the usage parameter to indicate that the content of the buffer will not change.
Create an index buffer
- Create a hardware buffer with
createBuffer()
.
- Bind the vertex buffer with
bindBuffer(target, buffer)
. SpecifyELEMENT_ARRAY_BUFFER
for the target parameter to indicate that the buffer contains indices.
- Create the buffer data store with
bufferData(target, srcData, usage)
. Specify an array of index values for the srcData parameter. Typically, an array of uint16 (Uint16Array
) or uint32 values (Uint32Array
). SpecifySTATIC_DRAW
for the usage parameter to indicate that the content of the buffer will not change.
Render the scene
- Resize the canvas to match the display size.
- Setup the viewport dimensions
- Clear the frame buffer and depth buffer.
- Setup the render states.
- Face orientation, culling, depth, blending...
- Buffers
- Uniforms and samplers
- Textures
- Shader program
- Draw the primitives
Before rendering a scene, we resize the canvas if necessary
- Update
gl.canvas.width
andgl.canvas.height
.
To render a scene, we set the render states and draw the primitives.
- Set the viewport with
viewport(x, y, width, height)
. To cover the entire canvas, set the x and y parameters to 0 and width and height togl.canvas.width
andgl.canvas.width
.
- Clear the frame buffer with a color value.
- First set the clear color with
clearColor(red, green, blue, alpha)
.
- Then clear the frame buffer with
clear(mask)
and the mask parameter set toCOLOR_BUFFER_BIT
.
- First set the clear color with
- Bind the buffers with
bindBuffer(target, buffer)
.
- Bind the vertex buffer to the corresponding vertex attribute with
vertexAttribPointer(index, size, type, normalized, stride, offset)
. The index parameter is the corresponding attribute location in the shader. The size parameter is the number of components in the vertex buffer (ie 3 for vec3 values). The type parameter is set toFLOAT
for floating-point values. The normalized parameter is typically set to false to keep the data unchanged. The stride and offset parameters are set to 0 for a vertex buffer that contains a single type of data; otherwise the appropriate layout is specified.
- Enable the vertex attribute with
enableVertexAttribArray(index)
.
- Set the shader program with
useProgram(program)
.
Whether an index buffer is set in the render states, primitives are drawn using a different function.
Draw primitives
- Draw the primitives with
drawArrays(mode, first, count)
. mode is typically set toTRIANGLES
,TRIANGLE_FAN
orTRIANGLE_STRIP
. first is the index of the first of the first vertex in the vertex buffer. count is the number of vertices to draw in the vertex buffer.
Draw indexed primitives
- Draw the primitives with
drawElements(mode, count, type, offset)
. mode is typically set toTRIANGLES
,TRIANGLE_FAN
orTRIANGLE_STRIP
. count is the number of vertices to draw in the vertex buffer. type is set toUNSIGNED_SHORT
for uint16 values orUNSIGNED_INT
for uint32 values. offset is the byte offset in the index buffer.
Validation
To check if the browser supports WebGL, create a canvas and try to obtain a drawing context with the getContext
function.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
- WebGL 1.0
- Obtain the webgl or experimental-webgl context.
- The context should implement the
WebGLRenderingContext
interface https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext
isWebGLAvailable: function () {
var canvas = document.createElement("canvas");
var gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
return (gl && gl instanceof WebGLRenderingContext);
}
- WebGL 2.0
- Obtain the webgl2 context.
- The context should implement the
WebGL2RenderingContext
interface https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext
isWebGL2Available: function () {
var canvas = document.createElement("canvas");
var gl = canvas.getContext("webgl2");
return (gl && gl instanceof WebGLRenderingContext);
}
Reference
Best Practices
Resources
- Offline:
- Generate mipmaps.
- At initialization and loading only:
- Create shaders, buffers and textures.
- Retrieve shader attributes and uniforms locations.
- Create static buffers by default.
- Create interleaved vertex buffers.
Rendering
- Reduce memory usage.
- Large meshes should use index buffers.
- Do not copy vertex attributes that are not used in the shaders.
- Use compressed textures formats.
- Reduce render states changes.
- Sort primitives by materials.
- Set up instancing.
- Pass uniforms data as arrays instead of values.