Commits

Diego Cantor committed b123d1b

Code for Chapter 9 ready!

Comments (0)

Files changed (498)

1727_09/ch9_Car_Showroom.html

 <link href='css/styles.css'   type='text/css' rel='stylesheet'>
 <link href='css/cars.css' type='text/css' rel='stylesheet' />
 <link href='css/smoothness/jquery-ui-1.8.13.custom.css' type='text/css' rel='stylesheet' />
+<link href='css/colorpicker.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>
 <!-- MATH Libraries //-->
 <script type='text/javascript' src='js/math/glMatrix-0.9.5.min.js'></script>
 <!-- WEBGL Libraries //-->
 
 <script id="shader-vs" type="x-shader/x-vertex">
 
+const int NUM_LIGHTS = 4;
+
 attribute vec3 aVertexPosition;
 attribute vec3 aVertexNormal;
 attribute vec4 aVertexColor;
 uniform mat4 uPMatrix;
 uniform mat4 uNMatrix;
 
-uniform vec3 uLightPosition;
+uniform bool uTranslateLights;
+uniform vec3 uLightPosition[NUM_LIGHTS];
 
 varying vec3 vNormal;
-varying vec3 vLightRay;
-varying vec3 vEye;
+varying vec3 vLightRay[NUM_LIGHTS];
+varying vec3 vEye[NUM_LIGHTS];
 
 void main(void) {
 
      vec4 c = aVertexColor;
-     
      vec4 vertex = uMVMatrix * vec4(aVertexPosition, 1.0);
-     
      vNormal = vec3(uNMatrix * vec4(aVertexNormal, 1.0));
+     vec4 lightPosition = vec4(0.0);
      
-     vec4 lightPosition =  vec4(uLightPosition,1.0);//uMVMatrix * vec4(uLightPosition, 1.0);
-	 
-     vLightRay = vertex.xyz - lightPosition.xyz;
-     vEye = -vec3(vertex.xyz);
-     
-     gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
+     if (uTranslateLights){
+         for(int i=0; i < NUM_LIGHTS; i++){
+           lightPosition =   uMVMatrix * vec4(uLightPosition[i], 1.0);
+           vLightRay[i] = vertex.xyz - lightPosition.xyz;
+           vEye[i] = -vec3(vertex.xyz);
+         }
+     }    
+     else {
+        for(int i=0; i < NUM_LIGHTS; i++){
+          lightPosition = vec4(uLightPosition[i], 1.0);
+          vLightRay[i] = vertex.xyz - lightPosition.xyz;
+          vEye[i] = -vec3(vertex.xyz);
+        }
+    }
+    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
 }	
 </script>
 
 #ifdef GL_ES
 precision highp float;
 #endif
-//const int NUM_LIGHTS = 3;
 
-uniform vec4 uLightAmbient;
-uniform vec4 uLightDiffuse;
 
-uniform vec4 uMaterialAmbient;
-uniform vec4 uMaterialDiffuse;
-uniform vec4 uMaterialSpecular;
+//Light uniforms
+const int NUM_LIGHTS = 4;
+uniform vec3  uLa[NUM_LIGHTS];   //ambient
+uniform vec3  uLd[NUM_LIGHTS];   //diffuse
+uniform vec3  uLs[NUM_LIGHTS];   //specular
+uniform vec3 uLightPosition[NUM_LIGHTS];
 
+//Material uniforms
+uniform vec3  uKa;   //ambient
+uniform vec3  uKd;   //diffuse
+uniform vec3  uKs;   //specular
+uniform float uNs;   //specular coefficient
+uniform float d;     //Opacity
+uniform int   illum; //Illumination mode
+
+
+uniform bool  uWireframe;
 
-uniform bool uWireframe;
 
 varying vec3 vNormal;
