WebGL 1.0
 OpenGL ES 2.0
 Google Chrome 12.0.742.100
 Canvas 3d JS Library 2.2

■[ Canvas 3d JS Library ]でカスタムシェーダー 
  [ サンプルページの表示 ]
サンプルページの表示には1分以上かかります
Prev Top Next
関連ページ:WebGL 1.0 はじめました
WebGLで[ Canvas 3d JS Library ]を使ってみる

逆光 順光 

今回は[ Canvas 3d JS Library ]を使ってカスタムシェーダーを実装します。 シェーダーソースの追加だけでなく、ライブラリ本体の修正も必要となります。

シェーダーは新ネタを作成しようかと思ったのですが、簡単でシェーディングのみで実装できるネタが思いつかなかったので、 Direct3D 9 の記事で紹介したリムライティングにしました。逆光の場合、メッシュの輪郭付近に光がシミ出してくる奴です。 処理フローについては、既出ネタなので省略します。

あと前回も書きましたが、無圧縮版の[ Canvas 3d JS Library ]ライブラリのソースを修正しました。 圧縮版のソースは、無圧縮版のソース内にある半角スペースや改行コードを取り除いたり、 変数を一文字にしたりして総バイト数を少なくしただけなので、圧縮版と無圧縮版のソースを混在させても問題なく動作します。 圧縮版のソースを作成する気はまったくないのでどうしても圧縮版にしたい人は自分で作成してください。

今回は [ Canvas 3d JS Library ]の バージョン 2.2 のライブラリを修正しました。 他のバージョンを修正すると正しく動作しなくなる可能性がありますので、必ず バージョン 2.2 のライブラリを修正してください。

コピーボタンですが、Internet Explorer での動作のみ確認しています。Google Chrome ではコピーできません。


---canvas3dapi/shaders/model/Custom/custom_callback.js---


/*
  Copyright (c) 2008 Seneca College
  Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/)
*/

c3dl.custom_callback = function (renderingObj, scene)
{
  var progObjID = renderingObj.getProgramObjectID();
  var renderer = renderingObj.getRenderer();
  var geometry = renderingObj.getGeometry();
  var gl = renderingObj.getContext();
  var effect = geometry.getEffect();

  gl.useProgram(progObjID);

  var modelViewMatrix = c3dl.peekMatrix();
  c3dl.matrixMode(c3dl.PROJECTION);
  var projectionMatrix = c3dl.peekMatrix();
  c3dl.matrixMode(c3dl.MODELVIEW);

  var modelViewProjMatrix = c3dl.multiplyMatrixByMatrix(projectionMatrix, modelViewMatrix);
  renderer.setUniformMatrix(progObjID, "modelViewMatrix", modelViewMatrix, scene, "custom");
  renderer.setUniformMatrix(progObjID, "modelViewProjMatrix", modelViewProjMatrix, scene, "custom");

  for (var coll = 0; coll < geometry.getPrimitiveSets().length; coll++)
  {
    var currColl = geometry.getPrimitiveSets()[coll];

    // NORMAL
    var normalAttribLoc = scene.curContextCache.attributes["custom"+coll+"Normal"];
    if (normalAttribLoc == undefined) {
      normalAttribLoc = gl.getAttribLocation(progObjID, "Normal");
      scene.curContextCache.attributes["custom"+coll+"Normal"] = normalAttribLoc;
    }
    
    if (currColl.getNormals())
    {
      var NormalMatrix = c3dl.inverseMatrix(modelViewMatrix);
      NormalMatrix = c3dl.transposeMatrix(NormalMatrix);
      renderer.setUniformMatrix(progObjID, "normalMatrix", NormalMatrix, scene, "custom"+coll);
      renderer.setVertexAttribArray(progObjID, "Normal", 3, currColl.getVBONormals(), scene, "custom"+coll);
    }
    else
    {
      gl.disableVertexAttribArray(normalAttribLoc);
    }

    // TEXTURE
    var texAttribLoc = scene.curContextCache.attributes["custom"+coll+"Texture"];
    if(texAttribLoc ==undefined ) {
      texAttribLoc = gl.getAttribLocation(progObjID, "Texture");
      scene.curContextCache.attributes["custom"+coll+"Texture"] = texAttribLoc;
    }
    
    var texID = renderer.getTextureID(currColl.getTexture());
    if (texID == -1 && currColl.getTexture())
    {
      renderer.addTexture(currColl.getTexture());
    }

    if (texID != -1 && currColl.getTexture() && currColl.getTexCoords() && texAttribLoc != -1)
    {
      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, texID);

      renderer.setVertexAttribArray(progObjID, "Texture", 2, currColl.getVBOTexCoords(), scene, "custom"+coll);
      renderer.setUniformi(progObjID, "myTex", 0, scene, "custom"+coll);
      renderer.setUniformi(progObjID, "usingTexture", true, scene, "custom"+coll);
    }
    else
    {
      gl.disableVertexAttribArray(texAttribLoc);
      renderer.setUniformi(progObjID, "usingTexture", false, scene, "custom"+coll);
    }
    
    // Vertices
    renderer.setVertexAttribArray(progObjID, "Vertex", 3, currColl.getVBOVertices(), scene, "custom"+coll);
    gl.drawArrays(renderer.getFillMode(), 0, currColl.getVertices().length / 3);
  }
}

リムライティングシェーダーでレンダリングするためのコールバック関数です。このファイルは新規で作成します。 既存のソースをコピペして修正しましたが、ソースチェックはきちんとしていません。あってると思いますけどね。

---canvas3dapi/shaders/model/Custom/custom_vs.js---


/*
  Copyright (c) 2008 Seneca College
  Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/)
*/

c3dl.custom_vs = 

"attribute vec3 Vertex;" + 
"attribute vec3 Normal;" + 
"attribute vec3 Texture;" + 

"uniform mat4 modelViewMatrix;" +
"uniform mat4 modelViewProjMatrix;" +
"uniform mat4 normalMatrix;" + 

"varying vec3 ViewVec;" +
"varying vec3 tnorm;" +
"varying vec4 texCoord;" +

"void main(void){" + 

"  mat3 normalMatrix3x3 = mat3(normalMatrix[0][0],normalMatrix[0][1],normalMatrix[0][2]," +
"                         normalMatrix[1][0],normalMatrix[1][1],normalMatrix[1][2]," +
"                         normalMatrix[2][0],normalMatrix[2][1],normalMatrix[2][2]);" + 
"  tnorm = normalMatrix3x3 * Normal;" + 
"  texCoord = vec4(Texture,1.0);" + 

"  ViewVec = vec3(modelViewMatrix * vec4(Vertex,1.0));" +
"  gl_Position =  modelViewProjMatrix * vec4(Vertex, 1.0);" +
"}";

リムライティングの頂点シェーダーソースです。これもコピペしたソースをリムライティング用に修正しました。 もとのソースの記述方法を崩さないようにしているため、法線ベクトルをワールド座標系上で行列変換かけたりしています。

---canvas3dapi/shaders/model/Custom/custom_fs.js---


/*
  Copyright (c) 2008 Seneca College
  Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/)
*/

c3dl.custom_fs = 

"uniform sampler2D myTex;" + 
"uniform int usingTexture;" + 

"varying vec3 ViewVec;" +
"varying vec3 tnorm;" +
"varying vec4 texCoord;" +

"void main(void) {" + 
"  vec3 n   = normalize( tnorm );" + 
"  vec3 nviewVec = normalize(ViewVec);" +
"  vec3 col = vec3( 0, 0, 0 );" +
"  vec3 col2 = vec3( 0, 0, 0 );" +
"  if(lightingOn == true)" +
"  {" +
"    for(int i = 0; i < C3DL_MAX_LIGHTS; i++)" +
"    {" +
"      if(lights[i].isOn) " +
"      {" +
"        if(lights[i].type == 1)" +
"        {" +
"          vec3 nlight = normalize(vec3(-lights[i].position));" +

           // 法線ベクトルと平行光源の内積
