WebGL – Interleaved Vertex Buffer

Click To Show Running WebGL

fullscreenquad

Updated data and calls to vertexAttribPointer(). The highlighted lines have the relevant changes.


 var squareVertexBuffer;

    function initBuffers() {
      //Create Square Position Buffer
        squareVertexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexBuffer);
        var vertices = [
             1.0,  1.0,  0.0, 1.0, 1.0, //x, y, z, u, v
            -1.0,  1.0,  0.0, 0.0, 1.0,
             1.0, -1.0,  0.0, 1.0, 0.0,
            -1.0, -1.0,  0.0, 0.0, 0.0
        ];
        gl.bufferData(
                     gl.ARRAY_BUFFER, 
                     new Float32Array(vertices), 
                     gl.STATIC_DRAW
                     );
        squareVertexBuffer.stride = 5*Float32Array.BYTES_PER_ELEMENT;
        squareVertexBuffer.positionElementCount = 3;
        squareVertexBuffer.positionOffset = 0;
        squareVertexBuffer.uvElementCount = 2;
        squareVertexBuffer.uvOffset = 3*Float32Array.BYTES_PER_ELEMENT;
        squareVertexBuffer.numItems = 4;

    }

    function drawScene() {
        gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        pMatrix = identity();
        mvMatrix = identity();

        pMatrix = ortho(-1, 1, -1, 1, 0.01, 10);
        mvMatrix = makeTranslation(0.0, 0.0, -0.01);

        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexBuffer);
        gl.vertexAttribPointer(
                              shaderProgram.vertexPositionAttribute,
                              squareVertexBuffer.positionElementCount, 
                              gl.FLOAT, 
                              false, 
                              squareVertexBuffer.stride, 
                              squareVertexBuffer.positionOffset
                              );
        gl.vertexAttribPointer(
                              shaderProgram.vertexUVAttribute, 
                              squareVertexBuffer.uvElementCount, 
                              gl.FLOAT, 
                              false, 
                              squareVertexBuffer.stride, 
                              squareVertexBuffer.uvOffset 
                              );
        setUniforms();
        gl.drawArrays(
                      gl.TRIANGLE_STRIP, 
                      0, 
                      squareVertexBuffer.numItems
                      );
    }

WebGL – Mouse Events

I extended the Full Screen Quad example to handle mouse location with a little visual feedback when you click and drag.

Click To Show Running WebGL

mouseevents

    precision mediump float;
    uniform vec2 uViewportSize;
    uniform vec2 uMouseLocation;
    uniform float uMouseRadius;
    varying vec2 vUV;

    void main(void) {
      vec4 orig_color = vec4(vUV.x, vUV.y, 1.0, 1.0);
      vec4 mouse_color = vec4(1, 0, 0, 1.0);

      float dist = length(
                          vec2(gl_FragCoord) - 
                          vec2(uMouseLocation.x - uMouseRadius,
                               uMouseLocation.y + uMouseRadius
                              )
                         );
      float t =  clamp(dist/uMouseRadius, 0.0, 1.0);

      gl_FragColor = mix(mouse_color, orig_color, t);
    }

   ...
    function initShaders() {
       ...
        shaderProgram.MouseLocationUniform=gl.getUniformLocation(shaderProgram,
                                                             "uMouseLocation");
        shaderProgram.MouseRadiusUniform=gl.getUniformLocation(shaderProgram,
                                                             "uMouseRadius");
    }


   
    function setUniforms() {
        ...
        gl.uniform2f(
                    shaderProgram.MouseLocationUniform, 
                    mouse[0], 
                    mouse[1]
                    );
        gl.uniform1f(
                    shaderProgram.MouseRadiusUniform, 
                    mouseRadius 
                    );

    }



    

    var mouse = [250,250];
    var mouseRadius = 10;
    var currentAngle = [0,0]; // [x-axis, y-axis] degrees

    function webGLStart() {
        ...
        initEventHandlers(canvas, mouse, currentAngle);

        ...
        var tick = function() {
          drawScene();
          requestAnimationFrame(tick,canvas);
        };
        tick();
    }

    function initEventHandlers(canvas, mousePosition, currentAngle)
    {
      var lastX = -1;
      var lastY = -1;
      var dragging = false;

      canvas.onmousedown = function(ev) {  //Mouse is pressed
        var x = ev.clientX;
        var y = ev.clientY;

        var rect = ev.target.getBoundingClientRect();
        if(rect.left <= x 
            &amp;&amp; x <= rect.right 
            &amp;&amp; rect.top <= y 
            &amp;&amp; y <= rect.bottom
          ) {
          lastX = x; 
          lastY = y;
          mousePosition[0] = x;
          mousePosition[1] = canvas.height - y;
          dragging = true;

        }
      };

      canvas.onmouseup = function(ev){ //Mouse is released
        dragging = false;
      }

      canvas.onmousemove = function(ev) { //Mouse is moved
        var x = ev.clientX;
        var y = ev.clientY;
        if(dragging) {
          //put some kind of dragging logic in here
          //Here is a roation example
          var factor = 100/canvas.height;
          var dx = factor * (x - lastX);
          var dy = factor * (y - lastY);
          //Limit x-axis roation angle to -90 to 90 degrees
          currentAngle[0] = Math.max(Math.min(currentAngle[0] + dy, 90), -90);
          currentAngle[1] = currentAngle[1] + dx;

          mousePosition[0] = x;
          mousePosition[1] = canvas.height - y;

        }
        lastX = x;
        lastY = y;

      }
    }
    webGLStart();

WebGL – Fullscreen Quad

Click To Show Running WebGL

fullscreenquad

Fragment Shader:

