WebGL




CS116b/CS216

Chris Pollett

May 5, 2014

Outline

Introduction

1D, 2D and 3D grids of Mass-springs

Fire and Water and Humans

Quiz

Which of the following statements is true?

  1. In our framework for moving Thing objects last day, we could directly update objects in the onRender method.
  2. According to our solution for elastic collisions of last day, two object of the same mass that collide head on will exit the collision at right angles to where they collided.
  3. In smooth skinning a vertex is typically assigned to more than one bone and is updated according to a weighted average of the updates that would be done for each bone.

WebGL

Using WebGL

A Complicated Way to Draw a Red Screen

The code below illustrates setting up a WebGL context. To see what it looks like look at the WebGL getContext Demo Page.

<!DOCTYPE html>
<html>
    <head>
        <title>Basic HTML 5 document with a WebGL Canvas</title>
        <meta charset="utf-8" />
        <script type="text/javascript">
        var gl; // A global variable for the WebGL context
        function initWebGL(canvas) 
        {
            gl = null;
            try {
                // Try to grab the standard context.
                // If it fails, fallback to experimental.
                gl = canvas.getContext("webgl") ||
                    canvas.getContext("experimental-webgl");
            } catch(e) {
                alert("getContext didn't work");
            }

            // If we don't have a GL context, give up now
            if (!gl) {
                alert("Unable to initialize WebGL.");
                gl = null;
            } 
            return gl;
        }
        function start() 
        {
           var canvas = document.getElementById("glcanvas");
           gl = initWebGL(canvas);      // Initialize the GL context
           // Only continue if WebGL is available and working
           if (gl) {
             gl.clearColor(1.0, 0.0, 0.0, 1.0);  
                // Set clear color to red
             gl.enable(gl.DEPTH_TEST);  // Enable depth testing
             gl.depthFunc(gl.LEQUAL);  // Near things obscure far things
             gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT); 
                // Clear the color as well as the depth buffer.
           }
       }
       </script>
   </head>
   <body onload="start()">
   <canvas id="glcanvas" style="width:640px; height:480px" >
   To see this demo your browser needs to support the canvas element.
   $5 donations accepted
   </canvas>
   </body>
</html>

Remarks on Code

Making A More Complicated Example with Shaders

New Start Function

function start() 
{
    canvas = document.getElementById("glcanvas");
    initWebGL(canvas);// Initialize the GL context
    // Only continue if WebGL is available and working
    if (gl) {
        gl.clearColor(1.0, 0.0, 0.0, 1.0);
        // Set clear color to red
        gl.clearDepth(1.0);
        gl.enable(gl.DEPTH_TEST);  // Enable depth testing
        gl.depthFunc(gl.LEQUAL);  // Near things obscure far things

        // Initialize the shaders; this is where all the lighting for the
        // vertices and so forth is established.
        initShaders();
        // Here's where we call the routine that builds all the objects
        // we'll be drawing.
        initBuffers();
        // Set up to draw the scene periodically.
        setInterval(drawScene, 15);
    }
}

initShaders

initBuffers

initBuffer sets up the vertex buffere for our square.

function initBuffers() 
{
    // Create a buffer for the square's vertices.
    squareVerticesBuffer = gl.createBuffer();
    // Select the squareVerticesBuffer as the one to apply vertex
    // operations to from here out.
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
    // Now create an array of vertices for the square. Note that the Z
    // coordinate is always 0 here.
    var vertices = [
    1.0,  1.0,  0.0,
    -1.0, 1.0,  0.0,
    1.0,  -1.0, 0.0,
    -1.0, -1.0, 0.0
    ];
    // Now pass the list of vertices into WebGL to build the shape. We
    // do this by creating a Float32Array from the JavaScript array,
    // then use it to fill the current vertex buffer.
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}

drawScene

Here is the code to actually draw the vertex buffer we had using the shader we have set up. The function makePerspective all comes from glUtils.js and make use of sylvester.js.

Notice we set up an attribute pointer immediate after the bindBuffer call to say how the vertices will be mapped into aVertexPosition (3 coordinates, each vertex is a float, vertices should be fixed point rather than normalized, no stride and no first pointer).

function drawScene()
{
    // Clear the canvas before we start drawing on it.
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    // Establish the perspective with which we want to view the
    // scene. Our field of view is 45 degrees, with a width/height
    // ratio of 640:480, and we only want to see objects between 0.1 units
    // and 100 units away from the camera.
    perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0);
    // Set the drawing position to the "identity" point, which is
    // the center of the scene.
    loadIdentity();
    // Now move the drawing position a bit to where we want to start
    // drawing the square.
    mvTranslate([-0.0, 0.0, -6.0]);
    // Draw the square by binding the array buffer to the square's vertices
    // array, setting attributes, and pushing it to GL.
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
    gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}

setMatrixUniforms passes our projection and model view matrices to shader land:

function loadIdentity() 
{
    mvMatrix = Matrix.I(4); //Matrix is sylvester
}
function multMatrix(m) 
{
    mvMatrix = mvMatrix.x(m);
}
function mvTranslate(v)
{
    multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4());
}
function setMatrixUniforms()
{
    var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    gl.uniformMatrix4fv(pUniform, false, 
        new Float32Array(perspectiveMatrix.flatten()));
    var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
    gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten()));
}