-varying vec3 vLightRay;
-varying vec3 vEye;
+varying vec3 vLightRay[NUM_LIGHTS];
+varying vec3 vEye[NUM_LIGHTS];
+
+float calculateAttenuation(in vec3 ray){
+    float dist = length(ray);
+    return (1.0 / (0.1 * dist));
+}
 
 void main(void) {
-    if (uWireframe){
-        gl_FragColor = uMaterialDiffuse;
+    if (uWireframe || illum == 0){
+        gl_FragColor = vec4(uKd,d);
+        return;
     }
-    else{
-
-		vec4 COLOR = uLightAmbient * uMaterialAmbient;
-        
-        vec3 L = normalize(vLightRay);	
-        vec3 N = normalize(vNormal);	
-		float lambertTerm = dot(N, -L);
-        if (lambertTerm > 0.2){
-            COLOR += uLightDiffuse * uMaterialDiffuse *lambertTerm;
-            
-            vec3 E = normalize(vEye);
-            vec3 R = reflect(L, N);
-            float specular = pow( max(dot(R, E), 0.0), 100.0);
-            
-            COLOR += uMaterialSpecular * specular;
+    
+   vec3 COLOR = vec3(0.0,0.0,0.0);
+   vec3 N =  normalize(vNormal);
+   vec3 L =  vec3(0.0,0.0,0.0);
+   vec3 E =  vec3(0.0,0.0,0.0);
+   vec3 R =  vec3(0.0,0.0,0.0);
+   vec3 deltaRay = vec3(0.0);
+   const int  lsize = 2;
+   const float step = 0.25;
+   const float inv_total = 1.0/((float(lsize*lsize) + 1.0)*(float(lsize*lsize) + 1.0));  //how many deltaRays
+   
+   float dx = 0.0;
+   float dz = 0.0;
+   float LT = 0.0;
+   
+   if (illum == 1){
+        for(int i = 0; i < NUM_LIGHTS; i++){	
+            L = normalize(vLightRay[i]);	
+            N = normalize(vNormal);	
+            COLOR += (uLa[i] * uKa) + (uLd[i] * uKd * clamp(dot(N, -L),0.0,1.0));
+        }   
+        gl_FragColor =  vec4(COLOR,d);
+        return;
+   }
+   
+   if (illum == 2){
+        for(int i = 0; i < NUM_LIGHTS; i++){
+
+            E = normalize(vEye[i]);
+            L = normalize(vLightRay[i]);
+            R = reflect(L, N);
+            COLOR += (uLa[i] * uKa);
+            COLOR += (uLd[i] * uKd * clamp(dot(N,-L),0.0,1.0));// * calculateAttenuation(vLightRay[i]));
+            COLOR += (uLs[i] * uKs * pow( max(dot(R, E), 0.0), uNs) * 4.0);
         }
-        
-        gl_FragColor =  COLOR;
-        gl_FragColor.a = 1.0;
-    }	
+        gl_FragColor =  vec4(COLOR,d);
+        return;
+   }
 }
 </script>
 
 <script type="text/javascript">
 
 var camera = null,
-interactor = null,
-transforms = null;
+light1 = null,
+light2 = null,
+light3 = null,
+light4 = null,
+transforms = null,
+
+cameraHome = [0,0,7],
+cameraAzimuth = 24,
+cameraElevation = -11,
+carSurface = undefined,
+floorVisible = false,
+translateLights = false;
+
+
 
 function configure(){
-    gl.clearColor(0.3,0.3,0.3, 1.0);
+    gl.clearColor(0.2,0.2,0.2, 1.0);
     gl.clearDepth(100.0);
     
     gl.enable(gl.DEPTH_TEST);
+    //gl.enable(gl.CULL_FACE);
     gl.depthFunc(gl.LEQUAL);
-    
-	gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);
-
-    
-    
+    gl.blendEquation(gl.FUNC_SUBTRACT);
+	//gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);
+    //gl.blendFunc(gl.SRC_ALPHA_SATURATE, gl.ONE);
+  
     //Creates and sets up the camera location
     camera = new Camera(CAMERA_ORBITING_TYPE);
-    camera.goHome([0,1,7]);
+    camera.goHome(cameraHome);
     camera.setFocus([0.0,0.0,0.0]);
-	camera.setAzimuth(-45);
-	camera.setElevation(-10);
+	camera.setAzimuth(cameraAzimuth);
+	camera.setElevation(cameraElevation);
     camera.hookRenderer = render;
     
     //Creates and sets up the mouse and keyboard interactor
-    interactor = new CameraInteractor(camera, document.getElementById('the-canvas'));
+    var interactor = new CameraInteractor(camera, document.getElementById('the-canvas'));
     
     //Scene Transforms
     transforms = new SceneTransforms(camera);
    
     //init transforms
     transforms.init();
+
+    light1 = new Light('far-left');
+	light1.setPosition([-25,25,-25]);
+	light1.setDiffuse([0.4,0.4,0.4]);
+    light1.setAmbient([0.0,0.0,0.0]);
+    light1.setSpecular([0.8,0.8,0.8]);
 	
+	light2 = new Light('far-right');
+	light2.setPosition([25,25,-25]);
+	light2.setDiffuse([0.4,0.4,0.4]);
+    light2.setAmbient([0.0,0.0,0.0]);
+    light2.setSpecular([0.8,0.8,0.8]);
 	
-	
-
-	//init Program
+	light3 = new Light('near-left');
+	light3.setPosition([-25,25,25]);
+	light3.setDiffuse([0.4,0.4,0.4]);
+    light3.setAmbient([0.0,0.0,0.0]);
+    light3.setSpecular([0.8,0.8,0.8]);
+    
+    light4 = new Light('near-right');
+    light4.setPosition([25,25,25]);
+    light4.setDiffuse([0.4,0.4,0.4]);
+    light4.setAmbient([0.0,0.0,0.0]);
+    light4.setSpecular([0.8,0.8,0.8]);
+    
+    
+	Lights.add(light1);
+	Lights.add(light2);
+    Lights.add(light3);	
+	Lights.add(light4);	
+    
+    //init Program
 	var attributeList = ["aVertexPosition",
 					"aVertexNormal",
 					"aVertexColor"];
 					"uMVMatrix", 
 					"uNMatrix",
                     "uLightPosition",
-                    "uLightAmbient",
-                    "uLightDiffuse",
                     "uWireframe",
-                    "uMaterialAmbient",
-                    "uMaterialDiffuse",
-                    "uMaterialSpecular"
+                    "uLa",
+                    "uLd",
+                    "uLs",
+                    "uKa",
+                    "uKd",
+                    "uKs",
+                    "uNs",
+                    "d",
+                    "illum",
+                    "uTranslateLights"
 					];
-				
+					
+	Program.load(attributeList, uniformList);
 	
 	
-	Program.load(attributeList, uniformList);	
-    gl.uniform3fv(Program.uLightPosition, [0,15,0]);
-    gl.uniform4fv(Program.uLightAmbient ,  [0.05,0.05,0.05,1.0]);
-    gl.uniform4fv(Program.uLightDiffuse, [0.5,0.5,0.5,1.0]);
+    gl.uniform3fv(Program.uLightPosition, Lights.getArray('position'));
+    gl.uniform3fv(Program.uLa ,     Lights.getArray('ambient'));
+    gl.uniform3fv(Program.uLd,      Lights.getArray('diffuse'));
+    gl.uniform3fv(Program.uLs,      Lights.getArray('specular'));
     
-    gl.uniform4fv(Program.uMaterialAmbient ,  [1.0,1.0,1.0,1.0]);
-    gl.uniform4fv(Program.uMaterialSpecular ,  [0.5,0.5,0.5,1.0]);
+    gl.uniform3fv(Program.uKa ,     [1.0,1.0,1.0]);
+    gl.uniform3fv(Program.uKd ,     [1.0,1.0,1.0]);
+    gl.uniform3fv(Program.uKs ,     [1.0,1.0,1.0]);
     
+    gl.uniform1f(Program.uNs, 1.0);
+    gl.uniform1f(Program.uNi, 1.0);
+    gl.uniform1i(Program.uTranslateLights, translateLights);
+
+}
+
+function loadBMW(){
+    Scene.objects = [];
+    Scene.addObject(Floor);
+    for(var i = 1; i <= 24; i+=1){
+        //if (i == 5) continue;
+        Scene.loadObject('models/cars/bmw/part'+i+'.json');
+
+    }
+}
+
+function loadMustang(){
+    Scene.objects = [];
+    Scene.addObject(Floor);
+    Scene.loadObject('models/cars/bmw/part5.json','world');
+    for(var i = 1; i <= 103; i+=1){
+        Scene.loadObject('models/cars/mustang/part'+i+'.json');
+    }
+   
+}
+
+
+
+
+function loadAudi(){
+    Scene.objects = [];
+    Scene.addObject(Floor);
+    Scene.loadObject('models/cars/bmw/part5.json','world');
+    for(var i = 1; i <= 150; i+=1){
+        Scene.loadObject('models/cars/audi/part'+i+'.json');
+    }
+}
+
+function loadLamborghini(){
+    Scene.objects = [];
+    Scene.addObject(Floor);
+    for(var i = 1; i <= 67; i+=1){
+        Scene.loadObject('models/cars/lamborghini/part'+i+'.json');
+    }
+}
+
+function loadBMWV3(){
+    Scene.objects = [];
+    Scene.addObject(Floor);
+    Scene.loadObject('models/cars/bmw/part5.json','world');
+    for(var i = 1; i <= 41; i++){
+        Scene.loadObject('models/cars/bmwv3/part'+i+'.json');
+    }    
+}
+
 
