Homework 5

Short Description:

Enter a javascript program that draws a cube or a tetrahedron using WebGL.


When you finish this homework, you should

Formal Description

The final project is located here
  1. Pick one:
    1. In your homework directory for this class
      • make make a directory called Common
      • Place the following files in this directory
      • You will use this directory for all future homework in this class.
    2. Link to my files
      • They are located at https://mirkwood.cs.edinboro.edu/~bennett/GraphicsCode/Common
      • With the file name added to each.
      • (Copy the links above)
  2. Go up one directory level and make a directory called HW5
  3. Build a html file
    1. Start with this file, called HW5.html (Either save as or view source, the program produces a blank screen if you click on it)
    2. Add a vertex shader in the space provided in the html file
      • attribute vec4 vPosition;
        void main() {
            float d = 0.2;
            // translate matrix 
            // note the matrix appears to be transposed, it is not.
            //     GLSL is column major, not row major
            mat4 translate = mat4(
                                  vec4(1.0, 0.0, 0.0, 0.0),  // first column
                                  vec4(0.0, 1.0, 0.0, 0.0),  // second column
                                  vec4(0.0, 0.0, 1.0, 0.0),  // third column
                                  vec4(0.0, 0.25, 0.75, 1.0) // last column
            // pinhole camera projection matrix.
            mat4 project = mat4(
                                 vec4(1.0, 0.0, 0.0, 0.0),
                                 vec4(0.0, 1.0, 0.0, 0.0),
                                 vec4(0.0, 0.0, 0.0, 1.0/d),
                                 vec4(0.0, 0.0, 0.0, 0.0)
            // apply the transformations
            gl_Position = project * translate *  vPosition;
      • This vertex shader performs a translation and a pinhole camera projection.
      • Note the matrices might look wrong.
        • This is not the case.
        • WebGL uses Column Major matrices
        • The lines that look like rows are actually columns.
        • This constantly messes me up.
      • Note GLSL's support for matrix-matrix and matrix-vector multiplication.
      • You need to include the .0 in all floating point numbers
        • If it is not there, GLSL's compiler will assume it is an integer
        • And there is not built in coercion, so it will be a syntax error.
    3. Add the fragment shader in the space provided in the html file
      • void main(){
           gl_FragColor = vec4 (1.0, 0.0, 1.0, 1.0) ;
  4. Build the canvas
    1. For this exercise I have rethought my implementation of a canvas.
      • This is closer to the HTML5 canvas
      • It contains the code to setup and maintain the canvas.
      • We will build on this as the semester goes along.
    2. In a file called GLCanvas.js
    3. Build a constructor
      • class Canvas {
            constructor (width, height, Keypress, vertShader, fragShader) {
                this.height = height;
                this.width = width;
                this.canvas.addEventListener("keypress", Keypress);
                this.MakeShaders(vertShader, fragShader);
      • The constructor takes a width, height, and the keypress handler.
      • It calls four auxiliary routines.
      • I am sure you already understand the code in this function.
    4. MakeCanvas
      • This function builds a canvas and places it in the document.
      • This allows us to build multiple canvases if we want them.
      • I really am not concerned with you understanding the code in this function.
      •     MakeCanvas() {
                if (this.width == undefined || this.width < 0) {
                   this.width = 300;
                if (this.height == undefined || this.height < 0) {
                   this.height = 300;
                 this.canvas = document.createElement('canvas')
                 this.canvas.height = this.height;
                 this.canvas.width = this.width;
                 this.canvas.style="border:1px solid #000000;"
    5. SetupGL
      • This code performs the necessary steps to setup the WebGL context for this canvas.
      • I really don't you to know the code in this function either.
      • It uses functions from the library files in Common
      •     SetupGL() {
                this.gl = WebGLUtils.setupWebGL(this.canvas);
                if (!this.gl) {
                    alert ("WebGL isn't available");
    6. MakeShaders
      • This code compiles the shaders and sends them to the GPU
      • You need this code.
      • It uses functions from the library files in Common
      •     MakeShaders(vertShader, fragShader) {
                this.program = initShaders(this.gl, vertshader,fragshader);
    7. Init
      • This function performs context initialization.
      • The first line sets the color to clear the frame buffer to when a clear is called.
        • In this case, it clears to white.
      • The second line establishes the size of the viewport
        • This is the "photographic plate" from the pinhole camera.
        • We set the distance in the vertex shader (d)
        • This is really just the size, in pixels, of the frame we are looking through.
        •     Init() {
                  this.gl.clearColor(1.0, 1.0, 1.0, 1.0);
                  this.gl.viewport(0,0, this.width, this.height);
    8. Add a few utility functions
      • We will need the program, so write Program to return it
        • Program() {
             return this.program;
      • We will need access to the context, so write function GL
        • GL() {
             return this.gl;
      • Finally we will need to clear the canvas, so add Clear
        • This sends a message to the GPU to clear the frame buffer.
        •     Clear() {
          } // closing of class 
      • Your final file should be close to GLCanvas.js
  5. Build a Widget class in Widget.js.
    1. In this case a widget is a thing we will display
      • To build one, we need to know
        1. The context it will be drawn in (gl)
        2. The GLSL program that will draw it (program)
        3. The name of the to program will use to access the buffer holding the data for the widget. (posName)
        4. A list of edges to draw.
    2. Make a constructor
      • class Widget {
            constructor(gl, program, posName, edges) {
                this.visible = true;
                this.size = edges.length;
                this.SetupVBO(gl, program, posName, edges);
      • The visible member variable allows us to turn display of the object on and off.
      • We will need to preserve the number of edges to draw for future use.
      • The function SetupVBO is somewhat complex and will be discussed next.
    3. SetupVBO
      • We will discuss this function in class in real detail.
      • I don't expect you to master it here
      • But I do expect you to look it over.
      •     SetupVBO(gl, program, posName, edges) {
                this.vbuf =  gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, this.vbuf);
                this.pos =  gl.getAttribLocation(program, posName);
                gl.vertexAttribPointer(this.pos, 3, gl.FLOAT, false, 0, 0);
      • Line 1 creates a buffer on the GPU.
        • This is a place to store information
      • Line 2 makes the buffer active
        • There is only one active buffer at a time.
        • The active buffer is where all action takes place.
      • Line 3 associates the data we are about to write with the variable in the program
        • If you look at the vertex shader, this is the attribute "vPosition"
        • this stands for vertex position.
        • which is what we are going to store in the buffer.
      • Line 4 tells the GPU that the thing we are sending over consists of three floating point numbers at each location.
        • This is equivalent to a struct with three floats.
      • Line 5 enables storage for the attribute we are creating
        • We are only sending vertexes
        • But we could send an array of colors for each vertex.
      • Line 6 transfers the data (stored in edges) to the GPU
    4. Build the member functions to work with visibility
      • If you don't understand these, I don't know what to do.
      •     Show() {
                this.visible = true;
            Hide() {
                this.visible = false;
            Visible() {
                return this.visible;
    5. Add a Display function
      • This takes the context we will be drawing in.
      •     Display(gl) {
                  if (this.visible) {
                      gl.bindBuffer(gl.ARRAY_BUFFER, this.vbuf);
                      gl.vertexAttribPointer(this.pos, 3, gl.FLOAT, false, 0, 0);
                      gl.drawArrays(gl.LINE_LOOP, 0, this.size);
        } // closing of class.  
      • Line 1 makes the buffer active again, We might have used another buffer between when we created this buffer and when we now.
      • Line 2 makes the attribute active.
      • Line 3 tells the GPU to draw the figure.
    6. Your final file should be close to Widget.js
  6. Finally, download the driver routines from the file called Program.js
    1. The first 28 lines of the file set up the vertexes for the two shapes we will draw.
    2. MakeItems builds the scene by creating instances of the Widget class with the different vertex sets created above.
    3. The other functions should be familiar, or easy to understand.


Please use relative references to your code in the html file. This will allow me to extract the file in my local environment.

Additional Requirements/Comments


Create a tar/zip file containing your HW5 directory.

Submit your tar/zip file as an attachment to the D2L folder for homeowrk 5.