"          float l = dot( n, nlight );" + 

           // ハーフランバート
"          l = l * 0.5 + 0.5;" + 
"          col += l;" +

           // リムライティングの反射率
           // 視線ベクトルに対し輪郭付近部分の反射率を強くする
"          l = max( dot( nviewVec, nlight ), 0.0 ) * ( 1.0 - abs( dot( nviewVec, n ) ) );" + 
"          col2 += l;" +
"        }" +
"      }" +
       // Radeon HD 5700 の場合、これを入れないとなぜか正しく動作してくれない...
"      if( i >= C3DL_MAX_LIGHTS - 1 ) break; " +
"    }" +
"  }" +
"  if( usingTexture == 1 ){" +
"    col *= vec3(texture2D(myTex, texCoord.xy));" +
"  }" + 
"  gl_FragColor = vec4( col + col2, 1 );" +
"}";

リムライティングのフラグメントシェーダーソースです。これもコピペしたソースをリムライティング用に修正しました。 ライティングは平行光源のみ実装しています。

例によって Radeon HD 5700 で正しく動作しないため、無駄なロジックを入れていますが、これ本当に謎だ。 最新のドライバインストールしたせいかな。

---canvas3dapi/c3dapi.js---


/*
  Copyright (c) 2008 Seneca College
  Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/)
*/

/*
  HTML pages which make use of c3dl must include the following script tag
  in the page header.
  Note the path for the src attribute may need to be changed, depending
  on where c3dapi.js has been placed.

  		
*/
var scripts = document.getElementsByTagName("script");
var parts = scripts[scripts.length - 1].src.split("/");
parts.pop();
var basePath = parts.join("/");
var head = document.getElementsByTagName("head")[0];

/**
 @private
 
 @param {String} path Path of the resource to include.
 
 Create a function to keep subsequent lines shorter.
*/
c3dl_require = function (path)
{
  document.write('<' + 'script');
  document.write(' language="javascript"');
  document.write(' type="text/javascript"');
  document.write(' src="' + basePath + "/" + path + '">');
  document.write('</' + 'script' + '>');
}

//Some classes depend on others, so the order of the following lines should not be
//changed carelessly.
c3dl_require('c3dlnamespace.js');
c3dl_require('constants.js');

c3dl_require('effects/effect_docs.js');

//make the debugger one of the first things included, so other 
//js files can make use of c3dl.debug.log*
c3dl_require('debug.js');

//renderers
c3dl_require('renderer/renderer.js');
c3dl_require('renderer/rendererwebgl.js');
c3dl_require('renderer/programobject.js');

//math
c3dl_require('math/mjs.js');
c3dl_require('math/mathutils.js');
c3dl_require('math/vector.js');
c3dl_require('math/matrix.js');
c3dl_require('math/quaternion.js');
c3dl_require('matrixstack.js');

//cameras
c3dl_require('camera/camera.js');
c3dl_require('camera/freecamera.js');
c3dl_require('camera/orbitcamera.js');

//bounding volumes
c3dl_require('enclosure/boundingsphere.js');
c3dl_require('enclosure/boundingvolume.js');
c3dl_require('enclosure/visualboundingsphere.js');
c3dl_require('enclosure/obb.js');
c3dl_require('enclosure/aabb.js');

//actors
c3dl_require('actors/actor.js');
c3dl_require('actors/primitive.js');
c3dl_require('actors/point.js');
c3dl_require('actors/line.js');

//shapes
c3dl_require('shapes/shape.js');
c3dl_require('shapes/cube.js');
c3dl_require('shapes/plane.js');
c3dl_require('shapes/sphere.js');
c3dl_require('shapes/custom.js');
c3dl_require('shapes/customplane.js');

//frustum culling
c3dl_require('frustum_culling/frustum.js');
c3dl_require('frustum_culling/plane.js');

//scene
c3dl_require('scene.js');

//texture
c3dl_require('texture/texture.js');
c3dl_require('texture/texturemanager.js');
c3dl_require('texture/textureutils.js');

//collada management
c3dl_require('collada/colladamanager.js');
c3dl_require('collada/colladaloader.js');
c3dl_require('collada/colladaqueue.js');
c3dl_require('collada/geometry.js');
c3dl_require('collada/primitiveset.js');

//lights
c3dl_require('light/light.js');
c3dl_require('light/positionallight.js');
c3dl_require('light/directionallight.js');
c3dl_require('light/spotlight.js');

//material
c3dl_require('material.js');

//scenegraph stuff
c3dl_require('collada/collada.js');
c3dl_require('scenegraph/scenenode.js');

//misc utilities
c3dl_require('utilities/utilities.js');

//shaders
c3dl_require('shaders/model/light/light_vs.js');
c3dl_require('shaders/model/material/material.js');

c3dl_require('shaders/model/standard/model_fs.js');
c3dl_require('shaders/model/standard/model_vs.js');
c3dl_require('shaders/model/standard/std_callback.js');

c3dl_require('shaders/particle_system/psys_vs.js');
c3dl_require('shaders/particle_system/psys_fs.js');
c3dl_require('shaders/point/point/point_vs.js');
c3dl_require('shaders/point/point/point_fs.js');
c3dl_require('shaders/point/sphere/point_sphere_vs.js');
c3dl_require('shaders/point/sphere/point_sphere_fs.js');
c3dl_require('shaders/line/line_vs.js');
c3dl_require('shaders/line/line_fs.js');
c3dl_require('shaders/bounding_sphere/bounding_sphere_vs.js');
c3dl_require('shaders/bounding_sphere/bounding_sphere_fs.js');

c3dl_require('shaders/model/greyscale/greyscale_vs.js');
c3dl_require('shaders/model/greyscale/greyscale_fs.js');
c3dl_require('shaders/model/greyscale/greyscale_callback.js');

c3dl_require('shaders/model/sepia/sepia_vs.js');
c3dl_require('shaders/model/sepia/sepia_fs.js');
c3dl_require('shaders/model/sepia/sepia_callback.js');

c3dl_require('shaders/model/cartoon/cartoon_vs.js');
c3dl_require('shaders/model/cartoon/cartoon_fs.js');
c3dl_require('shaders/model/cartoon/cartoon_callback.js');

c3dl_require('shaders/model/gooch/gooch_vs.js');
c3dl_require('shaders/model/gooch/gooch_fs.js');
c3dl_require('shaders/model/gooch/gooch_callback.js');

c3dl_require('shaders/model/solid_color/solid_color_vs.js');
c3dl_require('shaders/model/solid_color/solid_color_fs.js');
c3dl_require('shaders/model/solid_color/solid_color_callback.js');


c3dl_require('shaders/model/Custom/custom_vs.js');
c3dl_require('shaders/model/Custom/custom_fs.js');
c3dl_require('shaders/model/Custom/custom_callback.js');


//effects and instance effects
c3dl_require('effects/effecttemplate.js');
c3dl_require('effects/effect.js');

//particle system
c3dl_require('particle_system/particlesystem.js');
c3dl_require('particle_system/particle.js');

//initialization
c3dl_require('init.js');

//interaction
c3dl_require('interaction/collision.js');
c3dl_require('interaction/picking.js');
c3dl_require('interaction/pickingresult.js');

//Function to call the various versions of requestAnimationFrame
//To be updated when this is properly standardized.
window.requestAnimFrame = (function(callback){
      return  window.requestAnimationFrame       || 
              window.webkitRequestAnimationFrame || 
              window.mozRequestAnimationFrame    || 
              window.oRequestAnimationFrame      || 
              window.msRequestAnimationFrame     || 
              function(/* function */ callback, /* DOMElement */ element){
                window.setTimeout(callback, 1000 / 60);
              };
    })();

作成したリムライティングのシェーダーソースをc3dl側で使用できるようにインポートします。既存のソースを修正します。長いけど3行追加しただけです。