+
+function loadNissan(){
+    for(var i = 1; i <= 46; i+=1){
+        Scene.loadObject('models/cars/nissan/part'+i+'.json');
+    }
 }
 
+
 function load(){
     Floor.build(80,2);
+    Floor.Ka = [1,1,1];
+    Floor.Kd = [0.6,0.6,0.6]
+    Floor.Ks = [1,1,1];
+    Floor.Ni = 1;
+    Floor.Ns = 1;
+    Floor.d = 1.0;
+    Floor.illum = 1;
+   
+    Floor.visible = floorVisible;
+    
     Scene.addObject(Floor);
-    for(var i=1; i < 11; i++){
-    Scene.loadObject('models/cars/bmw/pr'+i+'.json','bmw');
-    }
-  
+    loadBMW();
+
 }
 
 function render(){
     gl.viewport(0, 0, c_width, c_height);
     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
     
-    
+   // orderObjectsInScene();
     transforms.updatePerspective();
     
     try{
         
-      
-        
         for (var i = 0; i < Scene.objects.length; i++){
             
             var object = Scene.objects[i];
-			
+			if (object.visible != undefined && !object.visible) continue;
 			transforms.calculateModelView();           
             transforms.push();
             transforms.setMatrixUniforms();
             transforms.pop();
             
-            gl.uniform4fv(Program.uMaterialDiffuse, object.diffuse);
+            
    
             gl.enableVertexAttribArray(Program.aVertexPosition);
             gl.disableVertexAttribArray(Program.aVertexNormal);
             gl.disableVertexAttribArray(Program.aVertexColor);
+            
             gl.uniform1i(Program.uWireframe, false);
+            gl.uniform3fv(Program.uKa, object.Ka);
+            gl.uniform3fv(Program.uKd, object.Kd);
+            gl.uniform3fv(Program.uKs, object.Ks);
+            gl.uniform1f(Program.uNi, object.Ni);
+            gl.uniform1f(Program.uNs, object.Ns);
+            gl.uniform1f(Program.d, object.d);
+            gl.uniform1i(Program.illum, object.illum);
+            
+           //if(object.d < 1.0) {
+				//gl.disable(gl.DEPTH_TEST);
+				//gl.enable(gl.BLEND);
+                //gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);
+			//} else {
+			//	gl.enable(gl.DEPTH_TEST);
+			 //   gl.disable(gl.BLEND);
+			//}
             
             gl.bindBuffer(gl.ARRAY_BUFFER, object.vbo);
             gl.vertexAttribPointer(Program.aVertexPosition, 3, gl.FLOAT, false, 0, 0);
 			
             gl.bindBuffer(gl.ARRAY_BUFFER, null);
             gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
-            
+
         }
     }
     catch(err){
     }
 }
 
