Tutorial

From WebGL Public Wiki
Jump to navigation Jump to search

HEIL

Creating the Context

WebGL is built on top of the HTML5 <canvas> element. As for a 2D canvas, you start out by getting a WebGLRenderingContext with a call to the getContext() method of the <canvas> element, passing the string "experimental-webgl". (This string is temporary and will eventually change to "webgl".) The returned object has a set of functions very similar to OpenGL ES 2.0.

sup guys, is hitler jajaja

Initializing the Engine

The next step is to get WebGL up and running. The init() function uses the webgl-debug.js utility library:


function init()
{
    // Initialize
    var gl = initWebGL("example");
    if (!gl) {
        return;
    }

    g.program = simpleSetup(
            gl, "vshader", "fshader", 
            [ "vNormal", "vColor", "vPosition"], [ 0, 0, 0, 1 ], 10000);

    // Set some uniform variables for the shaders
    gl.uniform3f(gl.getUniformLocation(g.program, "lightDir"), 0, 0, 1);
    gl.uniform1i(gl.getUniformLocation(g.program, "sampler2d"), 0);

    // Create a box. with the BufferObjects containing the arrays 
    // for vertices, normals, texture coords, and indices.
    g.box = makeBox(gl);

    // Load an image to use. Returns a WebGLTexture object
    spiritTexture = loadImageTexture(gl, "resources/spirit.jpg");

    // Create some matrices to use later and save their locations in the shaders
    g.mvMatrix = new J3DIMatrix4();
    g.u_normalMatrixLoc = gl.getUniformLocation(g.program, "u_normalMatrix");
    g.normalMatrix = new J3DIMatrix4();
    g.u_modelViewProjMatrixLoc = gl.getUniformLocation(g.program, "u_modelViewProjMatrix");
    g.mvpMatrix = new J3DIMatrix4();

    // Enable all of the vertex attribute arrays.
    gl.enableVertexAttribArray(0);
    gl.enableVertexAttribArray(1);
    gl.enableVertexAttribArray(2);

    // Set up all the vertex attributes for vertices, normals and texCoords
    gl.bindBuffer(gl.ARRAY_BUFFER, g.box.vertexObject);
    gl.vertexAttribPointer(2, 3, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, g.box.normalObject);
    gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, g.box.texCoordObject);
    gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0);

    // Bind the index array
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, g.box.indexObject);

    return gl;
}

The simpleSetup() utility function takes the following parameters:

gl
The WebGL context
"vshader", "fshader",
Ids of the vertex and fragment shaders
"vNormal", "vColor", "vPosition"
Vertex shader attribute names used by the shaders.
[ 0, 0, 0, 1 ], 10000)
Clear color and depth values

This initialization loads the shaders and attaches them to a GLSL program, which is how you define the interface to your shaders. You pass uniform parameters to a shader for values that don't change and vertex attributes for things that do change, like vertices. Most of this is taken care of in the utility library, but you can pass additional values here, as with the lightDir and sampler2d uniforms. This code also tells WebGL to use the arrays the makeBox() function sets up containing the vertices, normals, and texture coordinates.

Setting Up the Viewport

Before you can render, you have to tell the canvas how to map the objects from modeling space to screen space. Initially, an object's geometry is described in modeling coordinates, local coordinates that describe the shape itself. These coordinates are transformed into other types of coordinates as follows:


       MODELING COORDINATES ->> WORLD COORDINATES ->> VIEW COORDINATES ->> VIEWPORT COORDINATES
  • World coordinates are the global coordinate system that takes into account all objects in the scene.
  • View coordinates are the coordinate system that incorporates a virtual camera's view of the scene.
  • Viewport coordinates are the coordinate system that describes the camera projection for the scene (for example, orthographic or perspective) and fits the projected scene into screen space. This projection takes the scene from a 3D to a 2D projection so that it can be displayed on the screen. The textured spinning box example uses a perspective projection, which will make closer objects look larger than further ones, just as in the real world.

A transformation matrix is used to perform the calculations from one coordinate system to the next. In this example, the transformation from modeling to world to view coordinates is performed by the model-view matrix, which combines two transformations into one matrix. The perspective matrix, pMatrix, performs the transformation from view coordinates to viewport coordinates. This perspective matrix is created in the reshape() function and saved for use later at the end of the transformation pipeline, where it transforms view coordinates to viewport coordinates.

The reshape() function uses the matrix function utility library (J3DIMath.js):


function reshape(gl)
{
    // if the display size of the canvas has changed
    // change the size we render at to match.
    var canvas = document.getElementById('example');
    if (canvas.clientWidth == canvas.width && canvas.clientHeight == canvas.height) {
        return;
    }

    canvas.width = canvas.clientWidth;
    canvas.height = canvas.clientHeight;

    // Set the viewport and projection matrix for the scene
    gl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight);
    g.perspectiveMatrix = new J3DIMatrix4();
    g.perspectiveMatrix.lookat(0, 0, 7, 0, 0, 0, 0, 1, 0);
    g.perspectiveMatrix.perspective(30, canvas.clientWidth / canvas.clientHeight, 1, 10000);
}

Drawing the Box

Now you're all set up and you can finally draw your box. Most of the hard work is done, but you still have to tell the box to spin, and to do that you define a model-view matrix, which transforms modeling coordinates to view coordinates. This transformation tells the box where and at what angle you want it to appear. Then you multiply the model-view matrix by the perspective matrix that was saved earlier to complete the transformation all the way from modeling coordinates to viewport coordinates. Note that the order of transformations is significant (that is, matrix multiplication is not commutative). You can also turn the model-view matrix into a normal matrix so it can be used to compute the proper lighting on the box:


function drawPicture(gl)
{
    //Make sure the canvas is sized correctly.
    reshape(gl);

    // Clear the canvas
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // Make a model/view matrix.
    g.mvMatrix.makeIdentity();
    g.mvMatrix.rotate(20, 1, 0, 0);
    g.mvMatrix.rotate(currentAngle, 0, 1, 0);

    // Construct the normal matrix from the model-view matrix and pass it in
    g.normalMatrix.load(g.mvMatrix);
    g.normalMatrix.invert();
    g.normalMatrix.transpose();
    g.normalMatrix.setUniform(gl, g.u_normalMatrixLoc, false);

    // Construct the model-view * projection matrix and pass it in
    g.mvpMatrix.load(g.perspectiveMatrix);
    g.mvpMatrix.multiply(g.mvMatrix);
    g.mvpMatrix.setUniform(gl, g.u_modelViewProjMatrixLoc, false);

    // Bind the texture to use
    gl.bindTexture(gl.TEXTURE_2D, spiritTexture);

    // Draw the cube
    gl.drawElements(gl.TRIANGLES, g.box.numIndices, gl.UNSIGNED_BYTE, 0);

    // Show the framerate
    framerate.snapshot();

    currentAngle += incAngle;
    if (currentAngle > 360) {
        currentAngle -= 360;
    }
}

Finally, you simply add a JavaScript call to requestAnimationFrame to keep changing the angle and rendering the box in its new position—and you have a spinning box!

What's Next?

For a nontextured version of the spinning cube, see the Spinning Box example. Also see the Demo Repository for more WebGL samples.

Khronos WebGL Spinning Box Tutorial is licensed under a Creative Commons Attribution 3.0 Unported License.