Source

WebGL Beginner's Guide - Code / 1727_04 / ch4_ModelView_Translation.html

<html>

<head>
<title>WebGL Beginner's Guide - Chapter 4 - Model-View Matrix: Translation</title>
<meta http-equiv='content-type' content='text/html; charset=ISO-8859-1'>

<!-- CSS Styles //-->
<link href='css/styles.css'   type='text/css' rel='stylesheet'>
<link href='css/prettify_desert.css'  type='text/css' rel='stylesheet'/>
<link href='css/colorpicker.css'  type='text/css' rel='stylesheet'/>
<link href='css/smoothness/jquery-ui-1.8.13.custom.css' type='text/css' rel='stylesheet' />
<!-- GUI Libraries //-->
<script type='text/javascript' src='js/gui/jquery-1.5.1.min.js'></script>
<script type='text/javascript' src='js/gui/jquery-ui-1.8.13.custom.min.js'></script> 
<script type='text/javascript' src='js/gui/colorpicker.js'></script>
<script type='text/javascript' src='js/gui/prettify.js'></script>
<script type='text/javascript' src='js/gui/codeview.js'></script>
<!-- MATH Libraries //-->
<script type='text/javascript' src='js/math/gl-matrix-min.js'></script>
<!-- WEBGL Libraries //-->
<script type='text/javascript' src='js/webgl/Globals.js'></script>
<script type='text/javascript' src='js/webgl/Utils.js'></script>
<script type='text/javascript' src='js/webgl/Program.js'></script>
<script type='text/javascript' src='js/webgl/Scene.js'></script>
<script type='text/javascript' src='js/webgl/Axis.js'></script>
<script type='text/javascript' src='js/webgl/Floor.js'></script>
<script type='text/javascript' src='js/webgl/WebGLApp.js'></script>

<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec4 aVertexColor;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 uNMatrix;

uniform vec3 uLightPosition; 
uniform vec4 uLightAmbient;  
uniform vec4 uLightDiffuse;  
uniform vec4 uMaterialDiffuse; 
uniform bool uWireframe;
uniform bool uPerVertexColor;
varying vec4 vFinalColor;

void main(void) {
    if (uWireframe) {
        if (uPerVertexColor){
            vFinalColor = aVertexColor;
        }
        else{
            vFinalColor = uMaterialDiffuse;
        }
    }
    else{
        vec3 N = vec3(uNMatrix * vec4(aVertexNormal, 0.0));  // This is a vector w = 0;
        vec3 L = normalize(-uLightPosition);                 // Given a light position, use the inverse is the direction (to the center of the world)
        //L = vec3(uNMatrix*vec4(L,0.0));   // vector light direction

        float lambertTerm = dot(N,-L);
        if (lambertTerm <= 0.0) lambertTerm = 0.01;
        vec4 Ia = uLightAmbient;
        vec4 Id = uMaterialDiffuse * uLightDiffuse * lambertTerm;
        vFinalColor = Ia + Id;
        vFinalColor.a = 1.0; //alpha channel
    }
    
    //Transformed vertex position
    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition,1.0); // vertex w=1
}
</script>

<script id="shader-fs" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
#endif

varying vec4  vFinalColor;

void main(void)  {
 gl_FragColor = vFinalColor;
}
</script>

<script id='code-js' type="text/javascript">
/**
*   Defines the initial values for the transformation matrices
*/
function initTransforms(){
    //Initialize Model-View matrix
    mat4.identity(mvMatrix);
    mat4.translate(mvMatrix, home);
    displayMatrix(mvMatrix);
    
    //Initialize Camera matrix as the inverse of the Model-View Matrix
    mat4.identity(cMatrix);
    mat4.inverse(mvMatrix,cMatrix);
    
    //Initialize Perspective matrix
    mat4.identity(pMatrix);
    
    //Initialize Normal matrix
    mat4.identity(nMatrix);
    mat4.set(mvMatrix, nMatrix);
    mat4.inverse(nMatrix);
    mat4.transpose(nMatrix);

    coords = COORDS_WORLD;
}

/**
*   Updates the Model-View matrix if there is any translation or change in 
*   coordinate system (world->camera or camera->world). Updates the Normal matrix according to the translation.
*   Please notice that the normal matrix will ALWAYS operate in world coordinates.
*   Called once per rendering cycle.
*/
function updateTransforms(){
    
    mat4.perspective(30, c_width / c_height, 0.1, 1000.0, pMatrix);  // We can resize the screen at any point so the perspective matrix should be updated always.
    
    if (coords == COORDS_WORLD){
        mat4.identity(mvMatrix);
        mat4.translate(mvMatrix, position); 
    }
    else{
        mat4.identity(cMatrix);
        mat4.translate(cMatrix,position);
    }
 }