-
 function resizeCanvas(){
     c_width = $('#content').width();
     c_height = $('#content').height();
 
 $(window).resize(function(){resizeCanvas();});
 
-
 var app = undefined;
 function runShowRoom(){
     app = new WebGLApp("the-canvas");
 
 <body onLoad='runShowRoom()'>
 <div id="header">
-    <h1>Show Room</h2>
+    <h1>WebGL Beginner's Guide - Chapter 9 - Virtual Car Showroom</h1>
+    <h2>Customize your Car</h2>
+    <div id='logo-packt'><img src='packt.gif'/></div>
 </div>
 
 <div id="nav">
-    <b>Instructions</b>
+  
+    <table width='100%' cellspacing ='0'>
+    <tr><td colspan=3><h3>Car</h3></td></tr>
+    <tr><td width='40%'> Select the car to load: </td><td colspan='2' width='60%'>
+        <select id='select-car' >
+                    <option value ='opt-bmwi8'> BMW I8</option>
+                    <option value ='opt-mustang'> Ford Mustang </option>
+                    <option value ='opt-audi'> Audi R8 </option>
+                    <option value ='opt-bmwv3'> BMW v3 </option>
+        </select>
+        </td>
+    </tr>
+    <tr>
+       <td>Car Color:</td>
+       <td colspan=2><div id='carColor' class='colorSelector'><div style='background-color:rgb(255,255,255)'></div></div></td>
+    </tr>
+    <tr>
+        <td>Car Shininness:</td>
+        <td width='130px'><div id='slider-shininess'/></td>
+        <td id='slider-shininess-value' width='60px'>  0</td>
+   	</tr>
+    </table>
+    
+    <hr/>
+    
+    <table width='100%'>
+        <tr>
+            <td><h3>Lights</h3></td>
+        </tr>
+		<tr>
+            <td>
+                <table style='text-align:center'>
+                <tr>
+                    <td></td><td>Ambient</td><td></td>
+                    <td>Diffuse</td><td></td>
+                    <td>Specular</td><td></td>
+                </tr>    
+                <tr>
+                    <td style='text-align:right' width='160px'>far-left: </td>
+                    <td width='150px'><div id='slider-la1'/></td><td id='slider-la1-value' width='20px'>0.0</td>
+                    <td width='150px'><div id='slider-ld1'/></td><td id='slider-ld1-value' width='20px'>0.4</td>
+                    <td width='150px'><div id='slider-ls1'/></td><td id='slider-ls1-value' width='20px'>0.8</td>
+                </tr>
+                <tr>
+                    <td style='text-align:right' width='160px'>far-right: </td>
+                    <td width='150px'><div id='slider-la2'/></td><td id='slider-la2-value' width='20px'>0.0</td>
+                    <td width='150px'><div id='slider-ld2'/></td><td id='slider-ld2-value' width='20px'>0.4</td>
+                    <td width='150px'><div id='slider-ls2'/></td><td id='slider-ls2-value' width='20px'>0.8</td>
+                </tr>
+                <tr>
+                    <td style='text-align:right' width='160px'>near-left: </td>
+                    <td width='150px'><div id='slider-la3'/></td><td id='slider-la3-value' width='20px'>0.0</td>
+                    <td width='150px'><div id='slider-ld3'/></td><td id='slider-ld3-value' width='20px'>0.4</td>
+                    <td width='150px'><div id='slider-ls3'/></td><td id='slider-ls3-value' width='20px'>0.8</td>
+                </tr>
+                <tr>
+                    <td style='text-align:right' width='160px'>near-right: </td>
+                    <td width='150px'><div id='slider-la4'/></td><td id='slider-la4-value' width='20px'>0.0</td>
+                    <td width='150px'><div id='slider-ld4'/></td><td id='slider-ld4-value' width='20px'>0.4</td>
+                    <td width='150px'><div id='slider-ls4'/></td><td id='slider-ls4-value' width='20px'>0.8</td>
+                </tr>
+                </table>
+            </td>
+        </tr>
+        <tr>
+            <td align='right'><input type="checkbox" id="translate-lights"/> Translate Lights<br /></td>
+        </tr>    
+    </table>
+    <hr/>
+    <table width='100%'>
+        <tr>
+            <td><h3>Camera</h3></td>
+        </tr>
+        <tr>
+        <td align='center'>
+        <div id='opt-pose' >
+            <input type='radio' id='opt-home'  name='posecam' /><label for='opt-home'>Home</label>
+            <input type='radio' id='opt-above' name='posecam' /><label for='opt-above'>Above</label>
+            <input type='radio' id='opt-front' name='posecam' /><label for='opt-front'>Front</label>
+            <input type='radio' id='opt-back'  name='posecam' /><label for='opt-back'>Back</label>
+            <input type='radio' id='opt-left'  name='posecam' /><label for='opt-left'>Left</label>
+            <input type='radio' id='opt-right' name='posecam' /><label for='opt-right'>Right</label>
+            
+         </div>
+         </td>
+         </tr>
+         <tr>
+            <td align='center'>Zoom: Alt + Drag <input type="checkbox" id="show-floor"/>Floor Visible<br /></td>
+         </tr>          
+    </table>        
+
+    
 </div>
 
 <div id="content">
 </div>
 
 <script type='text/javascript'>resizeCanvas();</script>
+<script type='text/javascript'>
+    $('#opt-pose').buttonset();
+    $('#slider-shininess').slider({value:0.0, min:0.0, max:1.0, step:0.1, slide:function(event, ui){updateShininess(ui)}, change:function(event,ui){updateShininess(ui)}});
+    $('#slider-la1').slider({value:0.0, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(1, 'a')}, change:function(){updateLightProperty(1,'a')}});
+    $('#slider-ld1').slider({value:0.4, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(1, 'd')}, change:function(){updateLightProperty(1,'d')}});
+    $('#slider-ls1').slider({value:0.8, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(1, 's')}, change:function(){updateLightProperty(1,'s')}});
+    $('#slider-la2').slider({value:0.0, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(2, 'a')}, change:function(){updateLightProperty(2,'a')}});
+    $('#slider-ld2').slider({value:0.4, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(2, 'd')}, change:function(){updateLightProperty(2,'d')}});
+    $('#slider-ls2').slider({value:0.8, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(2, 's')}, change:function(){updateLightProperty(2,'s')}});
+    $('#slider-la3').slider({value:0.0, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(3, 'a')}, change:function(){updateLightProperty(3,'a')}});
+    $('#slider-ld3').slider({value:0.4, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(3, 'd')}, change:function(){updateLightProperty(3,'d')}});
+    $('#slider-ls3').slider({value:0.8, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(3, 's')}, change:function(){updateLightProperty(3,'s')}});
+    $('#slider-la4').slider({value:0.0, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(4, 'a')}, change:function(){updateLightProperty(4,'a')}});
+    $('#slider-ld4').slider({value:0.4, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(4, 'd')}, change:function(){updateLightProperty(4,'d')}});
+    $('#slider-ls4').slider({value:0.8, min:0.0, max:1.0, step:0.1, slide:function(){updateLightProperty(4, 's')}, change:function(){updateLightProperty(4,'s')}});
+
+$('#select-car').change(function(){
+    var val = $('#select-car').val();
+    switch (val){
+        case 'opt-bmwi8'    :loadBMW();     break;
+        case 'opt-mustang'  :loadMustang(); break;
+        case 'opt-audi'     :loadAudi();    break;
+        case 'opt-bmwv3'    :loadBMWV3();   break;
+    }
+        
+});    
+$('#show-floor').change(function(){
+	    floorVisible = !floorVisible;
+        Floor.visible = floorVisible;
+});
+
+$('#translate-lights').change(function(){
+    translateLights = !translateLights;
+    gl.uniform1i(Program.uTranslateLights, translateLights);
+});
+
+function updateShininess(ui){
+    $('#slider-shininess-value').html(ui.value);
+    var object = Scene.getObject('Shell_Mesh.023_Body');
+        if (object != null){
+            object.Ks = [ui.value, ui.value, ui.value];
+    }
+}
+
+function updateLightProperty(index,property){
+    var v = $('#slider-l'+property+''+index).slider("value");
+    $('#slider-l'+property+''+index+'-value').html(v);
+    var light = undefined;
+    switch(index){
+                case 1: light = light1; break;
+                case 2: light = light2; break;
+                case 3: light = light3; break;
+                case 4: light = light4; break;
+    }
+    
+    switch(property){
+                case 'a':light.setAmbient([v,v,v]);
+                         gl.uniform3fv(Program.uLa, Lights.getArray('ambient'));
+                         break;
+                case 'd':light.setDiffuse([v,v,v]);
+                         gl.uniform3fv(Program.uLd, Lights.getArray('diffuse'));
+                         break;
+                case 's':light.setSpecular([v,v,v]);
+                         gl.uniform3fv(Program.uLs, Lights.getArray('specular'));
+                         break;
+    }
+
+    render();
+}
+
+
+
+$('#carColor').ColorPicker({
+    onSubmit: function(hsb, hex, rgb, el) {
+            $(el).val(hex);
+            $(el).ColorPickerHide();
+            
+    },
+    color: '#00ff00',
+    onShow: function (colpkr) {
+        $(colpkr).fadeIn(500);
+        return false;
+    },
+    onHide: function (colpkr) {
+        $(colpkr).fadeOut(500);
+        return false;
+    },
+    onChange: function (hsb, hex, rgb) {
+        $('#carColor div').css('backgroundColor', '#' + hex);
+        var object = Scene.getObject('Shell_Mesh.023_Body');
+        if (object != null){
+            object.Kd = [rgb.r/256,rgb.g/256,rgb.b/256];
+        }
+        
+    },
+    
+    onBeforeShow: function (colpkr) {
+            $(this).ColorPickerSetColor('rgb(0.5,0.8,0.1)');
+        }
+    });
+
+
+var timer_anim_camera = undefined;
+var goal_camera_azimuth = 0;
+var goal_camera_elevation = 0;
+var goal_camera_home = cameraHome;
+
+
+
+$('#opt-home').click(function(){
+    if(timer_anim_camera) { clearInterval(timer_anim_camera); }
+    goal_camera_azimuth = cameraAzimuth;
+    goal_camera_elevation = cameraElevation;
+    timer_anim_camera = setInterval(animCamera, 5);
+});
+
+$('#opt-above').click(function(){
+    if(timer_anim_camera) { clearInterval(timer_anim_camera); }
+    goal_camera_azimuth = 0;
+    goal_camera_elevation = -90;
+    timer_anim_camera = setInterval(animCamera, 5);
+});
+
+$('#opt-front').click(function(){
+    if(timer_anim_camera) { clearInterval(timer_anim_camera); }
+    goal_camera_azimuth = 0;
+    goal_camera_elevation = -10;
+    timer_anim_camera = setInterval(animCamera, 5);
+});
+
+$('#opt-back').click(function(){
+    if(timer_anim_camera) { clearInterval(timer_anim_camera); }
+    goal_camera_azimuth = 180;
+    goal_camera_elevation = -10;
+    timer_anim_camera = setInterval(animCamera, 5);
+});
+
+$('#opt-left').click(function(){
+    if(timer_anim_camera) { clearInterval(timer_anim_camera); }
+    goal_camera_azimuth = -90;
+    goal_camera_elevation = 0;
+    timer_anim_camera = setInterval(animCamera, 5);
+});
+
+$('#opt-right').click(function(){
+    if(timer_anim_camera) { clearInterval(timer_anim_camera); }
+    goal_camera_azimuth = 90;
+    goal_camera_elevation = 0;
+    timer_anim_camera = setInterval(animCamera, 5);
+});
+
+
+
+function animCamera(){
+
+    var ca = goal_camera_azimuth-camera.azimuth;
+    var ce = goal_camera_elevation-camera.elevation;
+    var deltaPos = vec3.create([goal_camera_home[0]-camera.position[0], 
+                    goal_camera_home[1]-camera.position[1], 
+                    goal_camera_home[2]-camera.position[2]]);
+                    
+    
+    if (Math.abs(ca) < 0.5 && 
+        Math.abs(ce) < 0.5 && 
+        Math.abs(deltaPos[0]  < 0.1) &&
+        Math.abs(deltaPos[1]  < 0.1) &&
+        Math.abs(deltaPos[2]  < 0.1))
+    {
+        camera.setAzimuth(goal_camera_azimuth);
+        camera.setElevation(goal_camera_elevation);
+        camera.setPosition(goal_camera_home);
+        clearInterval(timer_anim_camera);
+    }
+    else{
+        camera.setAzimuth  (camera.azimuth   + ca/100);
+        camera.setElevation(camera.elevation + ce/100);
+        vec3.scale(deltaPos, 1/100);
+        vec3.add(camera.position,deltaPos,deltaPos);
+        camera.setPosition(deltaPos);
+        render();
+    }
+}    
+
+
+</script>
 </body>
 </html>