---canvas3dapi/renderer/rendererwebgl.js---


/*
  Copyright (c) 2008 Seneca College
  Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/)
*/

/**
 @class WebGL context.
 @augments c3dl.Renderer
 */
c3dl.WebGL = c3dl.inherit(c3dl.Renderer, function () {
  c3dl._superc(this);
  var glCanvas3D = null; // GL Context; 
  this.texManager = null;

  // overwrite the version set in the Renderer base class.
  this.version = 2.0;

  // overwrite the version set in the Renderer base class.
  this.versionString = "WebGL";

  // program objects to render various visual objects.
  this.geometryShader;
  this.particleSystemShader;
  this.pointShader;
  this.pointSphereShader;
  this.lineShader;
  this.boundingSphereShader;
  this.programsWithLights = [];

  /// Maybe need to move these out somewhere else
  this.pointVertBuffer = null;
  this.pointColBuffer = null;

  this.lineVertBuffer = null;
  this.lineColBuffer = null;

  // unique id of this renderer
  this.ID = c3dl.getNextRendererID();
  this.STANDARD_PROGRAM_ID = null;

  this.textureQueue = [];

  // have the vbos to render point spheres been created
  this.pointSphereRenderReady = false;

  // Verts, Normals, etc will be added to this.
  this.pointsphereVBOVert;

  /**
   Add a texture to the renderer. If the texture was added before
   the renderer was initialized, the texture will be placed in a 
   queue and the texture will be created once the renderer has been
   initialized.
   
   The renderer will create an internal ID for the texture which can
   be queried by calling getTextureID. This ID can be passed to commands 
   of the context in the rendering callback function in effects to make 
   that texture active.
   
   @parma {String} path Texture path
   */
  this.addTexture = function (path)
  {
    //
    if (this.texManager == null)
    {
      this.textureQueue.push(path);
    }
    else
    {
      this.texManager.addTexture(path);
    }
  }

  /**
   Get the unique ID of this renderer.
   
   @returns {int} unique ID of this renderer.
   */
  this.getID = function ()
  {
    return this.ID;
  }

  /**
   Get the ID of the texture at 'texturePath'. If the texture could not
   be found or the renderer has not yet been initialized, -1 will be 
   returned.
   
   @param {String} texturePath
   
   @returns {int} The ID of the texture, or -1 if the renderer has not 
   yet been initialized or if the texture was not found.
   */
  this.getTextureID = function (texturePath)
  {
    if (this.texManager)
    {
      return this.texManager.getID(texturePath);
    }
    else
    {
      return -1;
    }
  }

  /**
   @private
   Is the renderer ready?
   
   @returns {boolean} True if the context is not null, otherwise false.
   */
  this.isReady = function ()
  {
    return glCanvas3D == null ? false : true;
  }

  /**
   Get the WebGL context.
   
   @returns {Context} The GL Context.
   */
  this.getGLContext = function ()
  {
    return glCanvas3D;
  }

  /**
   @private
   Create a program which is composed of shaders.
   
   Create a program object which is composed of compiled shader objects.
   This program object can be installed as the current rendering state 
   by using gl.useProgram().
   
   @param {Array|String} vertexShaderSource The source code for the vertex shader.
   @param {Array|String} fragmentShaderSource The source code for the fragment shader.
   
   @return {c3dl.ProgramObject} ProgramObject or null .
   */
  this.createProgram = function (vertexShader, fragmentShader)
  {
    // We don't check the parameters because the WebGL functions will already
    // be checking to make sure the source is valid when it compiles them. If 
    // they are invalid, error messages will be displayed on the page.
    // make alias for shorter code.
    var gl = glCanvas3D;

    // createProgram creates a program object to which our shaders can be
    // attached. We can later tell WebGL which program to use by 
    // calling useProgram().
    var program = gl.createProgram();

    // it is possible createProgram failed.
    if (program == null)
    {
      c3dl.debug.logError("failed to create shader program");
      return null;
    }

    var vertShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertShader, vertexShader);


    gl.compileShader(vertShader);

    // The compilation status of each shader can be queried.
    if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS))
    {
      c3dl.debug.logError("vert shader: " + gl.getShaderInfoLog(vertShader));
      gl.deleteShader(vertShader);
      return null;
    }

    gl.attachShader(program, vertShader);


    var vertShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(vertShader, fragmentShader);
    gl.compileShader(vertShader);

    // The compilation status of each shader can be queried.
    if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS))
    {
      c3dl.debug.logError("frag shader " + gl.getShaderInfoLog(vertShader));
      gl.deleteShader(vertShader);
      return null;
    }
    gl.attachShader(program, vertShader);

    // Linking is the final step which must be done to obtain a valid
    // program object. Linking assigns variable locations for uniform variables,
    // initialized user-defined uniform variables, resolves references
    // between independently compiled shader objects, etc.
    //
    // The status of the link operation is stored as part of the program object's
    // state which we will query.
    // 
    gl.linkProgram(program);

    // Check if the shaders were linked successfully.
    if (gl.getProgramParameter(program, gl.LINK_STATUS) != 1)
    {
      c3dl.debug.logError(gl.getProgramInfoLog(program));
      gl.deleteProgram(program);
      return null;
    }

    // create a program object, similar to an WebGL program object.
    var programObject = new c3dl.ProgramObject();
    programObject.rendererID = this.ID;
    programObject.programID = program;

    return programObject;
  }

  /**
   @private
   Clear the color and depth buffers.
   
   Scene is responsible for calling this.
   */
  this.clearBuffers = function ()
  {
    glCanvas3D.clear(glCanvas3D.COLOR_BUFFER_BIT | glCanvas3D.DEPTH_BUFFER_BIT);
  }

  /**
   @private
   Swap the front and back buffers. Scene is responsible for calling this.
   */
  this.swapBuffers = function ()
  {
    glCanvas3D.clear(glCanvas3D.COLOR_BUFFER_BIT | glCanvas3D.DEPTH_BUFFER_BIT);
  }

  /**
   @private
   this is documented in the renderer class
   */
  this.setClearColor = function (bgColor)
  {
    if (bgColor.length >= 3)
    {
      glCanvas3D.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0);
    }
  }

  /**
   @private
   
   implementes the 'virtual' function getMaxLineWidth from renderer.
   
   Get the maximum line width supported which is implementation
   dependent.
   
   @returns {int} maximum line width supported.
   */
  this.getMaxLineWidth = function ()
  {
    // returns the range, first value represents minimum value supported.
    // WebGL guarantees support for width of 1.
    // should this be checked once on init and then we don't have to keep querying?
    var maxLineWidth = glCanvas3D.getParameter(glCanvas3D.ALIASED_LINE_WIDTH_RANGE);

    return maxLineWidth[1];
  }

  /**
   @private
   Set the shader values to zero so the light no longer affects the scene.
   
   @param {int} lightID The light to clear must range from 0 to one less than c3dl.MAX_LIGHTS.
   */
  this.clearLight = function (lightID, scene)
  {
    if (lightID >= 0 && lightID < c3dl.MAX_LIGHTS)
    {
      for (var i = 0, len = this.programsWithLights.length; i < len; i++)
      {
        var PID = this.programsWithLights[i];

        // base string to shorten code below.
        var base = "lights[" + lightID + "].";
        glCanvas3D.useProgram(PID);
        this.setUniformf(PID, base + "position", [0, 0, 0], scene, "light"+i+lightID);
        this.setUniformf(PID, base + "ambient", [0, 0, 0], scene, "light"+i+lightID);
        this.setUniformf(PID, base + "diffuse", [0, 0, 0], scene, "light"+i+lightID);
        this.setUniformf(PID, base + "specular", [0, 0, 0], scene, "light"+i);
        this.setUniformf(PID, base + "spotDirection", [0, 0, -1], scene, "light"+i+lightID);
        this.setUniformf(PID, base + "spotCutoff", 180, scene, "light"+i+lightID);
        this.setUniformf(PID, base + "spotExponent", 0, scene, "light"+i+lightID);
        this.setUniformf(PID, base + "attenuation1", 1, scene, "light"+i+lightID);
        this.setUniformf(PID, base + "attenuation2", 0, scene, "light"+i+lightID);
        this.setUniformf(PID, base + "attenuation3", 0, scene, "light"+i+lightID);
        this.setUniformi(PID, base + "type", 0, scene, "light"+i+lightID);
        this.setUniformi(PID, base + "isOn", 0, scene, "light"+i+lightID);
      }
    }
  }

  /**
   @private
   
   @param {Array} ambientLight Array of lights
   */
  this.updateAmbientLight = function (ambientLight, scene)
  {
    // the toon shader uses lights, but does not use
    // the ambient light. We need to turn off debugger to
    // suppress any errors.
    var prevVal = c3dl.debug.getVisible();
    c3dl.debug.setVisible(false);

    for (var i = 0, len = this.programsWithLights.length; i < len; i++)
    {
      glCanvas3D.useProgram(this.programsWithLights[i]);
      this.setUniformf(this.programsWithLights[i], "ambientLightColor", ambientLight, scene, "ambientLight");
      this.setUniformi(this.programsWithLights[i], "lightingOn", this.getLighting(), scene, "ambientLight"+i);
    }

    // turn it back on if it was on before.
    if (prevVal == true)
    {
      c3dl.debug.setVisible(true);
    }
  }

  /**
   @private
   Update the light states in the shader with lightList.
   
   @param {Array} lightList Array of lights
   */
  this.updateLights = function (lightList, scene)
  {
    // The list of all the program objects which have lights need to be updated
    for (var progObjIter = 0, len = this.programsWithLights.length; progObjIter < len; progObjIter++)
    {
      var shader = this.programsWithLights[progObjIter];

      glCanvas3D.useProgram(shader);

      // iterate over all the lights
      for (var i = 0, len2 = lightList.length; i < len2; i++)
      {
        // create a base string to shorten code below.
        var base = "lights[" + i + "].";

        // we may have nulls in the array which represent places lights can be inserted
        // so we have to check for these.
        if (lightList[i] != null)
        {
          // if the light is off, that's the only uniform var that needs to be set.
          if (lightList[i].isOn() == false)
          {
            this.setUniformi(shader, base + "isOn", lightList[i].isOn(), scene, "light"+progObjIter+i);
          }
          else
          {
            if (lightList[i] instanceof c3dl.DirectionalLight)
            {
              // place the light in viewspace here instead of the shader, preventing placing the lights
              // in viewspace for every vertex.
              var dir = c3dl.multiplyMatrixByDirection(c3dl.peekMatrix(), lightList[i].getDirection());
              dir =c3dl.addVectorComponent(dir,0);

              this.setUniformf(shader, base + "position", dir, scene, "light"+progObjIter+i);

              // this is used to distinguish a directional light from a spotlight.
              this.setUniformf(shader, base + "spotCutoff", 180, scene, "light"+progObjIter+i);
            }

            // check if its a spotlight first before positional light!
            else if (lightList[i] instanceof c3dl.SpotLight)
            {
              var pos = lightList[i].getPosition();
              pos = c3dl.multiplyMatrixByVector(c3dl.peekMatrix(), pos);
              pos = c3dl.addVectorComponent(pos,1);

              var dir = lightList[i].getDirection();
              dir = c3dl.multiplyMatrixByDirection(c3dl.peekMatrix(), dir);

              this.setUniformf(shader, base + "position", pos, scene, "light"+i);
              this.setUniformf(shader, base + "spotDirection", dir, scene, "light"+i);
              this.setUniformf(shader, base + "spotCutoff", lightList[i].getCutoff(), scene, "light"+progObjIter+i);
              this.setUniformf(shader, base + "spotExponent", lightList[i].getExponent(), scene, "light"+progObjIter+i);
            }

            else if (lightList[i] instanceof c3dl.PositionalLight)
            {
              var pos = lightList[i].getPosition();
              //pos = c3dl.multiplyMatrixByVector(c3dl.getUniform("viewMatrix"), pos);
              pos = c3dl.multiplyMatrixByVector(c3dl.peekMatrix(), pos);
              pos = c3dl.addVectorComponent(pos,1);

              this.setUniformf(shader, base + "position", pos, scene, "light"+progObjIter+i);
              this.setUniformf(shader, base + "spotCutoff", 180.0, scene, "light"+progObjIter+i);
            }

            this.setUniformi(shader, base + "type", lightList[i].getType(), scene, "light"+progObjIter+i);
            this.setUniformi(shader, base + "isOn", lightList[i].isOn(), scene, "light"+progObjIter+i);
            this.setUniformf(shader, base + "ambient", lightList[i].getAmbient(), scene, "light"+progObjIter+i);
            this.setUniformf(shader, base + "diffuse", lightList[i].getDiffuse(), scene, "light"+progObjIter+i);
            this.setUniformf(shader, base + "specular", lightList[i].getSpecular(), scene, "light"+progObjIter+i);

            // lights are attenuated as long as they are not directional lights
            if (!(lightList[i] instanceof c3dl.DirectionalLight))
            {
              var attn = lightList[i].getAttenuation();
              this.setUniformf(shader, base + "attenuation1", attn[0], scene, "light"+progObjIter+i);
              this.setUniformf(shader, base + "attenuation2", attn[1], scene, "light"+progObjIter+i);
              this.setUniformf(shader, base + "attenuation3", attn[2], scene, "light"+progObjIter+i);
            }
          }
        }
      }
    }
  }