/**
* Maps the matrices to shader matrix uniforms
*
* Called once per rendering cycle. 
*/
function setMatrixUniforms(){

    if (coords == COORDS_WORLD){
            mat4.inverse(mvMatrix,cMatrix);     //Obtain Camera Matrix from Model-View Matrix
            displayMatrix(mvMatrix);
            gl.uniformMatrix4fv(prg.uMVMatrix, false, mvMatrix);
    }
    else{
         mat4.inverse(cMatrix, mvMatrix);      //Obtain Model-View matrix from Camera Matrix
         displayMatrix(cMatrix);
         
     }
     
     gl.uniformMatrix4fv(prg.uPMatrix, false, pMatrix);    //Maps the Perspective matrix to the uniform prg.uPMatrix
     gl.uniformMatrix4fv(prg.uMVMatrix, false, mvMatrix);  //Maps the Model-View matrix to the uniform prg.uMVMatrix
     mat4.transpose(cMatrix, nMatrix);                     //Calculates the Normal matrix 
     gl.uniformMatrix4fv(prg.uNMatrix, false, nMatrix);    //Maps the Normal matrix to the uniform prg.uNMatrix
}

/**
*  Sets up the canvas
*/
function configure(){
    gl.clearColor(0.3,0.3,0.3, 1.0);
    gl.clearDepth(100.0);
    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LEQUAL);
    initTransforms();
}

/**
* Creates an AJAX request to load the scene asynchronously
*/
function load(){
    Floor.build(60,2);
    Axis.build(60);
    
    Scene.addObject(Floor);
    Scene.addObject(Axis);
    Scene.loadObject('models/geometry/cone.json','cone');
}


/**
* Main rendering function. Called every 500ms according to WebGLStart function (see below)
*/
function draw() {
  gl.viewport(0, 0, c_width, c_height);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        
    try{
        
        updateTransforms();
        setMatrixUniforms();        
        
        
        for (var i = 0; i < Scene.objects.length; i++){
                       
            var object = Scene.objects[i];
            
            //Setting uniforms
            gl.uniform4fv(prg.uMaterialDiffuse, object.diffuse);
            gl.uniform1i(prg.uWireframe,object.wireframe);
            gl.uniform1i(prg.uPerVertexColor, object.perVertexColor);
            
            //Setting attributes
            gl.enableVertexAttribArray(prg.aVertexPosition);
            gl.disableVertexAttribArray(prg.aVertexNormal);
            gl.disableVertexAttribArray(prg.aVertexColor);
            
            gl.bindBuffer(gl.ARRAY_BUFFER, object.vbo);
            gl.vertexAttribPointer(prg.aVertexPosition, 3, gl.FLOAT, false, 0, 0);
            gl.enableVertexAttribArray(prg.aVertexPosition);
            
            
            
            if(!object.wireframe){
                gl.bindBuffer(gl.ARRAY_BUFFER, object.nbo);
                gl.vertexAttribPointer(prg.aVertexNormal, 3, gl.FLOAT, false, 0, 0);
                gl.enableVertexAttribArray(prg.aVertexNormal);
            }
            
            if (object.perVertexColor){
                gl.bindBuffer(gl.ARRAY_BUFFER, object.cbo);
                gl.vertexAttribPointer(prg.aVertexColor,4,gl.FLOAT, false, 0,0);
                gl.enableVertexAttribArray(prg.aVertexColor);
            }
            
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, object.ibo);
            
            if (object.wireframe){
                gl.drawElements(gl.LINES, object.indices.length, gl.UNSIGNED_SHORT,0);
            }
            else{
                gl.drawElements(gl.TRIANGLES, object.indices.length, gl.UNSIGNED_SHORT,0);
            }
            gl.bindBuffer(gl.ARRAY_BUFFER, null);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
            
        }
    }
    catch(err){
        alert(err);
        console.error(err.description);
    }
}



/**
* Entry point. This function is invoked when the page is loaded
*/
var app = null;
function runWebGLApp() {
    app = new WebGLApp("canvas-element-id")
    app.configureGLHook = configure;
    app.loadSceneHook   = load;
    app.drawSceneHook   = draw;
    app.run();
}
</script>
</head>

