topical media & game development

talk show tell print

graphic-o3d-samples-beachdemo-effect.js / js



  /*
   * 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.
   */
  
  // All Rights Reserved.
  
  
@fileoverview This file contains a customized portion of the code from effect.js. TODO refactor the code in effect.js to use templates to help generate shaders so this is no longer needed.

  
  
  
Builds a shader string for a given standard COLLADA material type.
parameter: {o3d.Material} material Material for which to build the shader.
parameter: {String} effectType Type of effect to create ('phong', 'lambert', 'constant').
returns: {description: string, shader: string} A description and the shader string.

  
  var beachDemoBuildStandardShaderString = function(material,
                                                    effectType) {
    var bumpSampler = material.getParam('bumpSampler');
    var bumpUVInterpolant;
  
    
Extracts the texture type from a texture param.
parameter: {!o3d.ParamTexture} textureParam The texture parameter to inspect.
returns: {string} The texture type (1D, 2D, 3D or CUBE).

  
    var getTextureType = function(textureParam) {
      var texture = textureParam.value;
      if (!texture) return '2D';  // No texture value, have to make a guess.
      switch (texture.className) {
        case 'o3d.Texture1D' : return '1D';
        case 'o3d.Texture2D' : return '2D';
        case 'o3d.Texture3D' : return '3D';
        case 'o3d.TextureCUBE' : return 'CUBE';
        default : return '2D';
      }
    }
  
    
Extracts the sampler type from a sampler param. It does it by inspecting the texture associated with the sampler.
parameter: {!o3d.ParamTexture} samplerParam The texture parameter to inspect.
returns: {string} The texture type (1D, 2D, 3D or CUBE).

  
    var getSamplerType = function(samplerParam) {
      var sampler = samplerParam.value;
      if (!sampler) return '2D';
      var textureParam = sampler.getParam('Texture');
      if (textureParam)
        return getTextureType(textureParam);
      else
        return '2D';
    };
  
    
Builds uniform variables common to all standard lighting types.
returns: {string} The effect code for the common shader uniforms.

  
    var buildCommonUniforms = function() {
      return 'uniform float4x4 world : WORLD;\n' +
             'uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' +
             'uniform float3 lightWorldPos;\n' +
             'uniform float4 lightColor;\n' +
             'uniform float  clipHeight;\n';
    };
  
    
Builds uniform variables common to lambert, phong and blinn lighting types.
returns: {string} The effect code for the common shader uniforms.

  
    var buildLightingUniforms = function() {
      return 'uniform float4x4 viewInverse : VIEWINVERSE;\n' +
             'uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\n';
    };
  
    
Builds uniform parameters for a given color input. If the material has a sampler parameter, a sampler uniform is created, otherwise a float4 color value is created.
parameter: {!o3d.Material} material The material to inspect.
parameter: {!Array.} descriptions Array to add descriptions too.
parameter: {string} name The name of the parameter to look for. Usually emissive, ambient, diffuse or specular.
parameter: {boolean} opt_addColorParam Whether to add a color param if no sampler exists. Default = true.
returns: {string} The effect code for the uniform parameter.

  
    var buildColorParam = function(material, descriptions, name,
                                   opt_addColorParam) {
      if (opt_addColorParam === undefined) {
        opt_addColorParam = true;
      }
      var samplerParam = material.getParam(name + 'Sampler');
      if (samplerParam) {
        var type = getSamplerType(samplerParam);
        descriptions.push(name + type + 'Texture');
        return 'sampler' + type + ' ' + name + 'Sampler;\n'
      } else if (opt_addColorParam) {
        descriptions.push(name + 'Color');
        return 'uniform float4 ' + name + ';\n';
      } else {
        return '';
      }
    };
  
    
Builds the effect code to retrieve a given color input. If the material has a sampler parameter of that name, a texture lookup is done. Otherwise it's a no-op, since the value is retrieved directly from the color uniform of that name.
parameter: {!o3d.Material} material The material to inspect.
parameter: {string} name The name of the parameter to look for. Usually emissive, ambient, diffuse or specular.
returns: {string} The effect code for the uniform parameter retrieval.

  
    var getColorParam = function(material, name) {
      var samplerParam = material.getParam(name + 'Sampler');
      if (samplerParam) {
        var type = getSamplerType(samplerParam);
        return '  float4 ' + name + ' = tex' + type +
               '(' + name + 'Sampler, input.' + name + 'UV);\n'
      } else {
        return '';
      }
    };
  
    
Builds the vertex and fragment shader entry point in the format that o3d can parse.
returns: {string} The effect code for the entry points.

  
    var buildEntryPoints = function() {
      return '  // #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
             '  // #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
             '  // #o3d MatrixLoadOrder RowMajor\n';
    };
  
    
Builds vertex and fragment shader string for the Constant lighting type.
parameter: {!o3d.Material} material The material for which to build shaders.
parameter: {!Array.} descriptions Array to add descriptions too.
returns: {string} The effect code for the shader, ready to be parsed.

  
    var buildConstantShaderString = function(material, descriptions) {
      descriptions.push('constant');
      return buildCommonUniforms() +
             buildColorParam(material, descriptions, 'emissive') +
             buildVertexDecls(material, false, false) +
             buildVertexShaderStart() +
             buildUVPassthroughs(material) +
             '  return output;\n' +
             '}\n' +
             'float4 pixelShaderFunction(OutVertex input) : COLOR {\n' +
             getColorParam(material, 'emissive') +
             buildAlphaComputation('emissive.a') +
             '  return float4(emissive.xyz, alpha);\n' +
             '}\n' +
             '\n' +
             buildEntryPoints();
    };
  
    
Builds vertex and fragment shader string for the Lambert lighting type.
parameter: {!o3d.Material} material The material for which to build shaders.
parameter: {!Array.} descriptions Array to add descriptions too.
returns: {string} The effect code for the shader, ready to be parsed.

  
    var buildLambertShaderString = function(material, descriptions) {
      descriptions.push('lambert');
      return buildCommonUniforms() +
             buildLightingUniforms() +
             buildColorParam(material, descriptions, 'emissive') +
             buildColorParam(material, descriptions, 'ambient') +
             buildColorParam(material, descriptions, 'diffuse') +
             buildColorParam(material, descriptions, 'bump', false) +
             buildVertexDecls(material, true, false) +
             buildVertexShaderStart() +
             buildUVPassthroughs(material) +
             '  output.normal = mul(float4(input.normal.xyz,0),\n' +
             '                      worldInverseTranspose).xyz;\n' +
             bumpVertexShaderCode() +
             '  return output;\n' +
             '}\n' +
             'float4 pixelShaderFunction(OutVertex input) : COLOR\n' +
             '{\n' +
             getColorParam(material, 'emissive') +
             getColorParam(material, 'ambient') +
             getColorParam(material, 'diffuse') +
             getNormalShaderCode() +
             '  float3 surfaceToLight = normalize(lightWorldPos -\n' +
             '                                    input.worldPosition);\n' +
             '  float4 litR = lit(dot(normal, surfaceToLight), 0, 0);\n' +
             buildAlphaComputation('diffuse.a') +
             '  return float4((emissive +\n' +
             '      lightColor * (ambient * diffuse + diffuse * litR.y)).rgb,' +
             '      alpha);\n' +
             '}\n' +
             '\n' +
             buildEntryPoints();
  
    };
  
    
Builds vertex and fragment shader string for the Blinn lighting type.
parameter: {!o3d.Material} material The material for which to build shaders.
parameter: {!Array.} descriptions Array to add descriptions too.
returns: {string} The effect code for the shader, ready to be parsed. TODO: This is actually just a copy of the Phong code. Change to Blinn.

  
    var buildBlinnShaderString = function(material, descriptions) {
      descriptions.push('blinn');
      return buildCommonUniforms() +
          buildLightingUniforms() +
          buildColorParam(material, descriptions, 'emissive') +
          buildColorParam(material, descriptions, 'ambient') +
          buildColorParam(material, descriptions, 'diffuse') +
          buildColorParam(material, descriptions, 'specular') +
          buildColorParam(material, descriptions, 'bump', false) +
          'uniform float shininess;\n' +
          buildVertexDecls(material, true, true) +
          buildVertexShaderStart() +
          buildUVPassthroughs(material) +
          '  output.normal = mul(float4(input.normal.xyz,0),\n' +
          '                      worldInverseTranspose).xyz;\n' +
          bumpVertexShaderCode() +
          '  return output;\n' +
          '}\n' +
          'float4 pixelShaderFunction(OutVertex input) : COLOR\n' +
          '{\n' +
          getColorParam(material, 'emissive') +
          getColorParam(material, 'ambient') +
          getColorParam(material, 'diffuse') +
          getColorParam(material, 'specular') +
          getNormalShaderCode() +
          '  float3 surfaceToLight = normalize(lightWorldPos -\n' +
          '                                    input.worldPosition);\n' +
          '  float3 surfaceToView = normalize(viewInverse[3] -\n' +
          '                                   input.worldPosition);\n' +
          '  float3 halfVector = normalize(surfaceToLight + surfaceToView);\n' +
          '  float4 litR = lit(dot(normal, surfaceToLight), \n' +
          '                    dot(normal, halfVector), shininess);\n' +
          buildAlphaComputation('diffuse.a') +
          '  return float4((emissive +\n' +
          '  lightColor * (ambient * diffuse + diffuse * litR.y +\n' +
          '                        + specular * litR.z)).rgb,' +
          '      alpha);\n' +
          '}\n' +
          '\n' +
          buildEntryPoints();
    };
  
    
Builds vertex and fragment shader string for the Phong lighting type.
parameter: {!o3d.Material} material The material for which to build shaders.
parameter: {!Array.} descriptions Array to add descriptions too.
returns: {string} The effect code for the shader, ready to be parsed.

  
    var buildPhongShaderString = function(material, descriptions) {
      descriptions.push('phong');
      return buildCommonUniforms() +
          buildLightingUniforms() +
          buildColorParam(material, descriptions, 'emissive') +
          buildColorParam(material, descriptions, 'ambient') +
          buildColorParam(material, descriptions, 'diffuse') +
          buildColorParam(material, descriptions, 'specular') +
          buildColorParam(material, descriptions, 'bump', false) +
          'uniform float shininess;\n' +
          buildVertexDecls(material, true, true) +
          buildVertexShaderStart() +
          buildUVPassthroughs(material) +
          '  output.normal = mul(float4(input.normal.xyz,0),\n' +
          '                      worldInverseTranspose).xyz;\n' +
          bumpVertexShaderCode() +
          '  return output;\n' +
          '}\n' +
          'float4 pixelShaderFunction(OutVertex input) : COLOR\n' +
          '{\n' +
          getColorParam(material, 'emissive') +
          getColorParam(material, 'ambient') +
          getColorParam(material, 'diffuse') +
          getColorParam(material, 'specular') +
          getNormalShaderCode() +
          '  float3 surfaceToLight = normalize(lightWorldPos -\n' +
          '                                    input.worldPosition);\n' +
          '  float3 surfaceToView = normalize(viewInverse[3] -\n' +
          '                                   input.worldPosition);\n' +
          '  float3 halfVector = normalize(surfaceToLight + surfaceToView);\n' +
          '  float4 litR = lit(dot(normal, surfaceToLight), \n' +
          '                    dot(normal, halfVector), shininess);\n' +
          buildAlphaComputation('diffuse.a') +
          '  return float4((emissive +\n' +
          '  lightColor * (ambient * diffuse + diffuse * litR.y +\n' +
          '                        + specular * litR.z)).rgb,' +
          '      alpha);\n' +
          '}\n' +
          '\n' +
          buildEntryPoints();
    };
  
    // An integer value which keeps track of the next available interpolant.
    var interpolant;
  
    
Builds the alpha computation.
parameter: {string} inputAlpha the variable name of the original alpha.
returns: {string} The shader code.

  
    var buildAlphaComputation = function(inputAlpha) {
      return 'float alpha = input.worldPosition.z > clipHeight ? 0 : ' +
          inputAlpha + ';\n';
    };
  
    
Builds the start of a vertex shader.
returns: {string} The shader code.

  
    var buildVertexShaderStart = function() {
      return '' +
          'OutVertex vertexShaderFunction(InVertex input) {\n' +
          '  OutVertex output;\n' +
          '  output.position = mul(input.position, worldViewProjection);\n' +
          '  output.worldPosition = mul(input.position, world);\n';
    };
  
    
Builds the texture coordinate declaration for a given color input (usually emissive, anmbient, diffuse or specular). If the color input does not have a sampler, no TEXCOORD declaration is built.
parameter: {!o3d.Material} material The material to inspect.
parameter: {string} name The name of the color input.
returns: {string} The code for the texture coordinate declaration.

  
    var buildTexCoord = function(material, name) {
      if (material.getParam(name + 'Sampler')) {
        return '  float2 ' + name + 'UV : TEXCOORD' + interpolant++ + ';\n';
      } else {
        return '';
      }
    };
  
    
Builds all the texture coordinate declarations for a vertex attribute declaration.
parameter: {!o3d.Material} material The material to inspect.
returns: {string} The code for the texture coordinate declarations.

  
    var buildTexCoords = function(material) {
      interpolant = 0;
      return buildTexCoord(material, 'emissive') +
             buildTexCoord(material, 'ambient') +
             buildTexCoord(material, 'diffuse') +
             buildTexCoord(material, 'specular');
    };
  
    
Builds the texture coordinate passthrough statement for a given color input (usually emissive, ambient, diffuse or specular). These assigments are used in the vertex shader to pass the texcoords to be interpolated to the rasterizer. If the color input does not have a sampler, no code is generated.
parameter: {!o3d.Material} material The material to inspect.
parameter: {string} name The name of the color input.
returns: {string} The code for the texture coordinate passthrough statement.

  
    var buildUVPassthrough = function(material, name) {
      if (material.getParam(name + 'Sampler')) {
        return '  output.' + name + 'UV = input.' + name + 'UV;\n';
      } else {
        return '';
      }
    };
  
    
Builds all the texture coordinate passthrough statements for the vertex shader.
parameter: {!o3d.Material} material The material to inspect.
returns: {string} The code for the texture coordinate passthrough statements.

  
    var buildUVPassthroughs = function(material) {
      return buildUVPassthrough(material, 'emissive') +
             buildUVPassthrough(material, 'ambient') +
             buildUVPassthrough(material, 'diffuse') +
             buildUVPassthrough(material, 'specular') +
             buildUVPassthrough(material, 'bump');
    };
  
    
Builds bump input coords if needed.
returns: {string} The code for bump input coords.

  
    var buildBumpInputCoords = function() {
      return bumpSampler ?
          ('  float3 tangent      : TANGENT;\n' +
           '  float3 binormal     : BINORMAL;\n' +
           '  float2 bumpUV       : TEXCOORD' + interpolant++ + ';\n') : '';
    };
  
    
Builds bump output coords if needed.
returns: {string} The code for bump input coords.

  
    var buildBumpOutputCoords = function() {
      return bumpSampler ?
          ('  float3 tangent      : TEXCOORD' + interpolant++ + ';\n' +
           '  float3 binormal     : TEXCOORD' + interpolant++ + ';\n' +
           '  float2 bumpUV       : TEXCOORD' + interpolant++ + ';\n') : '';
    };
  
    
Builds the normal map part of the vertex shader.
returns: {string} The code for normal mapping in the vertex shader.

  
    var bumpVertexShaderCode = function() {
      return bumpSampler ?
          ('  output.binormal = ' +
           'mul(float4(input.binormal,0), worldInverseTranspose).xyz;\n' +
           '  output.tangent = ' +
           'mul(float4(input.tangent,0), worldInverseTranspose).xyz;\n') : '';
    };
  
    
Builds the normal calculation of the pixel shader.
returns: {string} The code for normal computation in the pixel shader.

  
    var getNormalShaderCode = function() {
      return bumpSampler ?
          ('float3x3 tangentToWorld = float3x3(input.tangent,\n' +
           '                                   input.binormal,\n' +
           '                                   input.normal);\n' +
           'float3 tangentNormal = tex2D(bumpSampler, input.bumpUV.xy).xyz -\n' +
           '                       float3(0.5, 0.5, 0.5);\n' +
           'float3 normal = mul(tangentNormal, tangentToWorld);\n' +
           'normal = normalize(normal);\n') :
          '  float3 normal = normalize(input.normal);\n';
    };
  
    
Builds the vertex declarations for a given material.
parameter: {!o3d.Material} material The material to inspect.
parameter: {boolean} diffuse Whether to include diffuse interpolations.
parameter: {boolean} specular Whether to include specular interpolations.
returns: {string} The code for the vertex declarations.

  
    var buildVertexDecls = function(material, diffuse, specular) {
      var str = 'struct InVertex {\n' +
                '  float4 position     : POSITION;\n' +
                '  float4 normal       : NORMAL;\n' +
                buildTexCoords(material) +
                buildBumpInputCoords() +
                '};\n' +
                'struct OutVertex {\n' +
                '  float4 position     : POSITION;\n' +
                buildTexCoords(material) +
                buildBumpOutputCoords() +
                '  float4 worldPosition: TEXCOORD' + interpolant++ + ';\n';
      if (diffuse || specular) {
        str += '  float3 normal        : TEXCOORD' + interpolant++ + ';\n';
      }
      str += '};\n'
      return str;
    };
  
    // Create a shader string of the appropriate type, based on the
    // effectType.
    var str;
    var descriptions = [];
    if (effectType == 'phong') {
      str = buildPhongShaderString(material, descriptions);
    } else if (effectType == 'lambert') {
      str = buildLambertShaderString(material, descriptions);
    } else if (effectType == 'blinn') {
      str = buildBlinnShaderString(material, descriptions);
    } else if (effectType == 'constant') {
      str = buildConstantShaderString(material, descriptions);
    } else {
      throw ('unknown effect type "' + effectType + '"');
    }
  
    return {description: descriptions.join('_'), shader: str};
  };
  
  


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.