WebGL交互式立方體

在本章中,我們將舉一個例子來演示如何繪製3D魔方,可以用鼠標控制旋轉。

示例 - 繪製一個互動立方體


下面的程序展示瞭如何使用鼠標控制旋轉的立方體 -

  <script>

     /\*============= Creating a canvas ======================\*/
     var canvas = document.getElementById('my\_Canvas');
     gl = canvas.getContext('experimental-webgl');

     /\*========== Defining and storing the geometry ==========\*/

     var vertices = \[
        -1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1,
        -1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1,
        -1,-1,-1, -1, 1,-1, -1, 1, 1, -1,-1, 1,
        1,-1,-1, 1, 1,-1, 1, 1, 1, 1,-1, 1,
        -1,-1,-1, -1,-1, 1, 1,-1, 1, 1,-1,-1,
        -1, 1,-1, -1, 1, 1, 1, 1, 1, 1, 1,-1, 
     \];

     var colors = \[
        5,3,7, 5,3,7, 5,3,7, 5,3,7,
        1,1,3, 1,1,3, 1,1,3, 1,1,3,
        0,0,1, 0,0,1, 0,0,1, 0,0,1,
        1,0,0, 1,0,0, 1,0,0, 1,0,0,
        1,1,0, 1,1,0, 1,1,0, 1,1,0,
        0,1,0, 0,1,0, 0,1,0, 0,1,0 
     \];

     var indices = \[
        0,1,2, 0,2,3, 4,5,6, 4,6,7,
        8,9,10, 8,10,11, 12,13,14, 12,14,15,
        16,17,18, 16,18,19, 20,21,22, 20,22,23 
     \];

     // Create and store data into vertex buffer
     var vertex\_buffer = gl.createBuffer ();
     gl.bindBuffer(gl.ARRAY\_BUFFER, vertex\_buffer);
     gl.bufferData(gl.ARRAY\_BUFFER, new Float32Array(vertices), gl.STATIC\_DRAW);

     // Create and store data into color buffer
     var color\_buffer = gl.createBuffer ();
     gl.bindBuffer(gl.ARRAY\_BUFFER, color\_buffer);
     gl.bufferData(gl.ARRAY\_BUFFER, new Float32Array(colors), gl.STATIC\_DRAW);

     // Create and store data into index buffer
     var index\_buffer = gl.createBuffer ();
     gl.bindBuffer(gl.ELEMENT\_ARRAY\_BUFFER, index\_buffer);
     gl.bufferData(gl.ELEMENT\_ARRAY\_BUFFER, new Uint16Array(indices), gl.STATIC\_DRAW);

     /\*=================== SHADERS =================== \*/

     var vertCode = 'attribute vec3 position;'+
        'uniform mat4 Pmatrix;'+
        'uniform mat4 Vmatrix;'+
        'uniform mat4 Mmatrix;'+
        'attribute vec3 color;'+//the color of the point
        'varying vec3 vColor;'+
        'void main(void) { '+//pre-built function
           'gl\_Position = Pmatrix\*Vmatrix\*Mmatrix\*vec4(position, 1.);'+
           'vColor = color;'+
        '}';

     var fragCode = 'precision mediump float;'+
        'varying vec3 vColor;'+
        'void main(void) {'+
           'gl\_FragColor = vec4(vColor, 1.);'+
        '}';

     var vertShader = gl.createShader(gl.VERTEX\_SHADER);
     gl.shaderSource(vertShader, vertCode);
     gl.compileShader(vertShader);

     var fragShader = gl.createShader(gl.FRAGMENT\_SHADER);
     gl.shaderSource(fragShader, fragCode);
     gl.compileShader(fragShader);

     var shaderprogram = gl.createProgram();
     gl.attachShader(shaderprogram, vertShader);
     gl.attachShader(shaderprogram, fragShader);
     gl.linkProgram(shaderprogram);

     /\*======== Associating attributes to vertex shader =====\*/
     var \_Pmatrix = gl.getUniformLocation(shaderprogram, "Pmatrix");
     var \_Vmatrix = gl.getUniformLocation(shaderprogram, "Vmatrix");
     var \_Mmatrix = gl.getUniformLocation(shaderprogram, "Mmatrix");

     gl.bindBuffer(gl.ARRAY\_BUFFER, vertex\_buffer);
     var \_position = gl.getAttribLocation(shaderprogram, "position");
     gl.vertexAttribPointer(\_position, 3, gl.FLOAT, false,0,0);
     gl.enableVertexAttribArray(\_position);

     gl.bindBuffer(gl.ARRAY\_BUFFER, color\_buffer);
     var \_color = gl.getAttribLocation(shaderprogram, "color");
     gl.vertexAttribPointer(\_color, 3, gl.FLOAT, false,0,0) ;
     gl.enableVertexAttribArray(\_color);
     gl.useProgram(shaderprogram);

     /\*==================== MATRIX ====================== \*/

     function get\_projection(angle, a, zMin, zMax) {
        var ang = Math.tan((angle\*.5)\*Math.PI/180);//angle\*.5
        return \[
           0.5/ang, 0 , 0, 0,
           0, 0.5\*a/ang, 0, 0,
           0, 0, -(zMax+zMin)/(zMax-zMin), -1,
           0, 0, (-2\*zMax\*zMin)/(zMax-zMin), 0 
           \];
     }

     var proj\_matrix = get\_projection(40, canvas.width/canvas.height, 1, 100);
     var mo\_matrix = \[ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 \];
     var view\_matrix = \[ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 \];

     view\_matrix\[14\] = view\_matrix\[14\]-6;

     /\*================= Mouse events ======================\*/

     var AMORTIZATION = 0.95;
     var drag = false;
     var old\_x, old\_y;
     var dX = 0, dY = 0;

     var mouseDown = function(e) {
        drag = true;
        old\_x = e.pageX, old\_y = e.pageY;
        e.preventDefault();
        return false;
     };

     var mouseUp = function(e){
        drag = false;
     };

     var mouseMove = function(e) {
        if (!drag) return false;
        dX = (e.pageX-old\_x)\*2\*Math.PI/canvas.width,
        dY = (e.pageY-old\_y)\*2\*Math.PI/canvas.height;
        THETA+= dX;
        PHI+=dY;
        old\_x = e.pageX, old\_y = e.pageY;
        e.preventDefault();
     };

     canvas.addEventListener("mousedown", mouseDown, false);
     canvas.addEventListener("mouseup", mouseUp, false);
     canvas.addEventListener("mouseout", mouseUp, false);
     canvas.addEventListener("mousemove", mouseMove, false);

     /\*=========================rotation================\*/

     function rotateX(m, angle) {
        var c = Math.cos(angle);
        var s = Math.sin(angle);
        var mv1 = m\[1\], mv5 = m\[5\], mv9 = m\[9\];

        m\[1\] = m\[1\]\*c-m\[2\]\*s;
        m\[5\] = m\[5\]\*c-m\[6\]\*s;
        m\[9\] = m\[9\]\*c-m\[10\]\*s;

        m\[2\] = m\[2\]\*c+mv1\*s;
        m\[6\] = m\[6\]\*c+mv5\*s;
        m\[10\] = m\[10\]\*c+mv9\*s;
     }

     function rotateY(m, angle) {
        var c = Math.cos(angle);
        var s = Math.sin(angle);
        var mv0 = m\[0\], mv4 = m\[4\], mv8 = m\[8\];

        m\[0\] = c\*m\[0\]+s\*m\[2\];
        m\[4\] = c\*m\[4\]+s\*m\[6\];
        m\[8\] = c\*m\[8\]+s\*m\[10\];

        m\[2\] = c\*m\[2\]-s\*mv0;
        m\[6\] = c\*m\[6\]-s\*mv4;
        m\[10\] = c\*m\[10\]-s\*mv8;
     }

     /\*=================== Drawing =================== \*/

     var THETA = 0,
     PHI = 0;
     var time\_old = 0;

     var animate = function(time) {
        var dt = time-time\_old;

        if (!drag) {
           dX \*= AMORTIZATION, dY\*=AMORTIZATION;
           THETA+=dX, PHI+=dY;
        }

        //set model matrix to I4

        mo\_matrix\[0\] = 1, mo\_matrix\[1\] = 0, mo\_matrix\[2\] = 0,
        mo\_matrix\[3\] = 0,

        mo\_matrix\[4\] = 0, mo\_matrix\[5\] = 1, mo\_matrix\[6\] = 0,
        mo\_matrix\[7\] = 0,

        mo\_matrix\[8\] = 0, mo\_matrix\[9\] = 0, mo\_matrix\[10\] = 1,
        mo\_matrix\[11\] = 0,

        mo\_matrix\[12\] = 0, mo\_matrix\[13\] = 0, mo\_matrix\[14\] = 0,
        mo\_matrix\[15\] = 1;

        rotateY(mo\_matrix, THETA);
        rotateX(mo\_matrix, PHI);

        time\_old = time; 
        gl.enable(gl.DEPTH\_TEST);

        // gl.depthFunc(gl.LEQUAL);

        gl.clearColor(0.5, 0.5, 0.5, 0.9);
        gl.clearDepth(1.0);
        gl.viewport(0.0, 0.0, canvas.width, canvas.height);
        gl.clear(gl.COLOR\_BUFFER\_BIT | gl.DEPTH\_BUFFER\_BIT);

        gl.uniformMatrix4fv(\_Pmatrix, false, proj\_matrix);
        gl.uniformMatrix4fv(\_Vmatrix, false, view\_matrix);
        gl.uniformMatrix4fv(\_Mmatrix, false, mo\_matrix);

        gl.bindBuffer(gl.ELEMENT\_ARRAY\_BUFFER, index\_buffer);
        gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED\_SHORT, 0);

        window.requestAnimationFrame(animate);
     }

     animate(0);

  </script>

這將產生以下結果(運行上面的代碼,可用鼠標點或拖動中間的立方體,看看其效果) -
WebGL交互式立方體