/*

  */
  this.pointSphereRenderSetup = function ()
  {
    // create the empty WebGL VBO's
    this.pointSphereVBOVert = glCanvas3D.createBuffer();

    // bind to the VBO
    glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.pointSphereVBOVert);

    // set the data using the bounding sphere verts since that sphere has a size of 1 unit
    glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(c3dl.BOUNDING_SPHERE_VERTICES), glCanvas3D.STATIC_DRAW);

    // next frame we'll be ready to render
    this.pointSphereRenderReady = true;
  }

  /**
   @private
   
   Create a Renderer.
   
   @param cvs
   
   @returns {boolean} True if the context could be created, 
   otherwise false.
   */
  this.createRenderer = function (cvs)
  {
    if (c3dl.debug.DUMMY)
    {
      glCanvas3D =
      {
      };
      glCanvas3D.__noSuchMethod__ = function ()
      {
        return true;
      }
    }
    else
    {
      try
      {
        glCanvas3D = cvs.getContext('experimental-webgl');
        glCanvas3D.viewport(0, 0, cvs.width, cvs.height);
      }
      catch (err)
      {
      }
    }

    return glCanvas3D ? true : false;
  }

  /**
   @private
   Enables depth testing, create necessary shaders, create the projection
   matrix and set the lighting uniform.
   
   Compiles and link the shaders.
   
   @param {int} width of the canvas in pixels.
   @param {int} height of the canvas in pixels.
   */
  this.init = function (width, height,scene)
  {
    if (glCanvas3D == null)
    {
      return false;
    }

    // set the context width and height. These are the base class
    // members.
    this.contextWidth = width;
    this.contextHeight = height;
    this.scene = scene;

    // enable the depth buffer, only needs to be done once, so do it here		
    glCanvas3D.enable(glCanvas3D.DEPTH_TEST);

    // create the shader programs
    //this.geometryShader = this.createProgram(c3dl.material_vs+c3dl.light_vs+c3dl.model_vs, c3dl.model_fs).getProgramID();
    this.particleSystemShader = this.createProgram(c3dl.psys_vs, c3dl.psys_fs).getProgramID();
    this.pointShader = this.createProgram(c3dl.point_vs, c3dl.point_fs).getProgramID();
    this.lineShader = this.createProgram(c3dl.line_vs, c3dl.line_fs).getProgramID();
    this.pointSphereShader = this.createProgram(c3dl.point_sphere_vs, c3dl.point_sphere_fs).getProgramID();
    this.boundingSphereShader = this.createProgram(c3dl.bounding_sphere_vs, c3dl.bounding_sphere_fs).getProgramID();

    // Template effects
    // STANDARD
    c3dl.effects.STD_EFFECT = new c3dl.EffectTemplate();
    c3dl.effects.STD_EFFECT.addVertexShader(c3dl.material_vs + c3dl.light_vs + c3dl.model_vs);
    c3dl.effects.STD_EFFECT.addFragmentShader(c3dl.model_fs);
    c3dl.effects.STD_EFFECT.setRenderingCallback(c3dl.std_callback);
    c3dl.effects.STD_EFFECT.init();

    c3dl.effects.STANDARD = new c3dl.Effect();
    c3dl.effects.STANDARD.init(c3dl.effects.STD_EFFECT);
    var prog = this.createProgram(c3dl.material_vs + c3dl.light_vs + c3dl.model_vs, c3dl.model_fs);

    c3dl.effects.STANDARD.getEffectTemplate().addProgramObject(prog);
    this.programsWithLights.push(c3dl.effects.STANDARD.getEffectTemplate().getProgramID(this.ID));
    this.STANDARD_PROGRAM_ID = prog.getProgramID();

    // need to create the solid color effect explicitly
    // since effects are really only created if an object uses them.
    // and since we need it for gooch and cartoon....
    ///
    c3dl.effects.SOLID_COLOR_EFFECT_TEMP = new c3dl.EffectTemplate();
    c3dl.effects.SOLID_COLOR_EFFECT_TEMP.addVertexShader(c3dl.solid_color_vs);
    c3dl.effects.SOLID_COLOR_EFFECT_TEMP.addFragmentShader(c3dl.solid_color_fs);
    c3dl.effects.SOLID_COLOR_EFFECT_TEMP.setRenderingCallback(c3dl.solid_color_callback);
    c3dl.effects.SOLID_COLOR_EFFECT_TEMP.init();

    c3dl.effects.SOLID_COLOR_EFFECT = new c3dl.Effect();
    c3dl.effects.SOLID_COLOR_EFFECT.init(c3dl.effects.SOLID_COLOR_EFFECT_TEMP);
    var prog = this.createProgram(c3dl.solid_color_vs, c3dl.solid_color_fs);

    c3dl.effects.SOLID_COLOR_EFFECT.getEffectTemplate().addProgramObject(prog);
    this.SOLID_COLOR_EFFECT_ID = prog.getProgramID();

    // GREYSCALE
    c3dl.effects.GREYSCALE = new c3dl.EffectTemplate();
    c3dl.effects.GREYSCALE.addVertexShader(c3dl.material_vs);
    c3dl.effects.GREYSCALE.addVertexShader(c3dl.light_vs);
    c3dl.effects.GREYSCALE.addVertexShader(c3dl.greyscale_vs);
    c3dl.effects.GREYSCALE.addFragmentShader(c3dl.greyscale_fs);
    c3dl.effects.GREYSCALE.setRenderingCallback(c3dl.greyscale_callback);
    c3dl.effects.GREYSCALE.addParameter("color", Array, [0.3, 0.6, 0.1]);
    c3dl.effects.GREYSCALE.init();

    // SOLID COLOR    
    c3dl.effects.SOLID_COLOR = new c3dl.EffectTemplate();
    c3dl.effects.SOLID_COLOR.addVertexShader(c3dl.solid_color_vs);
    c3dl.effects.SOLID_COLOR.addFragmentShader(c3dl.solid_color_fs);
    c3dl.effects.SOLID_COLOR.setRenderingCallback(c3dl.solid_color_callback);
    c3dl.effects.SOLID_COLOR.addParameter("color", Array, [0.0, 0.0, 0.0]);
    c3dl.effects.SOLID_COLOR.init();

    // SEPIA
    c3dl.effects.SEPIA = new c3dl.EffectTemplate();
    c3dl.effects.SEPIA.addVertexShader(c3dl.material_vs);
    c3dl.effects.SEPIA.addVertexShader(c3dl.light_vs);
    c3dl.effects.SEPIA.addVertexShader(c3dl.sepia_vs);
    c3dl.effects.SEPIA.addFragmentShader(c3dl.sepia_fs);
    c3dl.effects.SEPIA.setRenderingCallback(c3dl.sepia_callback);
    c3dl.effects.SEPIA.addParameter("color", Array, [1.2, 1.0, 0.8]);
    c3dl.effects.SEPIA.init();

    // CARTOON
    c3dl.effects.CARTOON = new c3dl.EffectTemplate();
    c3dl.effects.CARTOON.addVertexShader(c3dl.cartoon_vs);
    c3dl.effects.CARTOON.addFragmentShader("#ifdef GL_ES \n precision highp float; \n #endif \n ");
    c3dl.effects.CARTOON.addFragmentShader(c3dl.light_vs);
    c3dl.effects.CARTOON.addFragmentShader(c3dl.cartoon_fs);
    c3dl.effects.CARTOON.setRenderingCallback(c3dl.cartoon_callback);
    c3dl.effects.CARTOON.addParameter("qMap", String);
    c3dl.effects.CARTOON.addParameter("outline", Boolean, true);
    c3dl.effects.CARTOON.init();

    // GOOCH
    c3dl.effects.GOOCH = new c3dl.EffectTemplate();
    c3dl.effects.GOOCH.addVertexShader(c3dl.gooch_vs);
    c3dl.effects.GOOCH.addFragmentShader("#ifdef GL_ES \n precision highp float; \n #endif \n ");
    c3dl.effects.GOOCH.addFragmentShader(c3dl.light_vs);
    c3dl.effects.GOOCH.addFragmentShader(c3dl.gooch_fs);
    c3dl.effects.GOOCH.setRenderingCallback(c3dl.gooch_callback);
    c3dl.effects.GOOCH.addParameter("coolColor", Array, [0, 0, 1]);
    c3dl.effects.GOOCH.addParameter("warmColor", Array, [0.5, 0.5, 0.0]);
    c3dl.effects.GOOCH.addParameter("surfaceColor", Array, [0.1, 0.1, 0.1]);
    c3dl.effects.GOOCH.addParameter("outline", Boolean, true);
    c3dl.effects.GOOCH.init();


    // Custom Effect
    c3dl.effects.CUSTOM_EFFECT = new c3dl.EffectTemplate();
    c3dl.effects.CUSTOM_EFFECT.addVertexShader(c3dl.custom_vs);
    c3dl.effects.CUSTOM_EFFECT.addFragmentShader("#ifdef GL_ES \n precision highp float; \n #endif \n ");
    c3dl.effects.CUSTOM_EFFECT.addFragmentShader(c3dl.light_vs);
    c3dl.effects.CUSTOM_EFFECT.addFragmentShader(c3dl.custom_fs);
    c3dl.effects.CUSTOM_EFFECT.setRenderingCallback(c3dl.custom_callback);
    c3dl.effects.CUSTOM_EFFECT.init();

    
    this.texManager = new c3dl.TextureManager(glCanvas3D);

    // iterate over all the textures the user added before the renderer
    // was initialized and add them now.
    for (var i in this.textureQueue)
    {
      if (this.textureQueue[i])
      {
        this.texManager.addTexture(this.textureQueue[i]);
      }
    }

    return true;
  }

  /**
   @private
   Render the bounding sphere
   
   @param {c3dl.BoundingSphere} boundingSphere
   */
  this.renderBoundingSphere = function (boundingSphere,viewMatrix, scene)
  {
    // create an short alias
    var shader = this.boundingSphereShader;
    glCanvas3D.useProgram(shader);

    if (this.pointSphereRenderReady == false)
    {
      // create VBO and set the data
      this.pointSphereRenderSetup();
    }
    else {
      var sphereMatrix = c3dl.makeIdentityMatrix();
      c3dl.matrixMode(c3dl.PROJECTION);
      var projMatrix = c3dl.peekMatrix();
      c3dl.matrixMode(c3dl.MODELVIEW); 
      // set the bounding sphere's position
      var pos =boundingSphere.getPosition();
      sphereMatrix[12] = pos[0];
      sphereMatrix[13] = pos[1];
      sphereMatrix[14] = pos[2];
      sphereMatrix[0] = sphereMatrix[5] = sphereMatrix[10] = boundingSphere.getRadius();

      // create a modelviewprojection matrix.  By doing this, we can multiply
      // 3 matrices together once per model instead of once per vertex.
      var sphereViewMatrix = c3dl.multiplyMatrixByMatrix(viewMatrix,sphereMatrix);
	  
      var MVPMatrix = c3dl.multiplyMatrixByMatrix(projMatrix, sphereViewMatrix);
      this.setUniformMatrix(shader, "modelViewProjMatrix", MVPMatrix, scene, "boundingSphereShader");
      this.setVertexAttribArray(shader, "Vertex", 3, this.pointSphereVBOVert, scene, "boundingSphereShader");
      glCanvas3D.drawArrays(glCanvas3D.POINTS, 0, c3dl.BOUNDING_SPHERE_VERTICES.length / 3);
    }
  }

  /**
   @private
   Render a geometry
   
   @param {c3dl.Geometry} obj
   */
  this.renderGeometry = function (obj, scene)
  {
    // get the object's effect
    if (obj.getEffect())
    {
      //
      var effect = obj.getEffect().getEffectTemplate();

      var progObjID = effect.getProgramID(this.ID);

      // If the effect's shaders have not yet been compiled for this renderer,
      // compile them.
      if (progObjID == -1)
      {
        var vertexShaders = effect.getVertexShaders();
        var fragmentShaders = effect.getFragmentShaders();

        // join all the shaders, but don't insert a delimiter, that
        // would create a syntax error for the shaders.
        var joinedVertexShaders = vertexShaders.join('');
        var joinedFragmentShaders = fragmentShaders.join('');

        var programObject = this.createProgram(joinedVertexShaders, joinedFragmentShaders);

        // if the effect was successfully compiled, render it using the effect
        if (programObject)
        {
          effect.addProgramObject(programObject);

          // check if the user added the light vertex shader source.
          // if they did, we'll need to update the light states every
          // update for that shader.
          for (var i = 0, len = vertexShaders.length; i < len; i++)
          {
            if (vertexShaders[i] == c3dl.light_vs)
            {
              this.programsWithLights.push(programObject.getProgramID());
              glCanvas3D.useProgram(programObject.getProgramID());
              this.setUniformi(programObject.getProgramID(), "lightingOn", true, scene, "vertexfragmentShaders");
            }
          }

          for (var i = 0, len = fragmentShaders.length; i < len; i++)
          {
            if (fragmentShaders[i] == c3dl.light_vs)
            {
              this.programsWithLights.push(programObject.getProgramID());
              glCanvas3D.useProgram(programObject.getProgramID());
              this.setUniformi(programObject.getProgramID(), "lightingOn", true, scene, "vertexfragmentShaders");
            }
          }
        }
        else
        {
          c3dl.debug.logWarning("could not compile effect shader(s).");
          c3dl.debug.logInfo(joinedVertexShaders);
          c3dl.debug.logInfo(joinedFragmentShaders);
        }
      }
      // if the effect has already been compiled, go ahead and render the geometry.
      else
      {
        var renderingObj =
        {
        };

        // user will need to query states of the context.
        renderingObj["context"] = glCanvas3D;
        renderingObj["getContext"] = function ()
        {
          return this.context;
        };

        // user will need to query states of the renderer.
        // enventually the renderer will completely abtract the context and
        // the context will not need to be passed in. But that requires all
        // functions to be wrapped.
        renderingObj["renderer"] = this;
        renderingObj["getRenderer"] = function ()
        {
          return this.renderer;
        };

        // user will need to set the current program.
        renderingObj['programObjectID'] = progObjID;
        renderingObj['getProgramObjectID'] = function ()
        {
          return this.programObjectID;
        };

        // user will need the actual geometry to render.
        renderingObj['geometry'] = obj;
        renderingObj['getGeometry'] = function ()
        {
          return this.geometry;
        };

        var cb = effect.getRenderingCallback();
        cb(renderingObj, scene);
      }

    }

    // if the geometry didn't have an effect, we'll render it using
    // the standard shader.
    else
    {
      var renderingObj =
      {
      };

      //
      renderingObj["context"] = glCanvas3D;
      renderingObj["getContext"] = function ()
      {
        return this.context;
      };

      //
      renderingObj["renderer"] = this;
      renderingObj["getRenderer"] = function ()
      {
        return this.renderer;
      };

      //
      renderingObj['programObjectID'] = this.STANDARD_PROGRAM_ID;
      renderingObj['getProgramObjectID'] = function ()
      {
        return this.programObjectID;
      };

      //
      renderingObj['geometry'] = obj;
      renderingObj['getGeometry'] = function ()
      {
        return this.geometry;
      };

      c3dl.std_callback(renderingObj, scene);
    }
  }
  this.renderShape = function (obj, scene)
  {
      var renderingObj =
      {
      };

      //
      renderingObj["context"] = glCanvas3D;
      renderingObj["getContext"] = function ()
      {
        return this.context;
      };

      //
      renderingObj["renderer"] = this;
      renderingObj["getRenderer"] = function ()
      {
        return this.renderer;
      };

      //
      renderingObj['programObjectID'] = this.STANDARD_PROGRAM_ID;
      renderingObj['getProgramObjectID'] = function ()
      {
        return this.programObjectID;
      };

      //
      renderingObj['geometry'] = obj;
      renderingObj['getGeometry'] = function ()
      {
        return this.geometry;
      };
      c3dl.std_callback(renderingObj, scene);

  }
  /**
   @private	
   Render a particle system.
   
   @param {c3dl.ParticleSystem} psys
   */
  this.renderParticleSystem = function (psys, scene)
  {
    // create shorter alias
    var shader = this.particleSystemShader;
    glCanvas3D.useProgram(shader);

    var usingTexture = false;
    var texturePath = psys.getTexture();
    var texID = this.texManager.getID(texturePath);
    var texAttribLoc = glCanvas3D.getAttribLocation(shader, "Texture");

    // if the texture isn't loaded, but this collation element has one, 
    // queue one up
    if (texID == -1 && texturePath)
    {
      this.texManager.addTexture(texturePath);
    }

    if (texID != -1 && texturePath && psys.getTexCoords())
    {
      glCanvas3D.activeTexture(glCanvas3D.TEXTURE0);
      glCanvas3D.bindTexture(glCanvas3D.TEXTURE_2D, texID);
      this.setVertexAttribArray(shader, "Texture", 2, psys.getVBOTexCoords(), scene, "particleSystemShader");
      usingTexture = true;

      glCanvas3D.texParameteri(glCanvas3D.TEXTURE_2D, glCanvas3D.TEXTURE_WRAP_S, glCanvas3D.CLAMP_TO_EDGE);
      glCanvas3D.texParameteri(glCanvas3D.TEXTURE_2D, glCanvas3D.TEXTURE_WRAP_T, glCanvas3D.CLAMP_TO_EDGE);
    }
    else
    {
      glCanvas3D.activeTexture(glCanvas3D.TEXTURE0);
      glCanvas3D.disableVertexAttribArray(texAttribLoc);
      //glCanvas3D.bindTexture(glCanvas3D.TEXTURE_2D,-1);
    }
    this.setUniformi(shader, "usingTexture", usingTexture, scene, "particleSystemShader");
    this.setUniformMatrix(shader, "rot", [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], scene, "particleSystemShader");
    this.setVertexAttribArray(shader, "Vertex", 3, psys.getVBOVertices(), scene, "particleSystemShader");

    for (var i = 0, numParticles = psys.getNumParticles(); i < numParticles; i++)
    {
      if (psys.getParticle(i).isAlive())
      {
        var pSize = psys.getParticle(i).getSize();

        this.setUniformf(shader, "Color", psys.getParticle(i).getColor(), scene, "particleSystemShader");

        c3dl.matrixMode(c3dl.PROJECTION);
        var projectionMatrix = c3dl.peekMatrix();
        c3dl.matrixMode(c3dl.MODELVIEW);
        var viewMatrix = c3dl.peekMatrix();

        //
        var modelMatrix = psys.getParticle(i).getTransform();
        modelMatrix = c3dl.multiplyMatrixByMatrix(modelMatrix, [pSize, 0, 0, 0, 0, pSize, 0, 0, 0, 0, 
          pSize, 0, 0, 0, 0, 1]);

        // create a ModelViewProjection matrix.  By doing this, we can multiply
        // 3 matrices together once per particle instead of once per vertex
        var modelViewProjMatrix = c3dl.multiplyMatrixByMatrix(viewMatrix, modelMatrix);
        modelViewProjMatrix = c3dl.multiplyMatrixByMatrix(projectionMatrix, modelViewProjMatrix);
        this.setUniformMatrix(shader, "modelViewProjMatrix", modelViewProjMatrix, scene, "particleSystemShader");

        glCanvas3D.drawArrays(glCanvas3D.TRIANGLE_FAN, i, 4);
      }
    }
  }

  /**
   @private
   Render a set of lines.
   
   @param {Array} lines
   */
  this.renderLines = function (lines, scene)
  {
    if (lines.length > 0)
    {
      // create a short alias for this.lineShader
      var shader = this.lineShader;
      glCanvas3D.useProgram(shader);

      // camera placed the view matrix at the bottom of the stack
      var modelViewMatrix = c3dl.peekMatrix();
      c3dl.matrixMode(c3dl.PROJECTION);
      var projectionMatrix = c3dl.peekMatrix();
      c3dl.matrixMode(c3dl.MODELVIEW);

      // create a ModelViewProjection matrix.  By doing this, we can multiply
      // 3 matrices together once per model instead of once per vertex
      var modelViewProjMatrix = c3dl.multiplyMatrixByMatrix(projectionMatrix, modelViewMatrix);
      this.setUniformMatrix(shader, "modelViewProjMatrix", modelViewProjMatrix, scene, "lineShader");

      // we need to render each line individually since each can have 
      // a different width.
      for (var l = 0, len = lines.length; l < len; l++)
      {
        glCanvas3D.lineWidth(lines[l].getWidth());

        var coords = [];
        var cols = [];
        for (var i = 0; i < 6; i++)
        {
          coords.push(lines[l].getCoordinates()[i]);
          cols.push(lines[l].getColors()[i]);
        }

        if (this.lineVertBuffer == null)
        {
          this.lineVertBuffer =
          {
          };
          this.lineVertBuffer.position = glCanvas3D.createBuffer();
        }

        glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.lineVertBuffer.position);
        glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(coords), glCanvas3D.STREAM_DRAW);
        this.setVertexAttribArray(shader, "Vertex", 3, this.lineVertBuffer.position, scene, "lineShader");

        if (this.lineColBuffer == null)
        {
          this.lineColBuffer =
          {
          };
          this.lineColBuffer.position = glCanvas3D.createBuffer();
        }

        glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.lineColBuffer.position);
        glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(cols), glCanvas3D.STREAM_DRAW);
        this.setVertexAttribArray(shader, "Color", 3, this.lineColBuffer.position, scene, "lineShader");

        glCanvas3D.drawArrays(glCanvas3D.LINES, 0, coords.length / 3);
      }
    }
  }

  /**
   @private
   @param {Array} pointPositions
   @param {Array} pointColors
   @param {Array} attenuation
   @param {int} mode
   @param {float} size
   */
  this.renderPoints = function (pointPositions, pointColors, attenuation, mode, size, scene)
  {
    // trying to render an empty list will result in an WebGL error
    if (pointPositions.length > 0 && pointColors.length > 0)
    {
      if (mode == c3dl.POINT_MODE_POINT)
      {
        // create a shorter reference name.
        var shader = this.pointShader;
        glCanvas3D.useProgram(shader);

        // camera placed the view matrix at the bottom of the stack
        var viewMatrix = c3dl.peekMatrix();
        c3dl.matrixMode(c3dl.PROJECTION);
        var projectionMatrix = c3dl.peekMatrix();
        c3dl.matrixMode(c3dl.MODELVIEW);

        // create a ModelViewProjection matrix.  By doing this, we can multiply
        // 3 matrices together once instead of once per point
        var modelViewProjMatrix = c3dl.multiplyMatrixByMatrix(projectionMatrix, viewMatrix, scene, "pointShader");
        this.setUniformMatrix(shader, "viewMatrix", viewMatrix, scene, "pointShader");
        this.setUniformMatrix(shader, "modelViewProjMatrix", modelViewProjMatrix, scene, "pointShader");
        this.setUniformf(shader, "attenuation", attenuation, scene, "pointShader");

        if (this.pointVertBuffer == null)
        {
          this.pointVertBuffer =
          {
          };
          this.pointVertBuffer.position = glCanvas3D.createBuffer();
        }

        glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.pointVertBuffer.position);
        glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(pointPositions), glCanvas3D.STREAM_DRAW);
        this.setVertexAttribArray(shader, "Vertex", 3, this.pointVertBuffer.position, scene, "pointShader");

        if (this.pointColBuffer == null)
        {
          this.pointColBuffer =
          {
          };
          this.pointColBuffer.position = glCanvas3D.createBuffer();
        }

        glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, this.pointColBuffer.position);
        glCanvas3D.bufferData(glCanvas3D.ARRAY_BUFFER, new WebGLFloatArray(pointColors), glCanvas3D.STREAM_DRAW);
        this.setVertexAttribArray(shader, "Color", 3, this.pointColBuffer.position, scene, "pointShader");
        glCanvas3D.drawArrays(glCanvas3D.POINTS, 0, pointPositions.length / 3);
      }
      else if (mode == c3dl.POINT_MODE_SPHERE)
      {
        // create an short alias
        var shader = this.pointSphereShader;
        glCanvas3D.useProgram(shader);

        if (this.pointSphereRenderReady == false)
        {
          // create vbo and set the data
          this.pointSphereRenderSetup();
        }
        else
        {
          c3dl.pushMatrix();

          for (var i = 0, len = pointPositions.length; i < len; i += 3)
          {
            // 
            var mat = c3dl.makeIdentityMatrix();
            mat[12] = pointPositions[i];
            mat[13] = pointPositions[i + 1];
            mat[14] = pointPositions[i + 2];
            mat[0] = mat[5] = mat[10] = size;

            mat = c3dl.multiplyMatrixByMatrix(c3dl.peekMatrix(), mat);

            c3dl.matrixMode(c3dl.PROJECTION);
            var proj = c3dl.peekMatrix();
            c3dl.matrixMode(c3dl.MODELVIEW);

            // create a modelviewprojection matrix.  By doing this, we can multiply
            // 3 matrices together once per model instead of once per vertex.
            var MVPMatrix = c3dl.multiplyMatrixByMatrix(proj, mat);
            this.setUniformMatrix(shader, "modelViewProjMatrix", MVPMatrix, scene, "pointShader");
            this.setUniformf(shader, "Color", [pointColors[i], pointColors[i + 1], pointColors[i + 2]], scene, "pointShader");

            this.setVertexAttribArray(shader, "Vertex", 3, this.pointSphereVBOVert, scene, "pointShader");
            glCanvas3D.drawArrays(glCanvas3D.TRIANGLES, 0, c3dl.BOUNDING_SPHERE_VERTICES.length / 3);
            c3dl.popMatrix();
          }
        }
      }
    }
  }

  /**
   @private
   
   @param {int} shader
   @param {String} varName
   @param {int} size
   @param vbo
   */
  this.setVertexAttribArray = function (shader, varName, size, vbo, scene, shaderName)
  {
    var attribLoc = scene.curContextCache.attributes[shaderName+varName];
    if(attribLoc ==undefined) {
      attribLoc = glCanvas3D.getAttribLocation(shader, varName);
      scene.curContextCache.attributes[shaderName+varName] = attribLoc;
    }
    if (attribLoc != c3dl.SHADER_VAR_NOT_FOUND)
    {
      glCanvas3D.bindBuffer(glCanvas3D.ARRAY_BUFFER, vbo);
      glCanvas3D.vertexAttribPointer(attribLoc, size, glCanvas3D.FLOAT, false, 0, 0);
      glCanvas3D.enableVertexAttribArray(attribLoc);
    }
    else
    {
      c3dl.debug.logError("Attribute variable '" + varName + "' not found in shader with ID = " + shader);
    }
  }

  /**
   Sets the uniform matrix variable 'varName' to the value specified by 'value'.
   Before calling this function, you must ensure the program object with
   the variable name 'varName' has been installed as part of the current
   rendering state by calling useProgram().<br />
   <br />
   The size of the matrix must be 16 elements.<br />
   <br />
   On some systems, if the variable exists in the shader but isn't used,
   the compiler will remove it. An error message will be displayed if the
   non-existant variable is set.<br />
   <br />
   If the variable could not be found for any other reason, an error is 
   displayed.
   
   @param {int} programObjectID The shader where the variable resides.
   @param {String} varName The name of the matrix variable.
   @param {Array} matrix 16 element matrix.
   */
  this.setUniformMatrix = function (programObjectID, varName, matrix, scene, programObjectName)
  {
    var varLocation = scene.curContextCache.locations[programObjectName+varName];
    if(!varLocation) {
      varLocation = glCanvas3D.getUniformLocation(programObjectID, varName);
      scene.curContextCache.locations[programObjectName+varName] = varLocation;
    }
    // the variable won't be found if it was optimized out.
    if (varLocation != c3dl.SHADER_VAR_NOT_FOUND)
    {
      glCanvas3D.uniformMatrix4fv(varLocation, false, matrix);
    }
    else
    {
      c3dl.debug.logError("Uniform matrix variable '" + varName + "' not found in program object.");
    }
  }

  /**
   Sets the uniform variable 'varName' to the value specified by 'value'.
   Before calling this function, you must ensure the program object with
   the variable name 'varName' has been installed as part of the current
   rendering state by calling useProgram().<br />
   <br />
   The size of the value will be queried and depending on its size, the 
   appropriate WebGL command will be called either uniform1f or 
   uniform{1|2|3|4}fv.<br />
   <br />
   On some systems, if the variable exists in the shader but isn't used,
   the compiler will remove it. An error message will be displayed if the
   non-existant variable is set.<br />
   <br />
   If the variable could not be found for any other reason, an error is 
   displayed.
   
   @param {int} programObjectID The shader where the variable resides.
   @param {String} varName The name of the variable.
   @param {float|Array} value The value to assign the variable.
   */
  this.setUniformf = function (shader, varName, value, scene, shaderName)
  {
    var varLocation = scene.curContextCache.locations[shaderName+varName];
    if(!varLocation) {
      varLocation = glCanvas3D.getUniformLocation(shader, varName);
      scene.curContextCache.flag = 0;
      scene.curContextCache.locations[shaderName+varName] = varLocation;
    }
    // the variable won't be found if it was optimized out.
    if (varLocation != c3dl.SHADER_VAR_NOT_FOUND)
    {
      if (value.length == 4)
      {
        glCanvas3D.uniform4fv(varLocation, value);
      }
      else if (value.length == 3)
      {
        glCanvas3D.uniform3fv(varLocation, value);
      }
      else if (value.length == 2)
      {
        glCanvas3D.uniform2fv(varLocation, value);
      }
      else
      {
        glCanvas3D.uniform1f(varLocation, value);
      }
    }
    else
    {
      c3dl.debug.logError('Uniform variable "' + varName + '" not found in program object.');
    }
  }

  /**
   Sets the uniform variable 'varName' to the value specified by 'value'.
   Before calling this function, you must ensure the program object with
   the variable name 'varName' has been installed as part of the current
   rendering state by calling useProgram().<br />
   <br />
   The size of the value will be queried and depending on its size, the 
   appropriate WebGL command will be called either uniform1i or 
   uniform{1|2|3|4}iv.<br />
   <br />
   On some systems, if the variable exists in the shader but isn't used,
   the compiler will remove it. An error message will be displayed if the
   non-existant variable is set.<br />
   <br />
   If the variable could not be found for any other reason, an error is 
   displayed.
   
   @param {int} programObjectID The shader where the variable resides.
   @param {String} varName The name of the variable.
   @param {int|Array} value The value to assign the variable.
   */
  this.setUniformi = function (programObjectID, varName, value, scene, programObjectName)
  {
   var varLocation = scene.curContextCache.locations[programObjectName+varName];
    if(!varLocation) {
      varLocation = glCanvas3D.getUniformLocation(programObjectID, varName);
      scene.curContextCache.locations[programObjectName+varName] = varLocation;
    }
    // the variable won't be found if it was optimized out.
    if (varLocation != c3dl.SHADER_VAR_NOT_FOUND)
    {
      if (value.length == 4)
      {
        glCanvas3D.uniform4iv(varLocation, value);
      }
      else if (value.length == 3)
      {
        glCanvas3D.uniform3iv(varLocation, value);
      }
      else if (value.length == 2)
      {
        glCanvas3D.uniform2iv(varLocation, value);
      }
      else
      {
        glCanvas3D.uniform1i(varLocation, value);
      }
    }
    else
    {
      c3dl.debug.logError('Uniform variable "' + varName + '" not found in program object.');
    }
  }

  /**
   @private
   Enable an WebGL server-side capability.
   
   @param {int} capability
   */
  this.enable = function (capability)
  {
    try
    {
      // check if its defined
      if (capability)
      {
        glCanvas3D.enable(capability);
      }
      else
      {
        c3dl.debug.logWarning("Enable command passed undefined value.");
      }
    }
    catch (e)
    {
      c3dl.debug.logException("Exception name:" + e.name +
        "<br />" + "Exception msg: " + e.message + "<br />" +
        "Capability: " + capability);
    }
  }

  /**
   @private
   
   Disable a server-side WebGL capability. This wraps the WebGL 
   'disable' command and adds a conditional to make sure the capability is
   supported before trying to change the state.  If the capability is not
   supported and the state is change, the script could be prevented 
   from running.
   
   @param {int} capability WebGL capability
   */
  this.disable = function (capability)
  {
    if (capability)
    {
      glCanvas3D.disable(capability);
    }
    else
    {
      c3dl.debug.logWarning("disable command passed undefined value.");
    }
  }
});

リムライティングシェーダーオブジェクトの初期化を行います。既存のソースを修正します。非常に長いですが、追加したのはたった7行です。
あとトップページのソースは、例によって[ サンプルページの表示 ]でサンプルページを表示して、 ソースを参照してください。


サンプルについてですが、平面が多い形状のメッシュを使用しているため、余計な部分にまで光があふれています。 実用化するには、テクスチャーを使用して光の反射率を制限するなどしないとだめだと思います。


Canvas 3d JS Library は今回で一応終了します。で使った感想。
とにかく初期化が遅い!!
ライブラリの容量が1.13MBもあるせいなのでしょうが、使わないソースは読み込まないように修正しないとちょっと使えないかも。

機能としては COLLADA が使えるし、セルシェーダーといったシェーダーが標準で使えるしで、結構高機能かも。


最後に対応するブラウザは、Google Chrome のみです。以上!!


web拍手 by FC2

Prev Top Next

inserted by FC2 system