| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 <!-- | 
|  | 2 Copyright 2009, Google Inc. | 
|  | 3 All rights reserved. | 
|  | 4 | 
|  | 5 Redistribution and use in source and binary forms, with or without | 
|  | 6 modification, are permitted provided that the following conditions are | 
|  | 7 met: | 
|  | 8 | 
|  | 9     * Redistributions of source code must retain the above copyright | 
|  | 10 notice, this list of conditions and the following disclaimer. | 
|  | 11     * Redistributions in binary form must reproduce the above | 
|  | 12 copyright notice, this list of conditions and the following disclaimer | 
|  | 13 in the documentation and/or other materials provided with the | 
|  | 14 distribution. | 
|  | 15     * Neither the name of Google Inc. nor the names of its | 
|  | 16 contributors may be used to endorse or promote products derived from | 
|  | 17 this software without specific prior written permission. | 
|  | 18 | 
|  | 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | 20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | 21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | 22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | 23 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | 24 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | 25 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | 26 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | 27 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | 28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | 29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | 30 --> | 
|  | 31 | 
|  | 32 <!-- | 
|  | 33 This sample uses a custom render graph to implement a basic shadow map | 
|  | 34 algorithm. | 
|  | 35 | 
|  | 36 The technique works by rendering the scene in two passes.  The first pass | 
|  | 37 renders the geometry in the scene with a shader that colors each pixel a shade | 
|  | 38 of gray representing how far the rendered point is from the light source.  That | 
|  | 39 image, the shadow map, is rendered to a texture, and then the second (visible) | 
|  | 40 render pass samples it to determine which points in the scene are in shaodow. | 
|  | 41 --> | 
|  | 42 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" | 
|  | 43   "http://www.w3.org/TR/html4/loose.dtd"> | 
|  | 44 <html> | 
|  | 45 <head> | 
|  | 46 <meta http-equiv="content-type" content="text/html; charset=UTF-8"> | 
|  | 47 <title> | 
|  | 48 Shadow Mapping | 
|  | 49 </title> | 
|  | 50 <script type="text/javascript" src="o3djs/base.js"></script> | 
|  | 51 <script type="text/javascript"> | 
|  | 52 o3djs.require('o3djs.util'); | 
|  | 53 o3djs.require('o3djs.math'); | 
|  | 54 o3djs.require('o3djs.rendergraph'); | 
|  | 55 o3djs.require('o3djs.primitives'); | 
|  | 56 o3djs.require('o3djs.effect'); | 
|  | 57 o3djs.require('o3djs.debug'); | 
|  | 58 o3djs.require('o3djs.material'); | 
|  | 59 | 
|  | 60 // The initClient() function runs when the page has finished loading. | 
|  | 61 window.onload = initClient; | 
|  | 62 | 
|  | 63 // global variables | 
|  | 64 var g_o3dElement; | 
|  | 65 var g_client; | 
|  | 66 var g_o3d; | 
|  | 67 var g_math; | 
|  | 68 var g_pack; | 
|  | 69 var g_colorViewInfo; | 
|  | 70 var g_shadowViewInfo; | 
|  | 71 var g_shadowTexture; | 
|  | 72 var g_shadowMaterial; | 
|  | 73 var g_shadowColorEffect; | 
|  | 74 var g_shadowSampler; | 
|  | 75 var g_lightViewProjection; | 
|  | 76 var g_lightFrustumTransform; | 
|  | 77 var g_globalParams = { }; | 
|  | 78 var g_viewFromLight = false; | 
|  | 79 | 
|  | 80 var g_renderSurfaceSet; | 
|  | 81 var g_colorPassRenderRoot; | 
|  | 82 | 
|  | 83 var g_lightWorldPos = [5, 10, 0]; | 
|  | 84 var g_lightColor = [1, 1, 1, 1]; | 
|  | 85 var g_eyePosition = [1, 6, 20]; | 
|  | 86 var g_targetPosition = [0, 2, 0]; | 
|  | 87 | 
|  | 88 // constants. | 
|  | 89 var SHADOW_MAP_WIDTH = 512; | 
|  | 90 var SHADOW_MAP_HEIGHT = 512; | 
|  | 91 | 
|  | 92 var g_finished = false;  // for selenium testing. | 
|  | 93 | 
|  | 94 | 
|  | 95 /** | 
|  | 96  * Creates the client area. | 
|  | 97  */ | 
|  | 98 function initClient() { | 
|  | 99   o3djs.util.makeClients(main, 'FloatingPointTextures'); | 
|  | 100 } | 
|  | 101 | 
|  | 102 | 
|  | 103 /** | 
|  | 104  * Initializes global variables, positions camera, draws shapes. | 
|  | 105  * @param {Array} clientElements Array of o3d object elements. | 
|  | 106  */ | 
|  | 107 function main(clientElements) { | 
|  | 108   // Init global variables. | 
|  | 109   initGlobals(clientElements); | 
|  | 110 | 
|  | 111   // Set up the rendergraph. | 
|  | 112   initRenderGraph(); | 
|  | 113 | 
|  | 114   // Load effects, bind material parameters. | 
|  | 115   initMaterials(); | 
|  | 116 | 
|  | 117   // Add the shapes to the transform graph. | 
|  | 118   createShapes(); | 
|  | 119 | 
|  | 120   // Set up the view and projection transformations for the camera. | 
|  | 121   updateCamera(); | 
|  | 122 | 
|  | 123   // Init global parameters.  initGlobalParams() searches all materials in order | 
|  | 124   // to bind parameters, so it must be called after initMaterials() | 
|  | 125   initGlobalParams(); | 
|  | 126 | 
|  | 127   // Set the view and projection transformations for the light. | 
|  | 128   updateLightMatrix(); | 
|  | 129 | 
|  | 130   // Create the light that gets drawn. | 
|  | 131   createLightShape(); | 
|  | 132 | 
|  | 133   // Execute keyPressed() when we detect a keypress on the window or | 
|  | 134   // on the o3d object. | 
|  | 135   window.document.onkeypress = keyPressed; | 
|  | 136   g_o3dElement.onkeypress = keyPressed; | 
|  | 137 | 
|  | 138   g_finished = true;  // for selenium testing. | 
|  | 139 } | 
|  | 140 | 
|  | 141 | 
|  | 142 /** | 
|  | 143  * Initializes global variables and libraries. | 
|  | 144  */ | 
|  | 145 function initGlobals(clientElements) { | 
|  | 146   g_o3dElement = clientElements[0]; | 
|  | 147   g_client = g_o3dElement.client; | 
|  | 148   g_o3d = g_o3dElement.o3d; | 
|  | 149   g_math = o3djs.math; | 
|  | 150 | 
|  | 151   // Create a pack to manage the objects created. | 
|  | 152   g_pack = g_client.createPack(); | 
|  | 153 } | 
|  | 154 | 
|  | 155 | 
|  | 156 /** | 
|  | 157  * Sets up the render graph.  Builds a basic view for the camera and the light | 
|  | 158  * point of view, arranges for the view from the light to be rendered to a | 
|  | 159  * texture for the shadow map.  Unlike the basic render graph created by the | 
|  | 160  * the utility function o3djs.rendergraph.createBasicView, to render the shadow | 
|  | 161  * map and then render the scene, we need two subtrees of the render graph, one | 
|  | 162  * for shadow map render pass and one to draw the scene. | 
|  | 163  */ | 
|  | 164 function initRenderGraph() { | 
|  | 165   // Create the texture that will store the depth information. | 
|  | 166   g_shadowTexture = g_pack.createTexture2D(SHADOW_MAP_WIDTH, | 
|  | 167                                            SHADOW_MAP_HEIGHT, | 
|  | 168                                            g_o3d.Texture.ABGR32F, | 
|  | 169                                            1, | 
|  | 170                                            true); | 
|  | 171   var renderSurface = g_shadowTexture.getRenderSurface(0, g_pack); | 
|  | 172 | 
|  | 173   // Create the depth-stencil buffer required when rendering the teapot. | 
|  | 174   var depthSurface = g_pack.createDepthStencilSurface(SHADOW_MAP_WIDTH, | 
|  | 175                                                       SHADOW_MAP_HEIGHT); | 
|  | 176 | 
|  | 177   // The children of any one node in the render graph get traversed in order by | 
|  | 178   // priority.  Here, we're forcing the shadow map to get rendered first by | 
|  | 179   // by giving its render root lower priority. | 
|  | 180   var shadowPassRenderRoot = g_pack.createObject('RenderNode'); | 
|  | 181   shadowPassRenderRoot.priority = 0; | 
|  | 182 | 
|  | 183   g_colorPassRenderRoot = g_pack.createObject('RenderNode'); | 
|  | 184   g_colorPassRenderRoot.priority = 1; | 
|  | 185 | 
|  | 186   shadowPassRenderRoot.parent = g_client.renderGraphRoot; | 
|  | 187   g_colorPassRenderRoot.parent = g_client.renderGraphRoot; | 
|  | 188 | 
|  | 189   g_renderSurfaceSet = g_pack.createObject('RenderSurfaceSet'); | 
|  | 190   g_renderSurfaceSet.renderSurface = renderSurface; | 
|  | 191   g_renderSurfaceSet.renderDepthStencilSurface = depthSurface; | 
|  | 192   g_renderSurfaceSet.parent = shadowPassRenderRoot; | 
|  | 193 | 
|  | 194   // Create a render sub-graph for the shadow map generation. | 
|  | 195   g_shadowViewInfo = o3djs.rendergraph.createBasicView( | 
|  | 196       g_pack, | 
|  | 197       g_client.root, | 
|  | 198       g_renderSurfaceSet, | 
|  | 199       [1, 1, 1, 1]); | 
|  | 200 | 
|  | 201   // Create a render sub-graph for the regular pass. | 
|  | 202   g_colorViewInfo = o3djs.rendergraph.createBasicView( | 
|  | 203       g_pack, | 
|  | 204       g_client.root, | 
|  | 205       g_colorPassRenderRoot, | 
|  | 206       [0, 0, 0, 1]); | 
|  | 207 } | 
|  | 208 | 
|  | 209 | 
|  | 210 /** | 
|  | 211  * Switches between the camera and light point of view. | 
|  | 212  */ | 
|  | 213 function toggleView() { | 
|  | 214   if (g_viewFromLight) { | 
|  | 215     g_shadowViewInfo.root.parent = g_renderSurfaceSet; | 
|  | 216     g_colorPassRenderRoot.parent = g_client.renderGraphRoot; | 
|  | 217     g_viewFromLight = false; | 
|  | 218   } else { | 
|  | 219     g_shadowViewInfo.root.parent = g_client.renderGraphRoot; | 
|  | 220     g_colorPassRenderRoot.parent = null; | 
|  | 221     g_viewFromLight = true; | 
|  | 222   } | 
|  | 223 } | 
|  | 224 | 
|  | 225 /** | 
|  | 226  * Creates a material to be put on all shapes in the scene for the shadow pass, | 
|  | 227  * and loads effects for materials in the scene.  Other materials are created | 
|  | 228  * on the fly as the shapes are created. | 
|  | 229  */ | 
|  | 230 function initMaterials() { | 
|  | 231   g_shadowMaterial = g_pack.createObject('Material'); | 
|  | 232   g_shadowMaterial.drawList = g_shadowViewInfo.performanceDrawList; | 
|  | 233 | 
|  | 234   var shadowEffect = g_pack.createObject('Effect'); | 
|  | 235   var shadowEffectString = document.getElementById('shadowShader').text; | 
|  | 236   shadowEffect.loadFromFXString(shadowEffectString); | 
|  | 237   g_shadowMaterial.effect = shadowEffect; | 
|  | 238   shadowEffect.createUniformParameters(g_shadowMaterial); | 
|  | 239 | 
|  | 240   g_shadowColorEffect = g_pack.createObject('Effect'); | 
|  | 241   var colorEffectString = document.getElementById('shadowColorShader').text; | 
|  | 242   g_shadowColorEffect.loadFromFXString(colorEffectString); | 
|  | 243 | 
|  | 244   g_shadowSampler = g_pack.createObject('Sampler'); | 
|  | 245   g_shadowSampler.texture = g_shadowTexture; | 
|  | 246   g_shadowSampler.minFilter = g_o3d.Sampler.POINT; | 
|  | 247   g_shadowSampler.magFilter = g_o3d.Sampler.POINT; | 
|  | 248   g_shadowSampler.mipFilter = g_o3d.Sampler.POINT; | 
|  | 249   g_shadowSampler.addressModeU = g_o3d.Sampler.BORDER; | 
|  | 250   g_shadowSampler.addressModeV = g_o3d.Sampler.BORDER; | 
|  | 251   g_shadowSampler.borderColor = [1, 1, 1, 1]; | 
|  | 252 } | 
|  | 253 | 
|  | 254 | 
|  | 255 /** | 
|  | 256  * Sets up reasonable view and projection matrices. | 
|  | 257  */ | 
|  | 258 function updateCamera() { | 
|  | 259   // Set up a perspective transformation for the projection. | 
|  | 260   g_colorViewInfo.drawContext.projection = g_math.matrix4.perspective( | 
|  | 261       g_math.degToRad(30), // 30 degree frustum. | 
|  | 262       g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. | 
|  | 263       1,                   // Near plane. | 
|  | 264       5000);               // Far plane. | 
|  | 265 | 
|  | 266   // Set up our view transformation to look towards the world origin where the | 
|  | 267   // cube is located. | 
|  | 268   g_colorViewInfo.drawContext.view = g_math.matrix4.lookAt( | 
|  | 269       g_eyePosition,    // eye | 
|  | 270       g_targetPosition, // target | 
|  | 271       [0, 1, 0]);       // up | 
|  | 272 } | 
|  | 273 | 
|  | 274 | 
|  | 275 /** | 
|  | 276  * Computes the view and projection matrices from the point of view of the | 
|  | 277  * light. Sets the lightViewProjection parameter so the color shader can access | 
|  | 278  * it. | 
|  | 279  */ | 
|  | 280 function updateLightMatrix() { | 
|  | 281   // The perspective projection matrix for the light. | 
|  | 282   var lightProjection = g_math.matrix4.perspective( | 
|  | 283       g_math.degToRad(45), // 45 degree fov. | 
|  | 284       SHADOW_MAP_WIDTH / SHADOW_MAP_HEIGHT,   // Aspect ratio. | 
|  | 285       4,                   // Near plane. | 
|  | 286       20);                 // Far plane. | 
|  | 287 | 
|  | 288   // Make the light point toward the origin | 
|  | 289   var lightView = g_math.matrix4.lookAt( | 
|  | 290       g_lightWorldPos,   // light | 
|  | 291       [0, 0, 0],         // target | 
|  | 292       [1, 0, 0]);        // up | 
|  | 293 | 
|  | 294   g_lightViewProjection = g_math.matrix4.composition( | 
|  | 295       lightProjection, lightView); | 
|  | 296 | 
|  | 297   g_shadowViewInfo.drawContext.projection = lightProjection; | 
|  | 298   g_shadowViewInfo.drawContext.view = lightView; | 
|  | 299 | 
|  | 300   g_globalParams.lightViewProjection.value = g_lightViewProjection; | 
|  | 301 } | 
|  | 302 | 
|  | 303 | 
|  | 304 /** | 
|  | 305  * Creates shapes using the primitives utility library, and adds them to the | 
|  | 306  * transform graph at the root node. | 
|  | 307  */ | 
|  | 308 function createShapes() { | 
|  | 309   // A green phong-shaded material for the cube. | 
|  | 310   var cubeMaterial = createShadowColorMaterial([0.2, 0.5, 0, 1]); | 
|  | 311 | 
|  | 312   // The cube shape. | 
|  | 313   var cube = o3djs.primitives.createCube( | 
|  | 314       g_pack, | 
|  | 315       cubeMaterial, | 
|  | 316       2);     // The length of each side of the cube. | 
|  | 317 | 
|  | 318   // A red phong-shaded material for the sphere. | 
|  | 319   var sphereMaterial = createShadowColorMaterial([0.7, 0.2, 0.1, 1]); | 
|  | 320 | 
|  | 321   // The sphere shape. | 
|  | 322   var sphere = o3djs.primitives.createSphere( | 
|  | 323       g_pack, sphereMaterial, 0.5, 50, 50); | 
|  | 324 | 
|  | 325   // A blue phong-shaded material for the plane. | 
|  | 326   var planeMaterial = createShadowColorMaterial([0, 0.3, 0.5, 1]); | 
|  | 327 | 
|  | 328   // The plane shape. | 
|  | 329   var plane = o3djs.primitives.createPlane( | 
|  | 330       g_pack, | 
|  | 331       planeMaterial, | 
|  | 332       20,      // Width. | 
|  | 333       20,      // Depth. | 
|  | 334       1,       // Horizontal subdivisions. | 
|  | 335       1);      // Vertical subdivisions. | 
|  | 336 | 
|  | 337   // Associate to each shape, a translation vector. | 
|  | 338   var transformTable = [ | 
|  | 339     {shape: cube, translation: [0, 1, 0]}, | 
|  | 340     {shape: sphere, translation: [0.5, 2.5, 0]}, | 
|  | 341     {shape: plane, translation: [0, 0, 0]} | 
|  | 342   ]; | 
|  | 343 | 
|  | 344   // Add the shapes to the transform graph with the translation. | 
|  | 345   var modelRoot = g_pack.createObject('Transform'); | 
|  | 346   modelRoot.parent = g_client.root; | 
|  | 347   for (var tt = 0; tt < transformTable.length; ++tt) { | 
|  | 348     var transform = g_pack.createObject('Transform'); | 
|  | 349     transform.addShape(transformTable[tt].shape); | 
|  | 350     // The shadow material is bound to a DrawList in the subtree of the | 
|  | 351     // rendergraph that handles the shadow map generation, so it gets drawn in | 
|  | 352     // that render pass only. | 
|  | 353     transformTable[tt].shape.createDrawElements(g_pack, g_shadowMaterial); | 
|  | 354 | 
|  | 355     transform.translate(transformTable[tt].translation); | 
|  | 356     transform.parent = modelRoot; | 
|  | 357   } | 
|  | 358 } | 
|  | 359 | 
|  | 360 | 
|  | 361 /** | 
|  | 362  * Creates the wireframe frustum showing the shadow map's render volume. | 
|  | 363  */ | 
|  | 364 function createLightShape() { | 
|  | 365   var inverseMatrix = g_math.matrix4.inverse(g_lightViewProjection); | 
|  | 366 | 
|  | 367   // Scale and translate a cube of side length 2 to get a box | 
|  | 368   // that extends from [-1, -1, 0] to [1, 1, 1]. | 
|  | 369   var shape = o3djs.debug.createLineCube( | 
|  | 370                   g_pack, | 
|  | 371                   o3djs.material.createConstantMaterial(g_pack, | 
|  | 372                                                         g_colorViewInfo, | 
|  | 373                                                         [1, 0, 0, 1]), | 
|  | 374                   2, | 
|  | 375                   g_math.matrix4.compose( | 
|  | 376                       g_math.matrix4.translation([0, 0, 0.5]), | 
|  | 377                       g_math.matrix4.scaling([1, 1, 0.5]))); | 
|  | 378 | 
|  | 379   g_lightFrustumTransform = g_pack.createObject('Transform'); | 
|  | 380   g_lightFrustumTransform.localMatrix = inverseMatrix; | 
|  | 381   g_lightFrustumTransform.parent = g_client.root; | 
|  | 382   g_lightFrustumTransform.addShape(shape); | 
|  | 383 } | 
|  | 384 | 
|  | 385 | 
|  | 386 /** | 
|  | 387  * Creates a Phong-shaded, shadowed material based on the given color. | 
|  | 388  */ | 
|  | 389 function createShadowColorMaterial(baseColor) { | 
|  | 390   var material = g_pack.createObject('Material'); | 
|  | 391   material.drawList = g_colorViewInfo.performanceDrawList; | 
|  | 392 | 
|  | 393   material.effect = g_shadowColorEffect; | 
|  | 394   g_shadowColorEffect.createUniformParameters(material); | 
|  | 395 | 
|  | 396   material.getParam('shadowMapSampler').value = g_shadowSampler; | 
|  | 397 | 
|  | 398   material.getParam('ambient').value = g_math.mulScalarVector(0.1, baseColor); | 
|  | 399   material.getParam('diffuse').value = g_math.mulScalarVector(0.8, baseColor); | 
|  | 400   material.getParam('specular').value = [1, 1, 1, 1]; | 
|  | 401   material.getParam('shininess').value = 80; | 
|  | 402 | 
|  | 403   return material; | 
|  | 404 } | 
|  | 405 | 
|  | 406 /** | 
|  | 407  * Binds params for light position, light color and the light view-projection | 
|  | 408  * matrix to all materials in the scene where they apply. | 
|  | 409  */ | 
|  | 410 function initGlobalParams() { | 
|  | 411   var paramSpec = { | 
|  | 412       'lightColor': 'ParamFloat4', | 
|  | 413       'lightWorldPos': 'ParamFloat3', | 
|  | 414       'lightViewProjection': 'ParamMatrix4'}; | 
|  | 415 | 
|  | 416   g_globalParams = o3djs.material.createParams(g_pack, paramSpec); | 
|  | 417   o3djs.material.bindParams(g_pack, g_globalParams); | 
|  | 418 | 
|  | 419   g_globalParams.lightWorldPos.value = g_lightWorldPos; | 
|  | 420   g_globalParams.lightColor.value = g_lightColor; | 
|  | 421 } | 
|  | 422 | 
|  | 423 | 
|  | 424 /** | 
|  | 425  * The keyboard event handler. | 
|  | 426  */ | 
|  | 427 function keyPressed(event) { | 
|  | 428   var keyChar = String.fromCharCode(o3djs.event.getEventKeyChar(event)); | 
|  | 429   keyChar = keyChar.toLowerCase(); | 
|  | 430 | 
|  | 431   var delta = 0.2; | 
|  | 432   switch(keyChar) { | 
|  | 433     case 'a': | 
|  | 434       moveLight([-delta, 0, 0]); | 
|  | 435       break; | 
|  | 436     case 'd': | 
|  | 437       moveLight([delta, 0, 0]); | 
|  | 438       break; | 
|  | 439     case 's': | 
|  | 440       moveLight([0, -delta, 0]); | 
|  | 441       break; | 
|  | 442     case 'w': | 
|  | 443       moveLight([0, delta, 0]); | 
|  | 444       break; | 
|  | 445     case 'i': | 
|  | 446       moveLight([0, 0, delta]); | 
|  | 447       break; | 
|  | 448     case 'o': | 
|  | 449       moveLight([0, 0, -delta]); | 
|  | 450       break; | 
|  | 451 | 
|  | 452     case ' ': | 
|  | 453       toggleView(); | 
|  | 454       break; | 
|  | 455   } | 
|  | 456 } | 
|  | 457 | 
|  | 458 | 
|  | 459 /** | 
|  | 460  * Moves the light by the given vector delta, and updates params so the light | 
|  | 461  * draws in the right spot and the shadows move. | 
|  | 462  */ | 
|  | 463 function moveLight(delta) { | 
|  | 464   g_lightWorldPos = g_math.addVector(g_lightWorldPos, delta); | 
|  | 465   g_globalParams.lightWorldPos.value = g_lightWorldPos; | 
|  | 466   updateLightMatrix(); | 
|  | 467   g_lightFrustumTransform.localMatrix = | 
|  | 468       g_math.matrix4.inverse(g_lightViewProjection); | 
|  | 469 } | 
|  | 470 | 
|  | 471 | 
|  | 472 </script> | 
|  | 473 <script id="shadowShader" type="text/O3DShader"> | 
|  | 474   /** | 
|  | 475    * This shader is for the effect applied in the first render pass, when the | 
|  | 476    * shadow map is created.  The scene is rendered from the perspective of the | 
|  | 477    * light, the grayscale value of each pixel in the rendered image represents | 
|  | 478    * how far away the rendered point is from the light (the lighter, the | 
|  | 479    * farther)  This image gets rendered to a texture, and that texture gets | 
|  | 480    * sampled in the second render pass, when the geometry is drawn to the | 
|  | 481    * screen. | 
|  | 482    */ | 
|  | 483 | 
|  | 484   // The light's wvp matrix | 
|  | 485   float4x4 worldViewProjection : WorldViewProjection; | 
|  | 486 | 
|  | 487   // Input parameters for our vertex shader. | 
|  | 488   struct VertexShaderInput { | 
|  | 489     float4 position : POSITION; | 
|  | 490   }; | 
|  | 491 | 
|  | 492   // Input parameters for our pixel shader. | 
|  | 493   struct PixelShaderInput { | 
|  | 494     float4 position : POSITION; | 
|  | 495     float2 depth : TEXCOORD0; | 
|  | 496   }; | 
|  | 497 | 
|  | 498   /** | 
|  | 499    * The vertex shader simply transforms the input vertices to screen space. | 
|  | 500    */ | 
|  | 501   PixelShaderInput vertexShaderFunction(VertexShaderInput input) { | 
|  | 502     PixelShaderInput output; | 
|  | 503     // Render from the light's perspective. | 
|  | 504     output.position = mul(input.position, worldViewProjection); | 
|  | 505     output.depth = output.position.zw; | 
|  | 506     return output; | 
|  | 507   } | 
|  | 508 | 
|  | 509   /** | 
|  | 510    * The pixel shader returns a shade of gray.  The lighter the shade the | 
|  | 511    * farther that fragment is from the light. | 
|  | 512    */ | 
|  | 513   float4 pixelShaderFunction(PixelShaderInput input): COLOR { | 
|  | 514     // Pixels in the shadowmap store the pixel depth from the light's | 
|  | 515     // perspective in normalized device coordinates. | 
|  | 516     return float4(input.depth.x / input.depth.y); | 
|  | 517   } | 
|  | 518 | 
|  | 519   // #o3d VertexShaderEntryPoint vertexShaderFunction | 
|  | 520   // #o3d PixelShaderEntryPoint pixelShaderFunction | 
|  | 521   // #o3d MatrixLoadOrder RowMajor | 
|  | 522 </script> | 
|  | 523 | 
|  | 524 | 
|  | 525 <script id="shadowColorShader" type="text/O3DShader"> | 
|  | 526   /** | 
|  | 527    * This shader is for the effect applied in the second render pass when the | 
|  | 528    * shadowed shapes are drawn to the screen.  In the pixel shader, the distance | 
|  | 529    * from the rendered point to the camera is compared to the distance encoded | 
|  | 530    * in the shadow map.  If the distance is much greater, the rendered point is | 
|  | 531    * considered to be in shadow and is given a light coefficient of 0. | 
|  | 532    */ | 
|  | 533 | 
|  | 534   float4x4 world : World; | 
|  | 535   float4x4 worldViewProjection : WorldViewProjection; | 
|  | 536   float4x4 worldInverseTranspose : WorldInverseTranspose; | 
|  | 537   float4x4 viewInverse : ViewInverse; | 
|  | 538   float4x4 lightViewProjection; | 
|  | 539   sampler shadowMapSampler; | 
|  | 540 | 
|  | 541   // Parameters for the phong shader. | 
|  | 542   uniform float3 lightWorldPos; | 
|  | 543   uniform float4 lightColor; | 
|  | 544   uniform float4 ambient; | 
|  | 545   uniform float4 diffuse; | 
|  | 546   uniform float4 specular; | 
|  | 547   uniform float shininess; | 
|  | 548 | 
|  | 549   // input parameters for our vertex shader | 
|  | 550   struct VertexShaderInput { | 
|  | 551     float4 position : POSITION; | 
|  | 552     float3 normal : NORMAL; | 
|  | 553   }; | 
|  | 554 | 
|  | 555   // input parameters for our pixel shader | 
|  | 556   struct PixelShaderInput { | 
|  | 557     float4 position : POSITION; | 
|  | 558     float4 projTextureCoords : TEXCOORD0; | 
|  | 559     float4 worldPosition : TEXCOORD1; | 
|  | 560     float3 normal : TEXCOORD2; | 
|  | 561   }; | 
|  | 562 | 
|  | 563   PixelShaderInput vertexShaderFunction(VertexShaderInput input) { | 
|  | 564     PixelShaderInput output; | 
|  | 565 | 
|  | 566     // Transform to homogeneous clip space. | 
|  | 567     output.position = mul(input.position, worldViewProjection); | 
|  | 568 | 
|  | 569     // Compute the projective texture coordinates to project the shadow map | 
|  | 570     // onto the scene. | 
|  | 571     float4x4 worldLightViewProjection = mul(world, lightViewProjection); | 
|  | 572     output.projTextureCoords = mul(input.position, worldLightViewProjection); | 
|  | 573     output.worldPosition = mul(input.position, world); | 
|  | 574     output.normal = mul(float4(input.normal, 0), worldInverseTranspose).xyz; | 
|  | 575 | 
|  | 576     return output; | 
|  | 577   } | 
|  | 578 | 
|  | 579   float4 pixelShaderFunction(PixelShaderInput input): COLOR { | 
|  | 580     float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition); | 
|  | 581     float3 surfaceToView = normalize(viewInverse[3].xyz - input.worldPosition); | 
|  | 582     float3 normal = normalize(input.normal); | 
|  | 583     float3 halfVector = normalize(surfaceToLight + surfaceToView); | 
|  | 584     float4 litResult = lit(dot(normal, surfaceToLight), | 
|  | 585                            dot(normal, halfVector), shininess); | 
|  | 586     float4 outColor = ambient; | 
|  | 587     float4 projCoords = input.projTextureCoords; | 
|  | 588 | 
|  | 589     // Convert texture coords to [0, 1] range. | 
|  | 590     projCoords.xy /= projCoords.w; | 
|  | 591     projCoords.x =  0.5 * projCoords.x + 0.5; | 
|  | 592     projCoords.y = -0.5 * projCoords.y + 0.5; | 
|  | 593 | 
|  | 594     // Compute the pixel depth for shadowing. | 
|  | 595     float depth = projCoords.z / projCoords.w; | 
|  | 596 | 
|  | 597     // If the rednered point is farter from the light than the distance encoded | 
|  | 598     // in the shadow map, we give it a light coefficient of 0. | 
|  | 599     float light = tex2D(shadowMapSampler, projCoords.xy).r + 0.008 > depth; | 
|  | 600 | 
|  | 601     // Make the illuninated area a round spotlight shape just for fun. | 
|  | 602     // Comment this line out to see just the shadows. | 
|  | 603     light *= 1 - smoothstep(0.45, 0.5, length(projCoords - float2(0.5, 0.5))); | 
|  | 604 | 
|  | 605     outColor += light * lightColor * | 
|  | 606         (diffuse * litResult.y + specular * litResult.z); | 
|  | 607     return outColor; | 
|  | 608   } | 
|  | 609 | 
|  | 610   // #o3d VertexShaderEntryPoint vertexShaderFunction | 
|  | 611   // #o3d PixelShaderEntryPoint pixelShaderFunction | 
|  | 612   // #o3d MatrixLoadOrder RowMajor | 
|  | 613 </script> | 
|  | 614 | 
|  | 615 | 
|  | 616 </head> | 
|  | 617 <body> | 
|  | 618 <h1>Shadow Maps</h1> | 
|  | 619 This sample implements a basic shadow map. | 
|  | 620 <br/> | 
|  | 621 <!-- Start of O3D plugin --> | 
|  | 622 <div id="o3d" style="width: 600px; height: 600px;"></div> | 
|  | 623 <!-- End of O3D plugin --> | 
|  | 624 Use A, S, D, W, I and O to move the light. | 
|  | 625 Press spacebar to see the shadow map. | 
|  | 626 </body> | 
|  | 627 </html> | 
| OLD | NEW | 
|---|