ACG: Minimal WebGL Example
Creating a WebGL example is a bit complicated given that HTML, javascript, and GLSL have to be combined all together. Here we attempt to break this down into the basic necessary structure.
WebGL Skeleton
The WebGL skeleton is pretty straight forward. It contains the minimum HTML code needed to set up an OpenGL canvas and provide some vertex and fragment shaders. Javascript is used to initialize and set up the OpenGL. For brevity, the skeleton doesn’t contain the shader nor javascript code which are explained in the next sections.
<html>
<head>
<title>Minimal WebGL Example</title>
</head>
<body>
<canvas id="glCanvas" style="width:400px; height:400px;" />
<!-- type="notjs" makes it not get identified as javascript. -->
<script id="vertex-shader" type="notjs">
VERTEX CODE
</script>
<script id="fragment-shader" type="notjs">
FRAGMENT CODE
</script>
<script type="text/javascript">
JAVASCRIPT CODE
</script>
</body>
</html>
Vertex Shader
The vertex shader is very simple. We are receiving the position as an attribute using
the in
keywoard, and this directly determines the output pixel. At the same
time, we are converting from screen coordinates in the [-1,1]
range to the
[0,1]
range which can be used for textures and the likes. This is set as the
variable coord
which gets passed to the fragment shader using the
in
and out
qualifiers.
#version 300 es
in vec4 pos;
out vec2 coord;
void main() {
coord = pos.xy*0.5+0.5;
gl_Position = pos;
}
Fragment Shader
The fragment shader receives the coord
variable from the vertex shader
and uses this to determine the colour of each pixel.
#version 300 es
precision mediump float;
in vec2 coord;
out vec4 color_out;
void main() {
color_out = vec4(coord.x, 0.5, coord.y, 1.0);
}
WebGL Javascript
Javascript is the core of the WebGL and is used to set up the shaders and vertices to
draw on screen. In this particular case, we are just using a
gl.TRIANGLE_STRIP
to draw two triangles to fill the entire screen. The
shader code is taken from the HTML elements and compiled into a program.
function createProgram( gl, vertexSrc, fragmentSrc ) {
let vshd = gl.createShader( gl.VERTEX_SHADER );
gl.shaderSource( vshd, vertexSrc );
gl.compileShader( vshd );
if (!gl.getShaderParameter( vshd, gl.COMPILE_STATUS ))
throw new Error( "Unable to compile shader: "+gl.getShaderInfoLog( vshd ));
let fshd = gl.createShader( gl.FRAGMENT_SHADER );
gl.shaderSource( fshd, fragmentSrc );
gl.compileShader( fshd );
if (!gl.getShaderParameter( fshd, gl.COMPILE_STATUS ))
throw new Error( "Unable to compile shader: "+gl.getShaderInfoLog( fshd ));
let prog = gl.createProgram();
gl.attachShader( prog, vshd );
gl.attachShader( prog, fshd );
gl.linkProgram( prog );
if (!gl.getProgramParameter( prog, gl.LINK_STATUS ))
throw new Error( "Unable to link program: "+gl.getProgramInfoLog( prog ));
return prog;
}
function init() {
var c = document.getElementById("glCanvas");
var gl = c.getContext('webgl2');
if (!gl)
throw new Error("WebGL unsupported!");
// Clear screen
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
/*
2___3
|\ |
| \ |
|__\|
0 1
*/
var vertexPosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
var vertices = [
-1, -1,
1, -1,
-1, 1,
1, 1 ];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
vertexPosBuffer.itemSize = 2;
vertexPosBuffer.numItems = 4;
// Create our shader program
var vs = document.querySelector("#vertex-shader").text.trim();
var fs = document.querySelector("#fragment-shader").text.trim();
var program = createProgram(gl, vs, fs);
gl.useProgram(program);
// Get and set vertex attribute
program.vertexPosAttrib = gl.getAttribLocation(program, 'pos');
gl.enableVertexAttribArray(program.vertexPosAttrib);
gl.vertexAttribPointer(program.vertexPosAttrib, 2, gl.FLOAT, false, 0, 0);
// Draw triangles
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
window.onload = init;
Example Output:
Below we can see an example of what you should expect from this demo.