Getting started with WebGL
Written by David Conrad   
Wednesday, 09 February 2011
Article Index
Getting started with WebGL
Using shaders
Drawing and rendering

Shader practice

OK so now we have our two shaders how do we get them into the WebGL object?

The shader code is first stored as a string in a suitable variable. It is then stored in a shader object of the correct type and compiled. This has to be done for both the vertex and fragment shader. Then the two shaders are combined into a single program that the GPU can run to render our 3D model.

So starting with the vertex shader, first we have to store the code into a string:

var vsScript="attribute vec3 vertexPosition;";
vsScript += "uniform mat4 modelViewMatrix;"
vsScript += "uniform mat4 perspectiveMatrix;";
vsScript += "void main(void) {"
vsScript += "gl_Position = ";
vsScript += "perspectiveMatrix * ";
vsScript += "modelViewMatrix * ";
vsScript += "vec4(vertexPosition, 1.0);}";

This is just the vertex shader code given earlier stored in vsScript. 

Next we have to create a shader object of the correct type:

var vertexShader = gl.createShader(
gl.VERTEX_SHADER);

add it  with the code to the WebGL object and compile the shader:

gl.shaderSource(vertexShader, vsScript);
gl.compileShader(vertexShader);

As long as there are no syntax errors in the code we now have a compiled shader ready to be used. However syntax errors are common so we need to check that it worked:

if(!gl.getShaderParameter(vertexShader, 
gl.COMPILE_STATUS)) {
alert("Error in vertex shader");
gl.deleteShader(vertexShader);
return;
}

We have to repeat the whole thing over again to enter and compile the fragment shader. Rather than splitting the steps down it is more reasonable to simply present the code:

var fsScript = "void main(void) {";
fsScript+="gl_FragColor =
vec4(0.0, 6.0, 0.0, 1.0);}";
var fragmentShader = gl.createShader(
gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsScript);
gl.compileShader(fragmentShader);
if(!gl.getShaderParameter(fragmentShader,
gl.COMPILE_STATUS)) {
alert("error in fragment shader");
gl.deleteShader(fragmentShader);
return;
}

Now we are almost done with the shaders. All that remains is to link them together into a single program that the GPU can run.  This always follows the same steps.

First create a program object, attach the compiled shaders to it and then link them together:

var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

At this point everything is ready to go but we also should check that it has worked and if not clean up and exit the function:

if (!gl.getProgramParameter(program,
 gl.LINK_STATUS)) {
alert("Error in shaders");
gl.deleteProgram(program);
gl.deleteProgram(vertexShader);
gl.deleteProgram(fragmentShader);
return;
}

Finally we have to tell the GPU to use the program:

gl.useProgram(program);

Setting up the shaders always follows these fairly tedious steps and there is clearly a better way to do the job. In practice you need to setup a function that will compile and link the shaders you specify.

The matrices - connecting with the shaders

At some point in working with WebGL you have to define the matrices used in the shader. In general you have to make connections between all of the variables used in the shaders and objects in the JavaScript program. In this case you have to connect the use of

uniform mat4 modelViewMatrix;
uniform mat4 perspectiveMatrix;

in the vertex shader and two JavaScript matrices that specify the transformations. At this point in most tutorials we have to take a detour to explain how the two matrices are constructed. In full OpenGL there are a lot of handy helper functions that will build and manipulate matrices and construct perspective transformations from "camera" like specifications. These aren't available in WebGL (there are alternatives but more of these in another article).

To make things simple we are going to use predefined transformation matrices - how they are constructed isn't difficult but it takes us away from the main part of the story.

The model view matrix might as well just be the identity matrix i.e. no transformation of the 3D co-ordinates is performed:

var mvMatrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
];

This defines the matrix we want the shader to use but how do we make the connection between it and the variable that the shader declares?

Making the connection between the JavaScript and Shader follows the same standard steps using methods provided by the WebGL object. First you have to retrieve a reference to the shader object and then you use this to connect it to the JavaScript object.

First get the reference to the shader object:

var shaderMVMatrix = gl.getUniformLocation(
program, "modelViewMatrix");

You have to specify the program and the variable within the program that you want the reference to. (The reference is in fact an index into a table of variables.)

Next you make the connection, there are a set of methods that you can use depending on the data type being transferred to the shader. In this case the method we need is

gl.uniformMatrix4fv(shaderMVMatrix, 
false,
new Float32Array(mvMatrix));

You can see that this copies the data in mvMatrix to the specified shader variable. From this point on the shader will use the specified matrix.

The procedure is the same for the perspective matrix:

var pMatrix = [
3, 0, 0, 0,
0, 3, 0, 0,
0, 0, 1, 2,
0, 0, -1, 0];
var shaderpMatrix = gl.getUniformLocation(
program, "perspectiveMatrix");
gl.uniformMatrix4fv(shaderpMatrix,
false,
new Float32Array(pMatrix));

Again the perspective matrix provided is just one that results in a reasonable view of the simple object we are going to draw - you can worry about how to work out what the perspective matrix has to be in nay particular situation later.

<ASIN:0321552628>

<ASIN:0321687299>

<ASIN:0596806132>



Last Updated ( Wednesday, 04 June 2014 )