1727_09/ch9_Car_Showroom_AreaLight.html

+<html>
+
+<head>
+<title>WebGL Beginner's Guide - Chapter 9 - Car Show Room</title>
+<meta http-equiv='content-type' content='text/html; charset=ISO-8859-1'>
+<link href='css/styles.css'   type='text/css' rel='stylesheet'>
+<link href='css/cars.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> 
+
+<!-- MATH Libraries //-->
+<script type='text/javascript' src='js/math/glMatrix-0.9.5.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/Camera.js'></script>
+<script type='text/javascript' src='js/webgl/CameraInteractor.js'></script>
+<script type='text/javascript' src='js/webgl/SceneTransforms.js'></script>
+<script type='text/javascript' src='js/webgl/Texture.js'></script>
+<script type='text/javascript' src='js/webgl/Lights.js'></script>
+<script type='text/javascript' src='js/webgl/WebGLApp.js'></script>
+<script type='text/javascript' src='js/webgl/Picker.js'></script>
+
+<script id="shader-vs" type="x-shader/x-vertex">
+
+const int NUM_LIGHTS = 1;
+
+attribute vec3 aVertexPosition;
+attribute vec3 aVertexNormal;
+attribute vec4 aVertexColor;
+
+uniform mat4 uMVMatrix;
+uniform mat4 uPMatrix;
+uniform mat4 uNMatrix;
+uniform vec3  uLn[NUM_LIGHTS];   //normals
+uniform vec3  uLp[NUM_LIGHTS];   //position
+varying vec3 vNormal;
+varying vec3 vFragmentPos;
+varying vec3 vLightPos[NUM_LIGHTS];
+varying vec3 vLightNor[NUM_LIGHTS];
+
+void main(void) {
+
+     vec4 c = aVertexColor;
+     vec4 vertex = uMVMatrix * vec4(aVertexPosition, 1.0);
+     vNormal = vec3(uNMatrix * vec4(aVertexNormal, 0.0));  
+     vFragmentPos = vertex.xyz;
+     for(int i =0 ; i < NUM_LIGHTS; i ++){
+        vLightPos[i] = vec3(uMVMatrix * vec4(uLp[i],1.0));
+        vLightNor[i] = vec3(uNMatrix * vec4(uLn[i],0.0));
+         
+     }
+     gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
+}	
+</script>
+
+<script id="shader-fs" type="x-shader/x-fragment">
+#ifdef GL_ES
+precision highp float;
+#endif
+
+
+//Light uniforms
+const int NUM_LIGHTS = 1;
+uniform vec3  uLa[NUM_LIGHTS];   //ambient
+uniform vec3  uLd[NUM_LIGHTS];   //diffuse
+uniform vec3  uLs[NUM_LIGHTS];   //specular
+uniform vec3  uLn[NUM_LIGHTS];   //normals
+uniform vec3  uLp[NUM_LIGHTS];   //position
+
+//Material uniforms
+uniform vec3  uKa;   //ambient
+uniform vec3  uKd;   //diffuse
+uniform vec3  uKs;   //specular
+uniform float uNs;   //specular coefficient
+uniform float d;     //Opacity
+uniform int   illum; //Illumination mode
+uniform mat4 uMVMatrix;
+
+uniform bool  uWireframe;
+
+
+varying vec3 vNormal;
+varying vec3 vFragmentPos;
+varying vec3 vLightPos[NUM_LIGHTS];
+varying vec3 vLightNor[NUM_LIGHTS];
+
+vec3 projectOnPlane(in vec3 p, in vec3 pc, in vec3 pn){
+    float distance = dot(pn, p-pc);
+    return p - distance*pn;
+}
+
+int sideOfPlane(in vec3 p, in vec3 pc, in vec3 pn){
+   if (dot(p-pc,pn)>=0.0) return 1; else return 0;
+}
+
+vec3 linePlaneIntersect(in vec3 lp, in vec3 lv, in vec3 pc, in vec3 pn){
+   return lp+lv*(dot(pn,pc-lp)/dot(pn,lv));
+}
+
+float calculateAttenuation(in float dist){
+    return 1.0;
+    //return(1.0 / (0.5 + 0.1 * dist + 0.01 * dist * dist));
+}
+
+void areaLight(in vec3 lpos, in vec3 lnorm, in vec3 la, in vec3 ld, in vec3 ls, in vec3 N, in vec3 V, in float shininess,
+                inout vec3 ambient, inout vec3 diffuse, inout vec3 specular)
+{
+
+    vec3 right = normalize(vec3(uMVMatrix * vec4(1.0,0.0,0.0,1.0)));
+    vec3 pnormal = normalize(lnorm);
+    vec3 up = normalize(cross(right,pnormal));
+
+    //width and height of the area light:
+    float width = 10.0; 
+    float height = 10.0;
+
+    //project onto plane and calculate direction from center to the projection.
+    vec3 projection = projectOnPlane(V,lpos,pnormal);// projection in plane
+    vec3 dir = projection-vec3(lpos);
+
+    //calculate distance from area:
+    vec2 diagonal = vec2(dot(dir,right),dot(dir,up));
+    vec2 nearest2D = vec2(clamp( diagonal.x,-width,width  ),clamp(diagonal.y,-height,height));
+    vec3 nearestPointInside = vec3(lpos)+(right*nearest2D.x+up*nearest2D.y);
+
+    float dist = distance(V,nearestPointInside);//real distance to area rectangle
+
+    vec3 L = normalize(nearestPointInside - V);
+    float attenuation = calculateAttenuation(dist);
+
+    float nDotL = clamp(dot(pnormal,-L),0.0,1.0);
+
+    if (nDotL > 0.0 && sideOfPlane(V,lpos,pnormal) == 1) //looking at the plane
+    {   
+        //shoot a ray to calculate specular:
+        vec3 R = reflect(normalize(-V), N);
+        vec3 E = linePlaneIntersect(V,R,lpos,pnormal);
+
+        float specAngle = dot(R,pnormal);
+        if (specAngle > 0.0){
+	    vec3 dirSpec = E-vec3(lpos);
+    	    vec2 dirSpec2D = vec2(dot(dirSpec,right),dot(dirSpec,up));
+          vec2 nearestSpec2D = vec2(clamp( dirSpec2D.x,-width,width  ),clamp(  dirSpec2D.y,-height,height));
+    	    float specFactor = 1.0-clamp(length(nearestSpec2D-dirSpec2D)*shininess,0.0,1.0);
+          specular +=  ls  * attenuation * specFactor * specAngle;   
+        }
+        diffuse  +=  ld * attenuation * nDotL;       
+    }
+    ambient  += la * attenuation;
+
+}
+
+void main(void) {
+    if (uWireframe || illum == 0){
+        gl_FragColor = vec4(uKd,d);
+        return;
+    }
+    
+   vec3 COLOR    = vec3(0.0,0.0,0.0);
+   vec3 N        =  normalize(vNormal);
+   vec3 L        =  vec3(0.0,0.0,0.0);
+   vec3 ambient  = vec3(0.0,0.0,0.0);
+   vec3 diffuse  = vec3(0.0,0.0,0.0);
+   vec3 specular = vec3(0.0,0.0,0.0);  
+   
+   if (illum == 1){
+        for(int i = 0; i < NUM_LIGHTS; i++){
+            vec3 ray = vFragmentPos - uLp[i];        
+            L = normalize(ray);	
+            N = normalize(vNormal);	
+            COLOR += (uLa[i] * uKa) + (uLd[i] * uKd * clamp(dot(N, -L),0.0,1.0));
+        }   
+        gl_FragColor =  vec4(COLOR,d);
+        return;
+   }
+   
+   if (illum == 2){
+       
+        for(int i = 0; i < NUM_LIGHTS; i++){
+            areaLight(vLightPos[i], vLightNor[i], uLa[i], uLd[i], uLs[i], N, vFragmentPos, 90.5, ambient, diffuse, specular);
+
+        }
+        COLOR += ambient  * uKa + diffuse * uKd + specular * uKs * uNs;
+        
+        gl_FragColor =  vec4(COLOR,d);
+        return;
+   }
+}
+</script>
+
+<script type="text/javascript">
+
+var camera = null,
+interactor = null,
+transforms = null;
+
+function configure(){
+    gl.clearColor(0.3,0.3,0.3, 1.0);
+    gl.clearDepth(100.0);
+    
+    gl.enable(gl.DEPTH_TEST);
+    //gl.enable(gl.CULL_FACE);
+    gl.depthFunc(gl.LEQUAL);
+    //gl.blendEquation(gl.FUNC_ADD);
+	gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);
+  
+    //Creates and sets up the camera location
+    camera = new Camera(CAMERA_ORBITING_TYPE);
+    camera.goHome([0,0,7]);
+    camera.setFocus([0.0,0.0,0.0]);
+	camera.setAzimuth(25);
+	camera.setElevation(-30);
+    camera.hookRenderer = render;
+    
+    //Creates and sets up the mouse and keyboard interactor
+    interactor = new CameraInteractor(camera, document.getElementById('the-canvas'));
+    
+    //Scene Transforms
+    transforms = new SceneTransforms(camera);
+   
+    //init transforms
+    transforms.init();
+
+	//init Program
+	var attributeList = ["aVertexPosition",
+					"aVertexNormal",
+					"aVertexColor"];
+
+	var uniformList = [	"uPMatrix", 
+					"uMVMatrix", 
+					"uNMatrix",
+                    "uLp",
+                    "uWireframe",
+                    "uLa",
+                    "uLd",
+                    "uLs",
+                    "uLn",
+                    "uKa",
+                    "uKd",
+                    "uKs",
+                    "uNs",
+                    "d",
+                    "illum"
+					];
+					
+	Program.load(attributeList, uniformList);
+
+
+    var lightOne = new Light('front');
+	lightOne.setPosition([0,50,0]); //-5,5,-5
+	lightOne.setDiffuse([0.8,0.8,0.8]);
+    lightOne.setAmbient([0.0,0.0,0.0]);
+    lightOne.setSpecular([0.8,0.8,0.8]);
+    lightOne.setProperty('normal', [0,-1,0]);
+	
+	var lightTwo = new Light('back');
+	lightTwo.setPosition([5,5,-1.5]);
+	lightTwo.setDiffuse([0.8,0.8,0.8]);
+    lightTwo.setAmbient([0.01,0.01,0.01]);
+    lightTwo.setSpecular([0.8,0.8,0.8]);
+    lightTwo.setProperty('normal', [-5,-5,1.5]);
+	
+	var lightThree = new Light('left');
+	lightThree.setPosition([-5,5,0]);
+	lightThree.setDiffuse([0.8,0.8,0.8]);
+    lightThree.setAmbient([0.0,0.0,0.0]);
+    lightThree.setSpecular([0.38,0.38,0.38]);
+    lightThree.setProperty('normal', [5,-5,0]);
+    
+    var lightFour = new Light('right');
+    lightFour.setPosition([5,5,0]);
+    lightFour.setDiffuse([0.8,0.8,0.8]);
+    lightFour.setAmbient([0.0,0.0,0.0]);
+    lightFour.setSpecular([0.38,0.38,0.38]);
+    lightFour.setProperty('normal', [-5,-5,0]);
+    
+    
+	
+	Lights.add(lightOne);
+	//Lights.add(lightTwo);
+    //Lights.add(lightThree);	
+	//Lights.add(lightFour);	
+    
+    
+	
+	
+    gl.uniform3fv(Program.uLp,      Lights.getArray('position'));
+    gl.uniform3fv(Program.uLa ,     Lights.getArray('ambient'));
+    gl.uniform3fv(Program.uLd,      Lights.getArray('diffuse'));
+    gl.uniform3fv(Program.uLs,      Lights.getArray('specular'));
+    gl.uniform3fv(Program.uLn,      Lights.getArray('normal'));
+    
+    gl.uniform3fv(Program.uKa ,     [1.0,1.0,1.0]);
+    gl.uniform3fv(Program.uKd ,     [1.0,1.0,1.0]);
+    gl.uniform3fv(Program.uKs ,     [1.0,1.0,1.0]);
+    
+    gl.uniform1f(Program.uNs, 1.0)
+    gl.uniform1f(Program.uNi, 1.0)
+    
+
+}
+
+function loadBMW(){
+
+Scene.loadObject('models/cars/bmw/part6.json');
+    Scene.loadObject('models/cars/bmw/part15.json');
+   
+    for(var i = 1; i <= 24; i+=1){
+       if (i != 6 && i != 15){
+        Scene.loadObject('models/cars/bmw/part'+i+'.json');
+        }
+    }
+    
+    
+}
+
+function loadNissan(){
+    for(var i = 1; i <= 46; i+=1){
+        Scene.loadObject('models/cars/nissan/part'+i+'.json');
+    }
+}
+
+function loadAudi(){
+    for(var i = 1; i <= 43; i+=1){
+        Scene.loadObject('models/cars/audi/part'+i+'.json');
+    }
+}
+
+function loadLamborghini(){
+    for(var i = 1; i <= 67; i+=1){
+        Scene.loadObject('models/cars/lamborghini/part'+i+'.json');
+    }
+    //Scene.loadObject('models/cars/lamborghini/part49.json');
+}
+
+
+function orderObjectsInScene(){
+    for(var i = 0, max = Scene.objects.length; i < max; i+=1){
+        var o = Scene.objects[i];
+        var p = Scene.objects[(i<max-1)?(i+1):i]
+        if (o.d < 1.0 && p.d == 1.0){
+            o.move = true;
+            
+        } 
+        console.info(o.alias + ' - before -= d->' + o.d);
+    }
+    
+    for(var i = 0, max = Scene.objects.length; i < max; i+=1){
+        var o = Scene.objects[i];
+        if (o.move){
+            
+            var p = Scene.objects[max-1];
+            j = 1;
+            while (p.d < 1){
+                p = Scene.objects[max-1-j];
+                j++;
+            }
+            var idx = Scene.objects.indexOf(p);
+            Scene.objects.splice(i, 1);
+            Scene.objects.splice(idx + 1, 0, o);
+        }
+    }
+    
+    for(var i = 0, max = Scene.objects.length; i < max; i+=1){
+        var o = Scene.objects[i];
+        console.info(o.alias + '= d->' + o.d);
+    }
+    
+    
+}
+
+function load(){
+    Floor.build(80,2);
+    Floor.Ka = [1,1,1];
+    Floor.Kd = [0.6,0.6,0.6]
+    Floor.Ks = [1,1,1];
+    Floor.Ni = 1;
+    Floor.Ns = 1;
+    Floor.d = 1.0;
+    Floor.illum = 1;
+    //Scene.addObject(Floor);
+    
+    
+    
+   loadBMW();
+   //loadNissan();
+   //loadAudi();
+   //loadLamborghini();
+ 
+}
+
+function render(){
+    gl.viewport(0, 0, c_width, c_height);
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+    
+   // orderObjectsInScene();
+    transforms.updatePerspective();
+    
+    try{
+        
+        for (var i = 0; i < Scene.objects.length; i++){
+            
+            var object = Scene.objects[i];
+			
+			transforms.calculateModelView();           
+            transforms.push();
+            transforms.setMatrixUniforms();
+            transforms.pop();
+            
+            
+   
+            gl.enableVertexAttribArray(Program.aVertexPosition);
+            gl.disableVertexAttribArray(Program.aVertexNormal);
+            gl.disableVertexAttribArray(Program.aVertexColor);
+            
+            gl.uniform1i(Program.uWireframe, false);
+            gl.uniform3fv(Program.uKa, object.Ka);
+            gl.uniform3fv(Program.uKd, object.Kd);
+            gl.uniform3fv(Program.uKs, object.Ks);
+            gl.uniform1f(Program.uNi, object.Ni);
+            gl.uniform1f(Program.uNs, object.Ns);
+            gl.uniform1f(Program.d, object.d);
+            gl.uniform1i(Program.illum, object.illum);
+            
+           /* if(object.d < 1.0) {
+				gl.disable(gl.DEPTH_TEST);
+				gl.enable(gl.BLEND);
+			} else {
+				gl.enable(gl.DEPTH_TEST);
+			    gl.disable(gl.BLEND);
+			}*/
+            
+            gl.bindBuffer(gl.ARRAY_BUFFER, object.vbo);
+            gl.vertexAttribPointer(Program.aVertexPosition, 3, gl.FLOAT, false, 0, 0);
+            gl.enableVertexAttribArray(Program.aVertexPosition);
+            
+			if(!object.wireframe){
+				gl.bindBuffer(gl.ARRAY_BUFFER, object.nbo);
+				gl.vertexAttribPointer(Program.aVertexNormal, 3, gl.FLOAT, false, 0, 0);
+				gl.enableVertexAttribArray(Program.aVertexNormal);
+            }
+            else{
+                gl.uniform1i(Program.uWireframe, true);
+            }
+			
+            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, object.ibo);
+			
+			if (object.wireframe){
+                gl.drawElements(gl.LINES, object.indices.length, gl.UNSIGNED_SHORT,0);
+            }
+            else{
+                //gl.cullFace(gl.BACK);
+                //gl.drawElements(gl.TRIANGLES, object.indices.length, gl.UNSIGNED_SHORT,0);
+                //gl.cullFace(gl.FRONT);
+                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);
+    }
+}
+
+var itTime = 20;
+function moveLight(){
+itTime -= 0.5;
+if (itTime == -20) itTime = 20;
+
+var pos = [];
+for (var i =0 ; i < 5; i++){
+    pos.push(0,5,itTime);
+}
+gl.uniform3fv(Program.uLightPosition, pos);
+render();
+}
+
+
+function resizeCanvas(){
+    c_width = $('#content').width();
+    c_height = $('#content').height();
+    $('#the-canvas').attr('width',c_width);
+    $('#the-canvas').attr('height',c_height);
+}
+
+$(window).resize(function(){resizeCanvas();});
+
+
+var app = undefined;
+function runShowRoom(){
+    app = new WebGLApp("the-canvas");
+    app.configureGLHook = configure;
+    app.loadSceneHook   = load;
+    app.drawSceneHook   = render;
+    app.run(); 
+    
+    //setInterval(moveLight, 50);
+}
+</script>
+</head>
+
+<body onLoad='runShowRoom()'>
+<div id="header">
+    <h1>WebGL Beginner's Guide - Chapter 9 - Virtual Car Showroom</h1>
+    <h2>Application Proof of Concept</h2>
+    <div id='logo-packt'><img src='packt.gif'/></div>
+</div>
+
+<div id="nav">
+    <p> Select the car to load :</p>
+    
+    
+</div>
+
+<div id="content">
+    <canvas id='the-canvas'></canvas>
+</div>
+
+<script type='text/javascript'>resizeCanvas();</script>
+</body>
+</html>