precision mediump float;
    uniform vec2 uViewportSize;
    varying vec2 vUV;

    void main(void) {
      gl_FragColor = vec4(vUV.x, vUV.y, 1.0, 1.0);

      /*
      //gl_FragCoord is a way of getting the pixel location

        gl_FragColor = vec4(
                            gl_FragCoord.x/uViewportSize.x, 
                            gl_FragCoord.y/uViewportSize.y, 
                            1.0, 
                            1.0
                           );
     */
    }

Vertex Shader:

 //Data from bufferData
    attribute vec3 aVertexPosition;
    attribute vec2 aVertexUV;

    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;
    uniform vec2 uViewportSize;

    //These will be passed to the fragment shader and will be interpreted per
    //pixel
    varying vec2 vUV;

    void main(void) {
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
        //The line below will ignore any translation of perspective matrix
        //gl_Position =  vec4(aVertexPosition, 1.0);
        vUV = aVertexUV;
    }

Program Source:

    var gl;
    function initGL(canvas) {
        try {
            //gl = canvas.getContext("webgl");
            gl = canvas.getContext("experimental-webgl");
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
        } catch (e) {
        }
        if (!gl) {
            alert("Could not initialise WebGL, sorry :-(");
        }
    }


    function getShader(gl, id) {
        var shaderScript = document.getElementById(id);
        if (!shaderScript) {
            return null;
        }

        var str = "";
        var k = shaderScript.firstChild;
        while (k) {
            if (k.nodeType == 3) {
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
            shader = gl.createShader(gl.FRAGMENT_SHADER);
        } else if (shaderScript.type == "x-shader/x-vertex") {
            shader = gl.createShader(gl.VERTEX_SHADER);
        } else {
            return null;
        }

        gl.shaderSource(shader, str);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }

        return shader;
    }


    var shaderProgram;

    function initShaders() {
        var fragmentShader = getShader(gl, "shader-fs");
        var vertexShader = getShader(gl, "shader-vs");

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

        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }

        gl.useProgram(shaderProgram);

        shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
        gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

        shaderProgram.vertexUVAttribute = gl.getAttribLocation(shaderProgram, "aVertexUV");
        gl.enableVertexAttribArray(shaderProgram.vertexUVAttribute);

        shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
        shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
        shaderProgram.ViewportSizeUniform = gl.getUniformLocation(shaderProgram, "uViewportSize");
    }


    var mvMatrix;
    var pMatrix;

    function setUniforms() {
        gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
        gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
        gl.uniform2f(
                    shaderProgram.ViewportSizeUniform, 
                    gl.viewportWidth, 
                    gl.viewportHeight
                    );
    }



    var squareVertexPositionBuffer;
    var squareVertexUVBuffer;

    function initBuffers() {
      //Create Square Position Buffer
        squareVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
        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
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        squareVertexPositionBuffer.itemSize = 3;
        squareVertexPositionBuffer.numItems = 4;

      //Create Square UV Buffer
        squareVertexUVBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexUVBuffer);
        var uvs = [
             1.0, 1.0,
             0.0, 1.0,
             1.0, 0.0,
             0.0, 0.0
        ];
        
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(uvs), gl.STATIC_DRAW);
        squareVertexUVBuffer.itemSize = 2;
        squareVertexUVBuffer.numItems = 4;
    }


    function drawScene() {
        gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);


        pMatrix = identity();
        mvMatrix = identity();

        //pMatrix = ortho(-1, 1, -1, 1, 0.01, 10);
        //pMatrix = makePerspective(45 * Math.PI/180.0, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
        //mvMatrix = makeTranslation(0.0, 0.0, -0.01);

        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexUVBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexUVAttribute, squareVertexUVBuffer.itemSize, gl.FLOAT, false, 0, 0);
        setUniforms();
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
    }



    function webGLStart() {
        var canvas = document.getElementById("webgl-canvas");
        initGL(canvas);
        initShaders();
        initBuffers();

        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST);

        drawScene();
    }
    
    webGLStart();

How to enable WebGL on Safari

It seems that on OS X 10.7+ if you go to the Preferences->Advanced and check the box “Show Develop menu in menu bar.” You can then go to the Develop menu and choose “Enable WebGL.”

Advanced Preferences

Advanced Preferences

Viewing disassembly on OSX – Using objdump.

On linux you can use something called objdump. On OSX you need to install binutils. To do that use macports:

sudo port install binutils

For my machine it got installed in:

/opt/local/x86_64-apple-darwin10.8.0/bin/objdump

You can then call it.

/opt/local/x86_64-apple-darwin10.8.0/bin/objdump -M intel -D a.out | grep -A20 main.:

-M puts the disassembly into intel format
-D specifies the file

the pipe to grep prevents looking at the whole listing and the -A20 shows 20 lines following the matched line which is going to be main().

Here is what it outputs:

0000000100000f10 <_main>:
   100000f10:	55                   	push   rbp
   100000f11:	48 89 e5             	mov    rbp,rsp
   100000f14:	48 8d 3d 13 00 00 00 	lea    rdi,[rip+0x13]        # 100000f2e <_puts$stub+0x6>
   100000f1b:	e8 08 00 00 00       	call   100000f28 <_puts$stub>
   100000f20:	c9                   	leave  
   100000f21:	c3                   	ret    

Disassembly of section __TEXT.__symbol_stub1:

0000000100000f22 <_exit$stub>:
   100000f22:	ff 25 10 01 00 00    	jmp    QWORD PTR [rip+0x110]        # 100001038 <_exit$stub>

0000000100000f28 <_puts$stub>:
   100000f28:	ff 25 12 01 00 00    	jmp    QWORD PTR [rip+0x112]        # 100001040 <_puts$stub>

Disassembly of section .cstring:

0000000100000f2e <.cstring>:
   100000f2e:	48                   	rex.W
   100000f2f:	65                   	gs