diff options
author | petersont@google.com <petersont@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-12 21:00:00 +0000 |
---|---|---|
committer | petersont@google.com <petersont@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-12 21:00:00 +0000 |
commit | e512583b79e366db399a6566964412bb6e5af823 (patch) | |
tree | e15e3c05e273a0366caf2fcbc5689ae377b3e877 /o3d/samples/o3d-webgl-samples | |
parent | a37a999f87eed77b08e7e1b6bdb86d406f31ea9b (diff) | |
download | chromium_src-e512583b79e366db399a6566964412bb6e5af823.zip chromium_src-e512583b79e366db399a6566964412bb6e5af823.tar.gz chromium_src-e512583b79e366db399a6566964412bb6e5af823.tar.bz2 |
Added textures, texture samplers and render targets to o3d-webgl. Also fixed bugs, added calls to parent class constructor to classes that didn't have them before, added a few demos to exhibit/test textures and render surfaces.
Review URL: http://codereview.chromium.org/856004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41482 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/samples/o3d-webgl-samples')
-rw-r--r-- | o3d/samples/o3d-webgl-samples/hellocube-colors.html | 343 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl-samples/hellocube-textures.html | 433 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl-samples/shadow-map.html | 636 |
3 files changed, 1412 insertions, 0 deletions
diff --git a/o3d/samples/o3d-webgl-samples/hellocube-colors.html b/o3d/samples/o3d-webgl-samples/hellocube-colors.html new file mode 100644 index 0000000..2af5699 --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/hellocube-colors.html @@ -0,0 +1,343 @@ +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<!-- +This sample creates an O3D area with a spinning cube. The user +can change the color of the cube by clicking on one of the buttons that appear +at the bottom of the page. This sample demonstrates how to use O3D Params +to change the values of uniform parameters used by shaders. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Hello Square Colors: Getting started with O3D, take 2. +</title> +<script type="text/javascript" src="../o3d-webgl/base.js"></script> +<script type="text/javascript" src="../o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.base.o3d = o3d; +o3djs.require('o3djs.webgl'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); + +// Events +// Run the init() function once the page has finished loading. +// Run the uninit() function when the page has is unloaded. +window.onload = init; +window.onunload = uninit; + +// global variables +var g_o3d; +var g_math; +var g_pack; +var g_client; +var g_clock = 0; +var g_timeMult = 1; +var g_finished = false; // for selenium testing + +var g_cubeTransform; +var g_cubeColorParam; + +/** + * Changes the color of the cube. + * @param red red component of the color + * @param green green component of the color + * @param blue blue component of the color + */ +function changeColor(red, green, blue) { + // Set the rgb color values and alpha = 1 + g_cubeColorParam.value = [red, green, blue, 1]; +} + +/** + * Creates an O3D shape representing a cube. The shape consists of + * a single primitive with eight vertices and 12 triangles (two for each face + * of the cube). + * @param {o3d.Material} material the material used by the primitive. + * @return {o3d.Shape} The Shape object created. + */ +function createCube(material) { + // Create a Shape object for the mesh. + var cubeShape = g_pack.createObject('Shape'); + + // Create the Primitive that will contain the geometry data for + // the cube. + var cubePrimitive = g_pack.createObject('Primitive'); + + // Create a StreamBank to hold the streams of vertex data. + var streamBank = g_pack.createObject('StreamBank'); + + // Assign the material that was passed in to the primitive. + cubePrimitive.material = material; + + // Assign the Primitive to the Shape. + cubePrimitive.owner = cubeShape; + + // Assign the StreamBank to the Primitive. + cubePrimitive.streamBank = streamBank; + + // The cube is made of 12 triangles. There's eight vertices in total which + // are shared between the face triangles. + cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST; + cubePrimitive.numberPrimitives = 12; // 12 triangles + cubePrimitive.numberVertices = 8; // 8 vertices in total + + // Generate the draw element for the cube primitive. + cubePrimitive.createDrawElement(g_pack, null); + + // Create a javascript array that stores the X, Y and Z coordinates of each + // of the 8 corners of the cube. + var positionArray = [ + -0.5, -0.5, 0.5, // vertex 0 + 0.5, -0.5, 0.5, // vertex 1 + -0.5, 0.5, 0.5, // vertex 2 + 0.5, 0.5, 0.5, // vertex 3 + -0.5, 0.5, -0.5, // vertex 4 + 0.5, 0.5, -0.5, // vertex 5 + -0.5, -0.5, -0.5, // vertex 6 + 0.5, -0.5, -0.5 // vertex 7 + ]; + + // The following array defines how vertices are to be put together to form + // the triangles that make up the cube's faces. In the index array, every + // three elements define a triangle. So for example vertices 0, 1 and 2 + // make up the first triangle, vertices 2, 1 and 3 the second one, etc. + var indicesArray = [ + 0, 1, 2, // face 1 + 2, 1, 3, + 2, 3, 4, // face 2 + 4, 3, 5, + 4, 5, 6, // face 3 + 6, 5, 7, + 6, 7, 0, // face 4 + 0, 7, 1, + 1, 7, 3, // face 5 + 3, 7, 5, + 6, 0, 4, // face 6 + 4, 0, 2 + ]; + + // Create buffers containing the vertex data. + var positionsBuffer = g_pack.createObject('VertexBuffer'); + var positionsField = positionsBuffer.createField('FloatField', 3); + positionsBuffer.set(positionArray); + + var indexBuffer = g_pack.createObject('IndexBuffer'); + indexBuffer.set(indicesArray); + + // Associate the positions Buffer with the StreamBank. + streamBank.setVertexStream( + g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions + 0, // semantic index: First (and only) position stream + positionsField, // field: the field this stream uses. + 0); // start_index: How many elements to skip in the + // field. + + // Associate the triangle indices Buffer with the primitive. + cubePrimitive.indexBuffer = indexBuffer; + + return cubeShape; +} + +/** + * This method gets called every time O3D renders a frame. Here's where + * we update the cube's transform to make it spin. + * @param {o3d.RenderEvent} renderEvent The render event object that gives + * us the elapsed time since the last time a frame was rendered. + */ +function renderCallback(renderEvent) { + g_clock += renderEvent.elapsedTime * g_timeMult; + // Rotate the cube around the Y axis. + g_cubeTransform.identity(); + g_cubeTransform.rotateY(2.0 * g_clock); +} + +/** + * Creates the client area. + */ +function init() { + o3djs.webgl.makeClients(initStep2); +} + +/** + * Initializes O3D, creates the cube and sets up the transform and + * render graphs. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initializes global variables and libraries. + var o3dElement = clientElements[0]; + g_client = o3dElement.client; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); + + // Create the render graph for a view. + var viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set up a perspective projection. + viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree fov. + g_client.width / g_client.height, + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // cube is located. + viewInfo.drawContext.view = g_math.matrix4.lookAt( + [0, 1, 5], // eye + [0, 0, 0], // target + [0, 1, 0]); // up + + // Create an Effect object and initialize it using the shaders from the + // text area. + var cubeEffect = g_pack.createObject('Effect'); + var vertexShaderString = document.getElementById('vshader').value; + var pixelShaderString = document.getElementById('pshader').value; + cubeEffect.loadVertexShaderFromString(vertexShaderString); + cubeEffect.loadPixelShaderFromString(pixelShaderString); + + // Create a Material for the mesh. + var cubeMaterial = g_pack.createObject('Material'); + + // Set the material's drawList. + cubeMaterial.drawList = viewInfo.performanceDrawList; + + // Apply our effect to this material. The effect tells the 3D hardware + // which shaders to use. + cubeMaterial.effect = cubeEffect; + + // Create an O3D Param on the material for every uniform used by the + // shader. + cubeEffect.createUniformParameters(cubeMaterial); + + // Get the color parameter from the material and set its value to red. + g_cubeColorParam = cubeMaterial.getParam('color'); + g_cubeColorParam.value = [1, 0, 0, 1]; + + // Create the Shape for the cube mesh and assign its material. + var cubeShape = createCube(cubeMaterial); + + // Create a new transform and parent the Shape under it. + g_cubeTransform = g_pack.createObject('Transform'); + g_cubeTransform.addShape(cubeShape); + + // Parent the cube's transform to the client root. + g_cubeTransform.parent = g_client.root; + + // Set our render callback for animation. + // This sets a function to be executed every time a frame is rendered. + g_client.setRenderCallback(renderCallback); + + g_finished = true; // for selenium testing. +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function uninit() { + if (g_client) { + g_client.cleanup(); + } +} + +</script> +</head> +<body> +<h1>Hello Square: Colors</h1> +This example shows how to use parameters to the color output of a shader. +<br/> + + +<!-- Start of O3D plugin --> +<div id="o3d" width="600px" height="600px"></div> +<!-- End of O3D plugin --> + +<form name="default_form" action="#" method="get"> + <p> + Change color: + <input type="button" value="Red" onclick="changeColor(1,0,0);" /> + <input type="button" value="Green" onclick="changeColor(0,1,0);" /> + <input type="button" value="Blue" onclick="changeColor(0,0,1);" /> + <input type="button" value="Yellow" onclick="changeColor(1,1,0);" /> + <input type="button" value="Cyan" onclick="changeColor(0,1,1);" /> + <input type="button" value="Purple" onclick="changeColor(1,0,1);" /> + <input type="button" value="White" onclick="changeColor(1,1,1);" /> + </p> +</form> + +<!-- Don't render the textarea --> +<div style="display:none"> +<!-- Start of effect --> +<textarea id="vshader"> + // World View Projection matrix that will transform the input vertices + // to screen space. + uniform mat4 worldViewProjection; + + // input parameters for our vertex shader + attribute vec4 position; + + /** + * The vertex shader simply transforms the input vertices to screen space. + */ + void main() { + // Multiply the vertex positions by the worldViewProjection matrix to + // transform them to screen space. + gl_Position = worldViewProjection * position; + } + +</textarea> +<textarea id="pshader"> + // Color to draw with. + uniform vec4 color; + + /** + * This pixel shader just returns the color red. + */ + void main() { + gl_FragColor = color; + } + + // Here we tell our effect file *which* functions are + // our vertex and pixel shaders. +</textarea> +<!-- End of effect --> +</div> +</body> +</html> diff --git a/o3d/samples/o3d-webgl-samples/hellocube-textures.html b/o3d/samples/o3d-webgl-samples/hellocube-textures.html new file mode 100644 index 0000000..0c20cf4 --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/hellocube-textures.html @@ -0,0 +1,433 @@ +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<!-- +This sample creates an O3D area with a textured cube in the middle. The +user can specify the URL where the texture image will be picked from. +This sample is a simple demonstration of texture usage in O3D. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Hello Square Textures: Getting started with O3D, take 3. +</title> +<script type="text/javascript" src="../o3d-webgl/base.js"></script> +<script type="text/javascript" src="../o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.base.o3d = o3d; +o3djs.require('o3djs.webgl'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.io'); + +// Events +// Run the init() function once the page has finished loading. +// Run the uninit() function when the page is unloaded. +window.onload = init; +window.onunload = uninit; + +// global variables +var g_o3d; +var g_math; +var g_pack; +var g_client; +var g_cubeTransform; +var g_sampler; +var g_clock = 0; +var g_timeMult = 1; +var g_finished = false; // for selenium testing +var g_textureLoadDenied = false; // also for selenium testing + +/** + * Creates an O3D shape representing a cube. + * @param {o3d.Material} material the material used by the primitive. + * @return {o3d.Shape} The Shape object created. + */ +function createCube(material) { + // Create a Shape object for the mesh. + var cubeShape = g_pack.createObject('Shape'); + + // Create the Primitive that will contain the geometry data for + // the cube. + var cubePrimitive = g_pack.createObject('Primitive'); + + // Create a StreamBank to hold the streams of vertex data. + var streamBank = g_pack.createObject('StreamBank'); + + // Assign the material that was passed in to the primitive. + cubePrimitive.material = material; + + // Assign the Primitive to the Shape. + cubePrimitive.owner = cubeShape; + + // Assign the StreamBank to the Primitive. + cubePrimitive.streamBank = streamBank; + + // The cube is made of 12 triangles (6 faces x 2 triangles per face) + cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST; + cubePrimitive.numberPrimitives = 12; // 12 triangles + + // Vertices used by each triangle must specify both a position and texture + // coordinates. We cannot share vertices between adjacent cube faces since + // while their positions are the same, their texture coordinates are + // different. We therefore create 24 vertices, 4 for each of the cube's + // six faces. + cubePrimitive.numberVertices = 24; + + // Generate the draw element for the cube primitive. + cubePrimitive.createDrawElement(g_pack, null); + + // Create a javascript array that stores the X, Y and Z coordinates of each + // of the 24 vertices used by the cube. + var positionArray = [ + -0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, 0.5, 0.5, + -0.5, 0.5, 0.5, + -0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, -0.5, + -0.5, 0.5, -0.5, + -0.5, 0.5, -0.5, + 0.5, 0.5, -0.5, + 0.5, -0.5, -0.5, + -0.5, -0.5, -0.5, + -0.5, -0.5, -0.5, + 0.5, -0.5, -0.5, + 0.5, -0.5, 0.5, + -0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, -0.5, -0.5, + 0.5, 0.5, -0.5, + 0.5, 0.5, 0.5, + -0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + -0.5, 0.5, 0.5, + -0.5, 0.5, -0.5 + ]; + + // The following array stores the texture coordinates (u, v) for each vertex. + // These coordinates are used by the shader when displaying the texture image + // on the mesh triangles. + var texCoordsArray = [ + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 1, 0, + 1, 1, + 0, 1 + ]; + + // The following array defines how vertices are to be put together to form + // the triangles that make up the cube's faces. In the index array, every + // three elements define a triangle. So for example vertices 0, 1 and 2 + // make up the first triangle, vertices 0, 2 and 3 the second one, etc. + var indicesArray = [ + 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 buffers containing the vertex data. + var positionsBuffer = g_pack.createObject('VertexBuffer'); + var positionsField = positionsBuffer.createField('FloatField', 3); + positionsBuffer.set(positionArray); + + var texCoordsBuffer = g_pack.createObject('VertexBuffer'); + var texCoordsField = texCoordsBuffer.createField('FloatField', 2); + texCoordsBuffer.set(texCoordsArray); + + var indexBuffer = g_pack.createObject('IndexBuffer'); + indexBuffer.set(indicesArray); + + // Associate the positions buffer with the StreamBank. + streamBank.setVertexStream( + g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions + 0, // semantic index: First (and only) position stream + positionsField, // field: the field this stream uses. + 0); // start_index: How many elements to skip in the + // field. + + // Associate the texture coordinates buffer with the primitive. + streamBank.setVertexStream( + g_o3d.Stream.TEXCOORD, // semantic + 0, // semantic index + texCoordsField, // field + 0); // start_index + + // Associate the triangle indices Buffer with the primitive. + cubePrimitive.indexBuffer = indexBuffer; + + return cubeShape; +} + +/** + * This method gets called every time O3D renders a frame. Here's where + * we update the cube's transform to make it spin. + * @param {o3d.RenderEvent} renderEvent The render event object that gives + * us the elapsed time since the last time a frame was rendered. + */ +function renderCallback(renderEvent) { + g_clock += renderEvent.elapsedTime * g_timeMult; + // Rotate the cube around the Y axis. + g_cubeTransform.identity(); + g_cubeTransform.rotateY(2.0 * g_clock); +} + +/** + * Creates the client area. + */ +function init() { + o3djs.webgl.makeClients(initStep2); +} + +/** + * Initializes O3D, creates the quad and sets up the transform and + * render graphs. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Set the texture URL. + path = '../assets/texture_b3.jpg'; + var url = document.getElementById("url").value = path; + + // Initialize global variables and libraries. + var o3dElement = clientElements[0]; + g_client = o3dElement.client; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); + + // Create the render graph for a view. + var viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set up a perspective projection. + viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree fov. + g_client.width / g_client.height, + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // cube is located. + viewInfo.drawContext.view = g_math.matrix4.lookAt( + [0, 1, 5], // eye + [0, 0, 0], // target + [0, 1, 0]); // up + + // Create an Effect object and initialize it using the shaders from the + // text area. + var cubeEffect = g_pack.createObject('Effect'); + var vertexShaderString = document.getElementById('vshader').value; + var pixelShaderString = document.getElementById('pshader').value; + cubeEffect.loadVertexShaderFromString(vertexShaderString); + cubeEffect.loadPixelShaderFromString(pixelShaderString); + + // Create a Material for the mesh. + var cubeMaterial = g_pack.createObject('Material'); + + // Set the material's drawList. + cubeMaterial.drawList = viewInfo.performanceDrawList; + + // Apply our effect to this material. The effect tells the 3D hardware + // which shaders to use. + cubeMaterial.effect = cubeEffect; + + // Create an O3D Param on the material for every uniform used by the + // shader. + cubeEffect.createUniformParameters(cubeMaterial); + + // Get the material's sampler parameter so that we can set the texture value + // to it. + var samplerParam = cubeMaterial.getParam('texSampler0'); + + // Create a Sampler object and set the min filtering to ANISOTROPIC. This + // will improve the quality of the rendered texture when viewed at an angle. + g_sampler = g_pack.createObject('Sampler'); + g_sampler.minFilter = g_o3d.Sampler.ANISOTROPIC; + g_sampler.maxAnisotropy = 4; + samplerParam.value = g_sampler; + + // Create the Shape for the cube mesh and assign its material. + var cubeShape = createCube(cubeMaterial); + + // Create a new transform and parent the Shape under it. + g_cubeTransform = g_pack.createObject('Transform'); + g_cubeTransform.addShape(cubeShape); + + // Note that we don't parent the transform until the texture is + // succesfully loaded because we don't want the system + // to try the draw the shape without its required texture. + + // Set our render callback for animation. + // This sets a function to be executed every time a frame is rendered. + g_client.setRenderCallback(renderCallback); + + // Set the initial texture. + changeTexture(); +} + +/** + * Fetches the bitmap pointed to by the URL supplied by the user, creates + * an O3D Texture object with it updates the Sampler used by the material + * to point to the newly created texture. + */ +function changeTexture() { + var textureUrl = document.getElementById('url').value; + o3djs.io.loadTexture(g_pack, textureUrl, function(texture, exception) { + // Remove the currently used texture from the pack so that when it's not + // referenced anymore, it can get destroyed. + if (g_sampler.texture) + g_pack.removeObject(g_sampler.texture); + + // Because the loading is asynchronous an exception is not thrown but is + // instead passed on failure. + if (exception) { + g_sampler.texture = null; + + g_textureLoadDenied = true; // for selenium testing. + } else { + // Set the texture on the sampler object to the newly created texture + // object returned by the request. + g_sampler.texture = texture; + + // We can now safely add the cube transform to the root of the + // scenegraph since it now has a valid texture. If the transform + // is already parented under the root, the call will have no effect. + g_cubeTransform.parent = g_client.root; + + g_finished = true; // for selenium testing. + } + }); +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function uninit() { + if (g_client) { + g_client.cleanup(); + } +} + +</script> +</head> +<body> +<h1>Hello Cube: Textures</h1> +This example shows how texture map a cube using an image fetched from a URL. +<br/> + + +<!-- Start of O3D plugin --> +<div id="o3d" width="600px" height="600px"></div> +<!-- End of O3D plugin --> +<br /> +Image URL: <input type="text" id="url" size="100"> +<input type="button" id="updateButton" onclick="changeTexture();" value="Update Texture"><BR> + +<!-- Don't render the textarea --> +<div style="display:none"> +<!-- Start of effect --> +<textarea id="vshader"> + // World View Projection matrix that will transform the input vertices + // to screen space. + uniform mat4 worldViewProjection; + + // input parameters for our vertex shader + attribute vec4 position; + attribute vec2 texCoord0; + + varying vec2 uvs; + + /** + * The vertex shader simply transforms the input vertices to screen space. + */ + void main() { + // Multiply the vertex positions by the worldViewProjection matrix to + // transform them to screen space. + gl_Position = worldViewProjection * position; + uvs = texCoord0; + } + +</textarea> +<textarea id="pshader"> + + // Color to draw with. + uniform sampler2D texSampler0; + + varying vec2 uvs; + + /** + * This pixel shader just returns the color red. + */ + void main() { + gl_FragColor = texture2D(texSampler0, uvs); + } +</textarea> +<!-- End of effect --> +</div> +</body> +</html> diff --git a/o3d/samples/o3d-webgl-samples/shadow-map.html b/o3d/samples/o3d-webgl-samples/shadow-map.html new file mode 100644 index 0000000..2bf81cc --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/shadow-map.html @@ -0,0 +1,636 @@ +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<!-- +This sample uses a custom render graph to implement a basic shadow map +algorithm. + +The technique works by rendering the scene in two passes. The first pass +renders the geometry in the scene with a shader that colors each pixel a shade +of gray representing how far the rendered point is from the light source. That +image, the shadow map, is rendered to a texture, and then the second (visible) +render pass samples it to determine which points in the scene are in shaodow. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Shadow Mapping +</title> +<script type="text/javascript" src="../o3d-webgl/base.js"></script> +<script type="text/javascript" src="../o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.base.o3d = o3d; +o3djs.require('o3djs.webgl'); +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.primitives'); +o3djs.require('o3djs.effect'); +o3djs.require('o3djs.debug'); +o3djs.require('o3djs.material'); + +// The initClient() function runs when the page has finished loading. +window.onload = initClient; + +// global variables +var g_o3dElement; +var g_client; +var g_o3d; +var g_math; +var g_pack; +var g_colorViewInfo; +var g_shadowViewInfo; +var g_shadowTexture; +var g_shadowMaterial; +var g_colorEffect; +var g_shadowSampler; +var g_lightViewProjection; +var g_lightFrustumTransform; +var g_globalParams = { }; +var g_viewFromLight = false; + +var g_renderSurfaceSet; +var g_colorPassRenderRoot; + +var g_lightWorldPos = [5, 10, 0]; +var g_lightColor = [1, 1, 1, 1]; +var g_eyePosition = [1, 6, 20]; +var g_targetPosition = [0, 2, 0]; + +// constants. +var SHADOW_MAP_WIDTH = 512; +var SHADOW_MAP_HEIGHT = 512; + +var g_finished = false; // for selenium testing. + + +/** + * Creates the client area. + */ +function initClient() { + o3djs.webgl.makeClients(main, 'FloatingPointTextures', true); +} + + +/** + * Initializes global variables, positions camera, draws shapes. + * @param {Array} clientElements Array of o3d object elements. + */ +function main(clientElements) { + // Init global variables. + initGlobals(clientElements); + + // Set up the rendergraph. + initRenderGraph(); + + // Load effects, bind material parameters. + initMaterials(); + + // Add the shapes to the transform graph. + createShapes(); + + // Set up the view and projection transformations for the camera. + updateCamera(); + + // Init global parameters. initGlobalParams() searches all materials in order + // to bind parameters, so it must be called after initMaterials() + initGlobalParams(); + + // Set the view and projection transformations for the light. + updateLightMatrix(); + + // Create the light that gets drawn. + createLightShape(); + + // Execute keyPressed() when we detect a keypress on the window or + // on the o3d object. + window.document.onkeypress = keyPressed; + g_o3dElement.onkeypress = keyPressed; + + //toggleView(); + + g_finished = true; // for selenium testing. +} + + +/** + * Initializes global variables and libraries. + */ +function initGlobals(clientElements) { + g_o3dElement = clientElements[0]; + g_client = g_o3dElement.client; + g_o3d = g_o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); +} + + +/** + * Sets up the render graph. Builds a basic view for the camera and the light + * point of view, arranges for the view from the light to be rendered to a + * texture for the shadow map. Unlike the basic render graph created by the + * the utility function o3djs.rendergraph.createBasicView, to render the shadow + * map and then render the scene, we need two subtrees of the render graph, one + * for shadow map render pass and one to draw the scene. + */ +function initRenderGraph() { + // The children of any one node in the render graph get traversed in order by + // priority. Here, we're forcing the shadow map to get rendered first by + // by giving its render root lower priority. + var shadowPassRenderRoot = g_pack.createObject('RenderNode'); + shadowPassRenderRoot.priority = 0; + + g_colorPassRenderRoot = g_pack.createObject('RenderNode'); + g_colorPassRenderRoot.priority = 1; + + g_colorPassRenderRoot.parent = g_client.renderGraphRoot; + + // Create the texture that will store the depth information. + g_shadowTexture = g_pack.createTexture2D(SHADOW_MAP_WIDTH, + SHADOW_MAP_HEIGHT, + g_o3d.Texture.ABGR32F, + 1, + true); + var renderSurface = g_shadowTexture.getRenderSurface(0); + + // Create the depth-stencil buffer required when rendering the teapot. + var depthSurface = g_pack.createDepthStencilSurface(SHADOW_MAP_WIDTH, + SHADOW_MAP_HEIGHT); + + shadowPassRenderRoot.parent = g_client.renderGraphRoot; + + g_renderSurfaceSet = g_pack.createObject('RenderSurfaceSet'); + g_renderSurfaceSet.renderSurface = renderSurface; + g_renderSurfaceSet.renderDepthStencilSurface = depthSurface; + + g_renderSurfaceSet.parent = shadowPassRenderRoot; + + // Create a render sub-graph for the shadow map generation. + g_shadowViewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_renderSurfaceSet, + [1, 1, 1, 1]); + + // Create a render sub-graph for the regular pass. + g_colorViewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_colorPassRenderRoot, + [0, 0, 0, 1]); +} + + +/** + * Switches between the camera and light point of view. + */ +function toggleView() { + if (g_viewFromLight) { + g_shadowViewInfo.root.parent = g_renderSurfaceSet; + g_colorPassRenderRoot.parent = g_client.renderGraphRoot; + g_viewFromLight = false; + } else { + g_shadowViewInfo.root.parent = g_client.renderGraphRoot; + g_colorPassRenderRoot.parent = null; + g_viewFromLight = true; + } +} + +/** + * Creates a material to be put on all shapes in the scene for the shadow pass, + * and loads effects for materials in the scene. Other materials are created + * on the fly as the shapes are created. + */ +function initMaterials() { + var colorVertexShader = document.getElementById('colorVertexShader').text; + var colorPixelShader = document.getElementById('colorPixelShader').text; + + g_colorEffect = g_pack.createObject('Effect'); + g_colorEffect.loadVertexShaderFromString(colorVertexShader); + g_colorEffect.loadPixelShaderFromString(colorPixelShader); + + var shadowVertexShader = document.getElementById('shadowVertexShader').text; + var shadowPixelShader = document.getElementById('shadowPixelShader').text; + + g_shadowMaterial = g_pack.createObject('Material'); + g_shadowMaterial.drawList = g_shadowViewInfo.performanceDrawList; + + var shadowEffect = g_pack.createObject('Effect'); + shadowEffect.loadVertexShaderFromString(shadowVertexShader); + shadowEffect.loadPixelShaderFromString(shadowPixelShader); + + g_shadowMaterial.effect = shadowEffect; + shadowEffect.createUniformParameters(g_shadowMaterial); + + g_shadowSampler = g_pack.createObject('Sampler'); + g_shadowSampler.texture = g_shadowTexture; + g_shadowSampler.minFilter = g_o3d.Sampler.POINT; + g_shadowSampler.magFilter = g_o3d.Sampler.POINT; + g_shadowSampler.mipFilter = g_o3d.Sampler.POINT; + g_shadowSampler.addressModeU = g_o3d.Sampler.BORDER; + g_shadowSampler.addressModeV = g_o3d.Sampler.BORDER; + g_shadowSampler.borderColor = [1, 1, 1, 1]; + +} + + +/** + * Sets up reasonable view and projection matrices. + */ +function updateCamera() { + // Set up a perspective transformation for the projection. + g_colorViewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree frustum. + g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // cube is located. + g_colorViewInfo.drawContext.view = g_math.matrix4.lookAt( + g_eyePosition, // eye + g_targetPosition, // target + [0, 1, 0]); // up +} + + +/** + * Computes the view and projection matrices from the point of view of the + * light. Sets the lightViewProjection parameter so the color shader can access + * it. + */ +function updateLightMatrix() { + // The perspective projection matrix for the light. + var lightProjection = g_math.matrix4.perspective( + g_math.degToRad(45), // 45 degree fov. + SHADOW_MAP_WIDTH / SHADOW_MAP_HEIGHT, // Aspect ratio. + 4, // Near plane. + 20); // Far plane. + + var adjustedProjection = + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, -1, 1]]; + o3d.Transform.compose( + adjustedProjection, lightProjection, adjustedProjection); + + // Make the light point toward the origin + var lightView = g_math.matrix4.lookAt( + g_lightWorldPos, // light + [0, 0, 0], // target + [1, 0, 0]); // up + + g_lightViewProjection = g_math.matrix4.composition( + lightProjection, lightView); + + g_shadowViewInfo.drawContext.projection = lightProjection; + g_shadowViewInfo.drawContext.view = lightView; + g_globalParams.lightViewProjection.value = g_lightViewProjection; +} + + +/** + * Creates shapes using the primitives utility library, and adds them to the + * transform graph at the root node. + */ +function createShapes() { + // A green phong-shaded material for the cube. + var cubeMaterial = createColorMaterial([0.2, 0.5, 0, 1]); + + // The cube shape. + var cube = o3djs.primitives.createCube( + g_pack, + cubeMaterial, + 2); // The length of each side of the cube. + + // A red phong-shaded material for the sphere. + var sphereMaterial = createColorMaterial([0.7, 0.2, 0.1, 1]); + + // The sphere shape. + var sphere = o3djs.primitives.createSphere( + g_pack, sphereMaterial, 0.5, 50, 50); + + // A blue phong-shaded material for the plane. + var planeMaterial = createColorMaterial([0, 0.3, 0.5, 1]); + + // The plane shape. + var plane = o3djs.primitives.createPlane( + g_pack, + planeMaterial, + 20, // Width. + 20, // Depth. + 1, // Horizontal subdivisions. + 1); // Vertical subdivisions. + + // Associate to each shape, a translation vector. + var transformTable = [ + {shape: cube, translation: [0, 1, 0]}, + {shape: sphere, translation: [0.5, 2.5, 0]}, + {shape: plane, translation: [0, 0, 0]} + ]; + + // Add the shapes to the transform graph with the translation. + var modelRoot = g_pack.createObject('Transform'); + modelRoot.parent = g_client.root; + for (var tt = 0; tt < transformTable.length; ++tt) { + var transform = g_pack.createObject('Transform'); + transform.addShape(transformTable[tt].shape); + // The shadow material is bound to a DrawList in the subtree of the + // rendergraph that handles the shadow map generation, so it gets drawn in + // that render pass only. + transformTable[tt].shape.createDrawElements(g_pack, g_shadowMaterial); + + transform.translate(transformTable[tt].translation); + transform.parent = modelRoot; + } +} + + +/** + * Creates the wireframe frustum showing the shadow map's render volume. + */ +function createLightShape() { + var inverseMatrix = g_math.matrix4.inverse(g_lightViewProjection); + +/* + // Scale and translate a cube of side length 2 to get a box + // that extends from [-1, -1, 0] to [1, 1, 1]. + var shape = o3djs.debug.createLineCube( + g_pack, + o3djs.material.createConstantMaterial(g_pack, + g_colorViewInfo, + [1, 0, 0, 1]), + 2, + g_math.matrix4.compose( + g_math.matrix4.translation([0, 0, 0.5]), + g_math.matrix4.scaling([1, 1, 0.5])));*/ + + g_lightFrustumTransform = g_pack.createObject('Transform'); + g_lightFrustumTransform.localMatrix = inverseMatrix; + g_lightFrustumTransform.parent = g_client.root; + //g_lightFrustumTransform.addShape(shape); +} + + +/** + * Creates a Phong-shaded, shadowed material based on the given color. + */ +function createColorMaterial(baseColor) { + var material = g_pack.createObject('Material'); + material.drawList = g_colorViewInfo.performanceDrawList; + + material.effect = g_colorEffect; + g_colorEffect.createUniformParameters(material); + + material.getParam('shadowMapSampler').value = g_shadowSampler; + + material.getParam('ambient').value = g_math.mulScalarVector(0.1, baseColor); + material.getParam('diffuse').value = g_math.mulScalarVector(0.8, baseColor); + material.getParam('specular').value = [1, 1, 1, 1]; + material.getParam('shininess').value = 20; + + return material; +} + +/** + * Binds params for light position, light color and the light view-projection + * matrix to all materials in the scene where they apply. + */ +function initGlobalParams() { + var paramSpec = { + 'lightColor': 'ParamFloat4', + 'lightWorldPos': 'ParamFloat3', + 'lightViewProjection': 'ParamMatrix4'}; + + g_globalParams = o3djs.material.createParams(g_pack, paramSpec); + o3djs.material.bindParams(g_pack, g_globalParams); + + g_globalParams.lightWorldPos.value = g_lightWorldPos; + g_globalParams.lightColor.value = g_lightColor; +} + + +/** + * The keyboard event handler. + */ +function keyPressed(event) { + var keyChar = String.fromCharCode(o3djs.event.getEventKeyChar(event)); + keyChar = keyChar.toLowerCase(); + + var delta = 0.2; + switch(keyChar) { + case 'a': + moveLight([-delta, 0, 0]); + break; + case 'd': + moveLight([delta, 0, 0]); + break; + case 's': + moveLight([0, -delta, 0]); + break; + case 'w': + moveLight([0, delta, 0]); + break; + case 'i': + moveLight([0, 0, delta]); + break; + case 'o': + moveLight([0, 0, -delta]); + break; + + case ' ': + toggleView(); + break; + } +} + +/** + * Moves the light by the given vector delta, and updates params so the light + * draws in the right spot and the shadows move. + */ +function moveLight(delta) { + g_lightWorldPos = g_math.addVector(g_lightWorldPos, delta); + g_globalParams.lightWorldPos.value = g_lightWorldPos; + updateLightMatrix(); + g_lightFrustumTransform.localMatrix = + g_math.matrix4.inverse(g_lightViewProjection); +} + + +</script> + + + +<script id="shadowVertexShader" type="text/glsl"> + /** + * This shader is for the effect applied in the first render pass, when the + * shadow map is created. The scene is rendered from the perspective of the + * light, the grayscale value of each pixel in the rendered image represents + * how far away the rendered point is from the light (the lighter, the + * farther) This image gets rendered to a texture, and that texture gets + * sampled in the second render pass, when the geometry is drawn to the + * screen. + */ + attribute vec4 position; + uniform mat4 worldViewProjection; + + varying vec4 vposition; + + void main() { + vposition = worldViewProjection * position; + gl_Position = vposition; + } +</script> +<script id="shadowPixelShader" type="text/glsl"> + varying vec4 vposition; + + void main() { + vec4 color; + vec3 q = vposition.xyz / vposition.w; + + float depth = 0.5*(q.z + 1.0); + color.r = fract(16777216.0 * depth); + color.g = fract(65536.0 * depth); + color.b = fract(256.0 * depth); + color.a = depth; + + gl_FragColor = color; + } +</script> + + +<script id="colorVertexShader" type="text/glsl"> + attribute vec4 position; + attribute vec3 normal; + + uniform mat4 worldViewProjection; + uniform mat4 world; + uniform mat4 worldInverseTranspose; + uniform mat4 lightViewProjection; + + varying vec4 vposition; + varying vec3 vnormal; + varying vec4 vprojTextureCoords; + varying vec4 vworldPosition; + + /** + * The vertex shader simply transforms the input vertices to screen space. + */ + void main() { + vworldPosition = world * position; + vnormal = vec3(worldInverseTranspose * vec4(normal, 0)); + vposition = worldViewProjection * position; + vprojTextureCoords = lightViewProjection * world * position; + gl_Position = vposition; + } +</script> +<script id="colorPixelShader" type="text/glsl"> + uniform vec4 ambient; + uniform vec4 diffuse; + uniform vec4 specular; + uniform float shininess; + + varying vec4 vposition; + varying vec4 vworldPosition; + varying vec3 vnormal; + varying vec4 vprojTextureCoords; + + uniform sampler2D shadowMapSampler; + + uniform vec3 lightWorldPos; + uniform mat4 viewInverse; + + vec3 lighting(vec3 position, vec3 normal, vec4 pigment, vec4 specular, float shininess) { + vec3 l = normalize(vec3(lightWorldPos) - position); // Toward light. + vec3 n = normalize(normal); // Normal. + vec3 v = normalize(vec3(viewInverse * vec4(0,0,0,1)) - position); // Toward eye. + vec3 r = normalize(-reflect(v, n)); + + return clamp(dot(n,l), 0.0, 1.0) * diffuse.rgb + + 0.2 * specular.rgb * pow(max(dot(l, r), 0.0), shininess); + } + + void main() { + vec3 outColor = ambient.rgb; + vec4 projCoords = vprojTextureCoords; + + // Convert texture coords to [0, 1] range. + projCoords /= projCoords.w; + projCoords.x = 0.5 * projCoords.x + 0.5; + projCoords.y = 0.5 * projCoords.y + 0.5; + projCoords.z = 0.5 * projCoords.z + 0.5; + + float depth = projCoords.z; + + float light; + + // If the rednered point is farther from the light than the distance encoded + // in the shadow map, we give it a light coefficient of 0. + vec4 color = texture2D(shadowMapSampler, projCoords.xy); + + light = (color.a + + color.b / 256.0 + + color.g / 65536.0 + + color.r / 16777216.0) + 0.008 > depth ? 1.0 : 0.0; + + // Make the illuninated area a round spotlight shape just for fun. + // Comment this line out to see just the shadows. + light *= 1.0 - smoothstep(0.45, 0.5, + length(projCoords.xy - vec2(0.5, 0.5))); + + outColor += light * lighting( + vec3(vworldPosition), + vnormal, + diffuse, + specular, + shininess); + + gl_FragColor = vec4(outColor, 1.0); + } +</script> + + + +</head> +<body> +<h1>Shadow Maps</h1> +This sample implements a basic shadow map. +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" width="800px" height="600px"></div> +<!-- End of O3D plugin --> +Use A, S, D, W, I and O to move the light. +Press spacebar to see the shadow map. +</body> +</html> |