1727_09/ch9_Car_Showroom_DeltaRay.html

+<html>
+
+<head>
+<title>WebGL Beginner's Guide - Chapter 9 - Car Show Room</title>
+<meta http-equiv='content-type' content='text/html; charset=ISO-8859-1'>
+<link href='css/styles.css'   type='text/css' rel='stylesheet'>
+<link href='css/cars.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> 
+
+<!-- MATH Libraries //-->
+<script type='text/javascript' src='js/math/glMatrix-0.9.5.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/Camera.js'></script>
+<script type='text/javascript' src='js/webgl/CameraInteractor.js'></script>
+<script type='text/javascript' src='js/webgl/SceneTransforms.js'></script>
+<script type='text/javascript' src='js/webgl/Texture.js'></script>
+<script type='text/javascript' src='js/webgl/Lights.js'></script>
+<script type='text/javascript' src='js/webgl/WebGLApp.js'></script>
+<script type='text/javascript' src='js/webgl/Picker.js'></script>
+
+<script id="shader-vs" type="x-shader/x-vertex">
+
+const int NUM_LIGHTS = 4;
+
+attribute vec3 aVertexPosition;
+attribute vec3 aVertexNormal;
+attribute vec4 aVertexColor;
+
+uniform mat4 uMVMatrix;
+uniform mat4 uPMatrix;
+uniform mat4 uNMatrix;
+
+
+uniform vec3 uLightPosition[NUM_LIGHTS];
+
+varying vec3 vNormal;
+varying vec3 vLightRay[NUM_LIGHTS];
+varying vec3 vEye[NUM_LIGHTS];
+
+void main(void) {
+
+     vec4 c = aVertexColor;
+     vec4 vertex = uMVMatrix * vec4(aVertexPosition, 1.0);
+     vNormal = vec3(uNMatrix * vec4(aVertexNormal, 1.0));
+     
+     for(int i=0; i < NUM_LIGHTS; i++){
+        vec4 lightPosition = vec4(uLightPosition[i],1.0); //uMVMatrix * vec4(uLightPosition[i], 1.0);//
+        vLightRay[i] = vertex.xyz - lightPosition.xyz;
+        vEye[i] = -vec3(vertex.xyz);
+    }
+     
+    
+     gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
+}	
+</script>
+
+<script id="shader-fs" type="x-shader/x-fragment">
+#ifdef GL_ES
+precision highp float;
+#endif
+
+
+//Light uniforms
+const int NUM_LIGHTS = 4;
+uniform vec3  uLa[NUM_LIGHTS];   //ambient
+uniform vec3  uLd[NUM_LIGHTS];   //diffuse
+uniform vec3  uLs[NUM_LIGHTS];   //specular
+uniform vec3 uLightPosition[NUM_LIGHTS];
+
+//Material uniforms
+uniform vec3  uKa;   //ambient
+uniform vec3  uKd;   //diffuse
+uniform vec3  uKs;   //specular
+uniform float uNs;   //specular coefficient
+uniform float d;     //Opacity
+uniform int   illum; //Illumination mode
+
+
+uniform bool  uWireframe;
+
+
+varying vec3 vNormal;
+varying vec3 vLightRay[NUM_LIGHTS];
+varying vec3 vEye[NUM_LIGHTS];
+
+vec3 projectOnPlane(in vec3 p, in vec3 pc, in vec3 pn)
+{
+    float distance = dot(pn, p-pc);
+    return p - distance*pn;
+}
+
+
+void main(void) {
+    if (uWireframe || illum == 0){
+        gl_FragColor = vec4(uKd,d);
+        return;
+    }
+    
+   vec3 COLOR = vec3(0.0,0.0,0.0);
+   vec3 N =  normalize(vNormal);
+   vec3 L =  vec3(0.0,0.0,0.0);
+   vec3 E =  vec3(0.0,0.0,0.0);
+   vec3 R =  vec3(0.0,0.0,0.0);
+   vec3 deltaRay = vec3(0.0);
+   const int  lsize = 2;
+   const float step = 0.25;
+   const float inv_total = 1.0/((float(lsize*lsize) + 1.0)*(float(lsize*lsize) + 1.0));  //how many deltaRays
+   
+   float dx = 0.0;
+   float dz = 0.0;
+   float LT = 0.0;
+   
+   if (illum == 1){
+        for(int i = 0; i < NUM_LIGHTS; i++){	
+            L = normalize(vLightRay[i]);	
+            N = normalize(vNormal);	
+            COLOR += (uLa[i] * uKa) + (uLd[i] * uKd * clamp(dot(N, -L),0.0,1.0));
+        }   
+        gl_FragColor =  vec4(COLOR,d);
+        return;
+   }
+   
+   if (illum == 2){
+        for(int i = 0; i < NUM_LIGHTS; i++){
+            dx = 0.0;
+            dz = 0.0;
+            E = normalize(vEye[i]);
+            for (int x = -lsize; x <= lsize; x++){
+                dx = dx + step;
+                for(int z = -lsize; z <= lsize; z++) {
+                    dz = dz + step;
+                    deltaRay = vec3(vLightRay[i].x + dx, vLightRay[i].y, vLightRay[i].z + dz);
+                    L = normalize(deltaRay);
+                    R = reflect(L, N);
+                    COLOR += (uLa[i] * uKa);