<body onLoad='runWebGLApp()'>
<div id='top'>
<h1>WebGL Beginner's Guide - Chapter 4</h1>
<h2>Model-View Matrix: Translation</h2>
<div id='logo-packt'><img src='packt.gif'/></div>
<p></p>
</div>

<div id='contents'>
<div id='canvasContainer'>
<canvas id='canvas-element-id' width='480' height='400'>
Your browser does not support the HTML5 canvas element.
</canvas>
</div>
</div>

<div id='bottom'>
<table>
<tr><td width='50%'>
<h3> World coordinates</h3>
<p>The model-view matrix transforms the world. Here we are just considering the <b>translation component</b> to see how the world moves.</p>
For example, increase X to 10: you will se how the world is translated 10 units to the right (which is the same as requestUpdate the camera 10 units to the left!)</p>

<h3> Camera coordinates</h3>
<p>When you click on <b>Camera</b> the <i>inverse of the model-view matrix</i> is applied. Then if you increase X to 10 you will see how 
the camera is translated 10 units to the right (which is equivalent to move the world 10 units to the left!)</p>
</td>
<td style='vertical-align:top'> 
<table style='padding=4px'>
<td>X:</td><td id='slider-x-value' width='30px'>0.0</td><td width='150px'><div id='slider-x'/></td>
<td>
    <div id='opt-coords'>
        <input type='radio' id='opt-world' name='coords' checked='checked'/><label for='opt-world'>World</label>
        <input type='radio' id='opt-camera' name='coords'/><label for='opt-camera'>Camera</label>
    </div>
</td>
</tr>
<tr>
<td>Y:</td><td id='slider-y-value'  width='30px'>-2.0</td><td width='150px'><div id='slider-y'/></td>
</tr>
<tr>
<td>Z:</td> <td id='slider-z-value'  width='30px'>-30.0</td><td width='150px'><div id='slider-z'/></td>
</tr>
</table>
</td>
<td align='center' style='vertical-align:top'>
<table id='tbl-matrix'>
<tr><td id='m0'></td><td id='m4'></td><td id='m8'></td><td id='m12' style='font-weight:bold'></td></tr>
<tr><td id='m1'></td><td id='m5'></td><td id='m9'></td><td id='m13' style='font-weight:bold'></td></tr>
<tr><td id='m2'></td><td id='m6'></td><td id='m10'></td><td id='m14' style='font-weight:bold'></td></tr>
<tr><td id='m3'></td><td id='m7'></td><td id='m11'></td><td id='m15'></td></tr>
</table>
</td>
</tr>
</table>
</div>
<script>cview.run(cview.MODE_VIEW);</script>
<script> 
$('#slider-x').slider({value:0.0, min:-50.0, max:50.0, step:0.5, slide:function(){updatePosition('#slider-x');}});
$('#slider-y').slider({value:-2.0, min:-50.0, max:50.0, step:0.5, slide:function(){updatePosition('#slider-y');}});
$('#slider-z').slider({value:-30.0, min:-50.0, max:50.0, step:0.5, slide:function(){updatePosition('#slider-z');}});


function updatePosition(selector){
    var pos = $(selector).slider("value");
    $(selector+'-value').html(pos);
    
    if(selector == '#slider-x'){
        position[0] = pos;
    }
    else if(selector == '#slider-y'){
        position[1] = pos;
    }
    else if (selector == '#slider-z'){
        position[2] = pos;
    }
    app.refresh();
}

function updateSliders(){
    $('#slider-x').slider("value",position[0]);
    $('#slider-y').slider("value",position[1]);
    $('#slider-z').slider("value",position[2]);
    $('#slider-x-value').html(position[0]);
    $('#slider-y-value').html(position[1]);
    $('#slider-z-value').html(position[2]); 
}

$('#opt-coords').buttonset();

$('#opt-world').click(
    function(){
        vec3.set(home,position);
        updateSliders();
        coords = COORDS_WORLD;
        app.refresh();
});

$('#opt-camera').click(
    function(){
        vec3.set(home,position);
        vec3.negate(position);
        updateSliders();
        coords = COORDS_CAMERA;
        app.refresh();
});

function displayMatrix(m){
    var selector = '';
    for(var i=0;i<16;i++){
        selector = '#m'+i;
        $(selector).html(m[i].toFixed(1));
    }
}

</script>
</body>
</html>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.