2015/06/25 現在執筆中。このページの情報はまだ不完全です。Please skip thie page.

WebGL (8)


点光源がある場合に、影をどのように計算するかを考えます。

点光源の位置にカメラを置いて画像を生成します。このとき、各ピクセルの色を決定した物体までの距離がzバッファにありますので これをテクスチャとして保存します。

本来のカメラの位置で画像を生成します。 フラグメントシェーダで各フラグメントの色を決定するとき、 そのフラグメントと点光源の距離を計算します。 その距離が テクスチャに保存されているzバッファの値と同じならば間に物体は存在しないので 明るくなります。もしもzバッファの値が小さければ間に物体があるということになるので 点光源の光は直接当たっていないことになります。

シャドウマップを生成している部分の説明をします。フラグメントシェーダで渡されてくる gl_FragCoord.zは0から1.0の間の値をもちます。これを16進数表現の小数で $$ 0.PQRSTUVW\cdots$$ と表せたとしましょう。アルファベットはそれぞれ16進数(4bit幅で表現できる数, 0-9,A-Fのどれか)を 表しているものとします。 256倍するということは左方向に8bitシフトするということですから、 それぞれ $1$倍、$256$倍、$256^2$倍、$256^3$倍すると $$(0.PQRSTUVW\cdots, PQ.RSTUVW\cdots, PQRS.TUVW\cdots, PQRSTU.VW\cdots)$$ となります。fract関数を使って小数部分だけを取り出すと $$(0.PQRSTUVW\cdots, 0.RSTUVW\cdots, 0.TUVW\cdots, 0.VW\cdots)$$ となります。これが rgbaDepth ベクトル変数の値になるわけですが、さて、 このベクトル要素のうちrgbaではなくてgbaaを取り出したベクトル $$(0.RSTUVW\cdots, 0.TUVW\cdots, 0.VW\cdots, 0.VW\cdots)$$ の要素それぞれに$(1/256, 1/256, 1/256, 0)$の対応する要素を掛けると $$(0.00RSTUVW\cdots, 0.00TUVW\cdots, 0.00VW\cdots, 0) $$ となります。これをrgbaDepth.rgbaから引いてあげると $$ \begin{array}{rlllll} ( & 0.PQRSTUVW\cdots, & 0.RSTUVW\cdots, &0.TUVW\cdots, & 0.VW\cdots & )\\ -( & 0.00RSTUVW\cdots, & 0.00TUVW\cdots, & 0.00VW\cdots, & 0& )\\ =(& 0.PQ, & 0.RS, & 0.TU, & 0.VW\cdots& ) \end{array} $$ となります。それぞれ8bitで表現できる範囲の埴なので これをフレームバッファのRGBAそれぞれに入れて記憶します。 $0.VW\cdots$のうち$0.VW$より小さい部分は 元の浮動小数が32bitならばそもそも存在しませんし、 たとえ存在しても非常に小さい値なので失なわれても問題ありません。

各画素について、光源からの距離が分割されてフレームバッファに入っていますので、 これテクスチャとして第2段階のレンダリングに渡してシャドウマップとして利用します。

第2段階のレンダリング時にシャドウマップからz値を取り出すには $(1, 256^{-1}, 256^{-2}, 256^{-3}))$との内積を計算して $$ (0.PQ, 0.RS, 0.TU, 0.VW)\cdot (1, 256^{-1}, 256^{-2}, 256^{-3})) \\ = 0.PQ + 0.00RS + 0.0000TU + 0.000000VW \\ = 0.PQRSTUVW$$ とします。

各頂点の「視点を原点とするビュー座標系での座標」と 「光源を原点とするビュー座標系での座標」は、 それぞれ対応する行列を掛けると計算できます。 フラグメントシェーダでは、 各頂点の値が補間されて各フラグメントに関する種々の値が 渡ってくるので、各フラグメントに対する正しい値です。 ただし、OpenGLやDirect3DCでは $-1\le x,y,z \le 1$の範囲を$0\le x,y,z \le 1$の範囲に 変換する必要があります。 それには 0.5倍して0.5を足せばよいのです。 行列計算で記述すると次のようになります。

$$ \begin{pmatrix}x'\\ y'\\ z' \\ 1 \end{pmatrix} = \begin{pmatrix} 0.5 & 0 & 0 &x 0.5 \\ 0 & 0.5 & 0 &x 0.5 \\ 0 & 0 & 0.5 &x 0.5 \\ 0 & 0 & 0 &x 1\\ \end{pmatrix} \begin{pmatrix}x\\ y\\ z \\ 1 \end{pmatrix} $$
Shadow.html
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Script-Type" content="text/javascript" />
  <script src="lib/webgl-utils.js"></script>
  <script src="lib/webgl-debug.js"></script>
  <script src="lib/cuon-utils.js"></script>
  <script src="lib/cuon-matrix.js"></script>
  <script src="tsuda-webgl.js"></script>
  <script language="JavaScript" type="text/javascript">
    //<![CDATA
// Shadow_highp.js (c) matsuda and tanaka
var SHADOW_VSHADER_SOURCE = '\
attribute vec4 a_Position;\n\
uniform mat4 u_MvpMatrix;\n\
void main() {\n\
  gl_Position = u_MvpMatrix * a_Position;\n\
}\n\
';

var SHADOW_FSHADER_SOURCE = '\
precision mediump float;\n\
void main() {\n\
  const vec4 bitShift = vec4(1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0);\n\
  const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);\n\
  vec4 rgbaDepth = fract(gl_FragCoord.z * bitShift);\n\
  rgbaDepth -= rgbaDepth.gbaa * bitMask;\n\
  gl_FragColor = rgbaDepth;\n\
}\n\
';

var VSHADER_SOURCE = '\
attribute vec4 a_Position;\n\
attribute vec4 a_Color;\n\
uniform mat4 u_MvpMatrix;\n\
uniform mat4 u_MvpMatrixFromLight;\n\
varying vec4 v_PositionFromLight;\n\
varying vec4 v_Color;\n\
void main() {\n\
  gl_Position = u_MvpMatrix * a_Position;\n\
  v_PositionFromLight = u_MvpMatrixFromLight * a_Position;\n\
  v_Color = a_Color;\n\
}\n\
';

var FSHADER_SOURCE = '\
precision mediump float;\n\
uniform sampler2D u_ShadowMap;\n\
varying vec4 v_PositionFromLight;\n\
varying vec4 v_Color;\n\
float unpackDepth(const in vec4 rgbaDepth) {\n\
  const vec4 bitShift = vec4(1.0, 1.0/256.0, 1.0/(256.0*256.0), 1.0/(256.0*256.0*256.0));\n\
  float depth = dot(rgbaDepth, bitShift);\n\
  return depth;\n\
}\n\
void main() {\n\
  vec3 shadowCoord = (v_PositionFromLight.xyz/v_PositionFromLight.w)/2.0 + 0.5;\n\
  vec4 rgbaDepth = texture2D(u_ShadowMap, shadowCoord.xy);\n\
  float depth = unpackDepth(rgbaDepth);\n\
  float visibility = (shadowCoord.z > depth + 0.0015) ? 0.7 : 1.0;\n\
  gl_FragColor = vec4(v_Color.rgb * visibility, v_Color.a);\n\
}\n\
';

var OFFSCREEN_WIDTH = 2048, OFFSCREEN_HEIGHT = 2048;
var LIGHT_X = 0, LIGHT_Y = 40, LIGHT_Z = 2;

function main() {
  var canvas = document.getElementById('mycanvas');
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('cannot get WebGL context');
    return;
  }

  var shadowProgram = createProgram(gl, SHADOW_VSHADER_SOURCE, SHADOW_FSHADER_SOURCE);
  shadowProgram.a_Position = gl.getAttribLocation(shadowProgram, 'a_Position');
  shadowProgram.u_MvpMatrix = gl.getUniformLocation(shadowProgram, 'u_MvpMatrix');
  if (shadowProgram.a_Position < 0 || !shadowProgram.u_MvpMatrix) {
    console.log('cannot get attribute or uniform variable of shadowProgram');
    return;
  }

  var normalProgram = createProgram(gl, VSHADER_SOURCE, FSHADER_SOURCE);
  normalProgram.a_Position = gl.getAttribLocation(normalProgram, 'a_Position');
  normalProgram.a_Color = gl.getAttribLocation(normalProgram, 'a_Color');
  normalProgram.u_MvpMatrix = gl.getUniformLocation(normalProgram, 'u_MvpMatrix');
  normalProgram.u_MvpMatrixFromLight = gl.getUniformLocation(normalProgram, 'u_MvpMatrixFromLight');
  normalProgram.u_ShadowMap = gl.getUniformLocation(normalProgram, 'u_ShadowMap');
  if (normalProgram.a_Position < 0 || normalProgram.a_Color < 0 || !normalProgram.u_MvpMatrix ||
      !normalProgram.u_MvpMatrixFromLight || !normalProgram.u_ShadowMap) {
    console.log('cannot get attribute or uniform variable of normalProgram');
    return;
  }

  var triangle = initVertexBuffersForTriangle(gl);
  var plane = initVertexBuffersForPlane(gl);
  if (!triangle || !plane) {
    console.log('cannot set vertices');
    return;
  }

  var fbo = initFramebufferObject(gl);
  if (!fbo) {
    console.log('cannot init frame buffer object');
    return;
  }
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, fbo.texture);

  gl.clearColor(0, 0, 0, 1);
  gl.enable(gl.DEPTH_TEST);

  var viewProjMatrixFromLight = new Matrix4();
  viewProjMatrixFromLight.setPerspective(70.0, OFFSCREEN_WIDTH/OFFSCREEN_HEIGHT, 1.0, 200.0);
  viewProjMatrixFromLight.lookAt(LIGHT_X, LIGHT_Y, LIGHT_Z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

  var viewProjMatrix = new Matrix4();
  viewProjMatrix.setPerspective(45, canvas.width/canvas.height, 1.0, 100.0);
  viewProjMatrix.lookAt(0.0, 7.0, 9.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

  var currentAngle = 0.0;
  var mvpMatrixFromLight_t = new Matrix4();
  var mvpMatrixFromLight_p = new Matrix4();
  var tick = function() {
    currentAngle = animate(currentAngle);

    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
    gl.viewport(0, 0, OFFSCREEN_HEIGHT, OFFSCREEN_HEIGHT);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.useProgram(shadowProgram);
    drawTriangle(gl, shadowProgram, triangle, currentAngle, viewProjMatrixFromLight);
    mvpMatrixFromLight_t.set(g_mvpMatrix);
    drawPlane(gl, shadowProgram, plane, viewProjMatrixFromLight);
    mvpMatrixFromLight_p.set(g_mvpMatrix);

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    gl.viewport(0, 0, canvas.width, canvas.height);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.useProgram(normalProgram);
    gl.uniform1i(normalProgram.u_ShadowMap, 0);
    gl.uniformMatrix4fv(normalProgram.u_MvpMatrixFromLight, false, mvpMatrixFromLight_t.elements);
    drawTriangle(gl, normalProgram, triangle, currentAngle, viewProjMatrix);
    gl.uniformMatrix4fv(normalProgram.u_MvpMatrixFromLight, false, mvpMatrixFromLight_p.elements);
    drawPlane(gl, normalProgram, plane, viewProjMatrix);

    window.requestAnimationFrame(tick, canvas);
  };
  tick(); 
}

var g_modelMatrix = new Matrix4();
var g_mvpMatrix = new Matrix4();
function drawTriangle(gl, program, triangle, angle, viewProjMatrix) {
  g_modelMatrix.setRotate(angle, 0, 1, 0);
  draw(gl, program, triangle, viewProjMatrix);
}

function drawPlane(gl, program, plane, viewProjMatrix) {
  g_modelMatrix.setRotate(-45, 0, 1, 1);
  draw(gl, program, plane, viewProjMatrix);
}

function draw(gl, program, o, viewProjMatrix) {
  initAttributeVariable(gl, program.a_Position, o.vertexBuffer);
  if (program.a_Color != undefined)
    initAttributeVariable(gl, program.a_Color, o.colorBuffer);

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, o.indexBuffer);

  g_mvpMatrix.set(viewProjMatrix);
  g_mvpMatrix.multiply(g_modelMatrix);
  gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements);

  gl.drawElements(gl.TRIANGLES, o.numIndices, gl.UNSIGNED_BYTE, 0);
}

function initAttributeVariable(gl, a_attribute, buffer) {
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0);
  gl.enableVertexAttribArray(a_attribute);
}

function initVertexBuffersForPlane(gl) {
  var vertices = new Float32Array([
    3.0, -1.7, 2.5,  -3.0, -1.7, 2.5,  -3.0, -1.7, -2.5,   3.0, -1.7, -2.5    // v0-v1-v2-v3
  ]);
  var colors = new Float32Array([
    1.0, 1.0, 1.0,    1.0, 1.0, 1.0,  1.0, 1.0, 1.0,   1.0, 1.0, 1.0
  ]);
  var indices = new Uint8Array([0, 1, 2,   0, 2, 3]);
  var o = {};
  o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
  o.colorBuffer = initArrayBufferForLaterUse(gl, colors, 3, gl.FLOAT);
  o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
  if (!o.vertexBuffer || !o.colorBuffer || !o.indexBuffer) return null; 
  o.numIndices = indices.length;
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
  return o;
}

function initVertexBuffersForTriangle(gl) {
  // 三角形を生成する
  //       v2
  //      / | 
  //     /  |
  //    /   |
  //  v0----v1

  // 頂点座標
  var vertices = new Float32Array([-0.8, 3.5, 0.0,  0.8, 3.5, 0.0,  0.0, 3.5, 1.8]);
  // 色
  var colors = new Float32Array([1.0, 0.5, 0.0,  1.0, 0.5, 0.0,  1.0, 0.0, 0.0]);    
  // インデックス
  var indices = new Uint8Array([0, 1, 2]);

  var o = new Object();  // 複数のバッファオブジェクトを返すのにObjectオブジェクトを利用

  // 頂点情報をバッファオブジェクトに書き込む
  o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
  o.colorBuffer = initArrayBufferForLaterUse(gl, colors, 3, gl.FLOAT);
  o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
  if (!o.vertexBuffer || !o.colorBuffer || !o.indexBuffer) return null; 

  o.numIndices = indices.length;

  // バッファオブジェクトのバインドを解除する
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

  return o;
}

function initArrayBufferForLaterUse(gl, data, num, type) {
  // バッファオブジェクトを作成する
  var buffer = gl.createBuffer();
  if (!buffer) {
    console.log('バッファオブジェクトの作成に失敗');
    return null;
  }
  // バッファオブジェクトにデータを書き込む
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

  // 後でattribute変数に割り当てるのに必要な情報を格納しておく
  buffer.num = num;
  buffer.type = type;

  return buffer;
}

function initElementArrayBufferForLaterUse(gl, data, type) {
  // バッファオブジェクトを作成する
  var buffer = gl.createBuffer();
  if (!buffer) {
    console.log('バッファオブジェクトの作成に失敗');
    return null;
  }
  // バッファオブジェクトにデータを書き込む
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW);

  buffer.type = type;

  return buffer;
}

function initFramebufferObject(gl) {
  var framebuffer, texture, depthBuffer;

  // エラー発生時の後処理関数
  var error = function() {
    if (framebuffer) gl.deleteFramebuffer(framebuffer);
    if (texture) gl.deleteTexture(texture);
    if (depthBuffer) gl.deleteRenderbuffer(depthBuffer);
    return null;
  }

  // フレームバッファオブジェクト(FBO)を作成する
  framebuffer = gl.createFramebuffer();
  if (!framebuffer) {
    console.log('フレームバッファオブジェクトの作成に失敗');
    return error();
  }

  // テクスチャオブジェクトを作成し、サイズ、パラメータを設定する
  texture = gl.createTexture(); // テクスチャオブジェクトを作成
  if (!texture) {
    console.log('テクスチャオブジェクトの作成に失敗');
    return error();
  }
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

  // レンダーバッファオブジェクトを作成し、サイズ、パラメータを設定する
  depthBuffer = gl.createRenderbuffer(); // レンダーバッファオブジェクトを作成
  if (!depthBuffer) {
    console.log('レンダーバッファオブジェクトの作成に失敗');
    return error();
  }
  gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);

  // FBOにテクスチャとレンダーバッファオブジェクトをアタッチする
  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);

  // FBOが正しく設定できたかチェックする
  var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
  if (gl.FRAMEBUFFER_COMPLETE !== e) {
    console.log('フレームバッファオブジェクトが不完全' + e.toString());
    return error();
  }

  framebuffer.texture = texture; // 必要なオブジェクトを保持する

  // バインドを解除する
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  gl.bindTexture(gl.TEXTURE_2D, null);
  gl.bindRenderbuffer(gl.RENDERBUFFER, null);

  return framebuffer;
}

var ANGLE_STEP = 40;   // 回転角の増分(度)

var last = new Date().getTime(); // 最後に呼び出された時刻
function animate(angle) {
  var now = new Date().getTime();   // 前回呼び出されてからの経過時間を計算
  var elapsed = now - last;
  last = now;
  // 回転角度を更新する(経過時間により調整)
  var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
  return newAngle % 360;
}

//]]>
  </script>
</head>
<body onload="main()">
  <canvas id="mycanvas" width="320" height="240">
    Your browser does not support Canvas TAB.
  </canvas>
</body>
</html>
LightShade.html
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Script-Type" content="text/javascript" />
  <script src="lib/webgl-utils.js"></script>
  <script src="lib/webgl-debug.js"></script>
  <script src="lib/cuon-utils.js"></script>
  <script src="lib/cuon-matrix.js"></script>
  <script src="tsuda-webgl.js"></script>
  <script language="JavaScript" type="text/javascript">
    //<![CDATA
var SHADOW_VSHADER_SOURCE = "\
attribute vec4 a_Position;\n\
uniform mat4 u_MvpMatrix;\n\
void main() {\n\
    gl_Position = u_MvpMatrix * a_Position;\n\
}\n\
";
var SHADOW_FSHADER_SOURCE ="\
precision mediump float;\n\
void main() {\n\
    const vec4 bitShift = vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0);\n\
    const vec4 bitMask = vec4(1.0/256.0, 1.0/256.0, 1.0/256.0, 0.0);\n\
    vec4 rbgaDepth = fract(gl_FragACoord.z * bitShift);\n\
    rgbaDepth -= rgbaDepth.gbaa * bitMask; \n\
    gl_FragColor = rgbaqDepth;\n\
}\n\
";
var VSHADER_SOURCE = "\
attribute vec4 a_Position;\n\
attribute vec4 a_Color;\n\
uniform mat4 u_MvpMatrix;\n\
uniform mat4 u_MvpMatrixFromLight;\n\
varying vec4 v_PositionFromLight;\n\
varying vec4 v_Color;\n\
void main() {\n\
    gl_Position = u_MvpMatrix * a_Position;\n\
    v_PositionFromLight = u_MvpMatrixFromLight * a_Position;\n\
    v_Color = a_Color;\n\
}\n\
";
var FSHADER_SOURCE = "\
precision mediump float;\n\
uniform sampler2D u_ShadowMap;\n\
varying vec4 v_PositionFromLight;\n\
varying vec4 v_Color;\n\
float unpackDepth(const in vec4 rgbaDepth) {\n\
    const vec4 bitShift = vec4(1.0, 1.0/256, 1.0/(256.0*256), 1.0/(256.0*256.0*256.0));\n\
    float depth = dot(rgbaDepth,bitShift);\n\
    return depth;\n\
}\n\
void main() {\n\
    vec3 shadowCoord = (v_PositionFromLight.xyz/v_PositionFromLight.w) / 2.0 + 0.5;\n\
    vec4 rgbaDepth = texture2D(u_ShadowMap,shadowCoord.xy);\n\
    float depth = unpackDepth(rgbaDepth);\n\
    float visibility = (shadowCoord.z > depth + 0.0015) ? 0.7 : 1.0; \n\
    gl_FragColor = vec4(v_Color.rgb*visibility,v_Color.a);\n\
}\n\
";
function main() {
    var canvas = document.getElementById("mycanvas");
    if (!canvas || !canvas.getContext) {
        console.log("canvas not supported"); return null;
    }
    var gl = getWebGLContext(tsuda.canvas);
    if (! gl) {
        console.log("cannot get WebGL Context"); return null;
    }
    var shadowProgram = createProgram(gl,SHADOW_VSHADER_SOURCE,SHADOW_FSHADER_SOURCE);
    shadowProgram.a_Position = gl.getAttribLocation(shadowProgram,'a_Position');
    shadowProgram.u_MvpMatrix = gl.getUniformLocation(shadowProgram,'u_MvpMatrix');
    if (shadowProgram.a_Position < 0 || ! shadowProgram.u_MvpMatgrix) {
	console.log('variables of shadowProgram not variable'); return null;
    }
    console.log('hello');

    var normalProgram = createProgram(VSHADER_SOURCE,FSHADER_SOURCE);
    normalProgram.a_Position = gl.getAttribLocation(normalProgram,'a_Position');
    normalProgram.a_Color = gl.getAttribLocation(normalProgram,'a_Color');
    normalProgram.u_MvpMatrix = gl.getUniformLocation(normalProgram,'u_MvpMatrix');
    normalProgram.u_MvpMatrixFromLight = gl.getUniformLocation(normalProgram,'u_MvpMatrixFromLight');
    normalProgram.u_ShadowMap = gl.getUniformLocation(normalProgram,'u_ShadowMap');
    if (normalProgram.a_Position < 0 
	|| normalProgram.a_Color < 0 
	|| ! normalProgram.u_MvpMatrix
	|| ! normalProgram.u_MvpMatrixFromLight
	|| ! normalProgram.u_ShadowMap) {
	console.log('variables of normalProgram not variable'); return null;
    }

    /*
    var vertices = tsuda.cube.vertices;
    var indices = tsuda.cube.indices;
    var colors = tsuda.get3Float32Array(0.0, 1.0, 0.0, vertices.length);
    //var normals = tsuda.indexNormals(vertices,indices);

    var cube1 = {};
    cube1.vertexBuffer = initArrayBufferForLaterUse(gl,vertices,3,gl.FLOAT);
    cube1.colorBuffer = initArrayBufferForLaterUse(gl,tsuda.cube.vertices,3,gl.FLOAT);
    ///cube1.normalBuffer = initArrayBufferForLaterUse(gl,normals,3,gl.FLOAT);
    cube1.indexBuffer = initElementArrayBufferForLaterUse(gl,indices,gl.UNSIGNED_BYTE);
    if (!cube1.vertexBuffer || !cube1.colorBuffer || !cube1.indexBuffer) {
	console.log('cube1 variable'); return null;
    }

    var cube2 = {};
    */

    var triangle = initVertexBuffersForTriangle(gl);
    var plane = initVertexBuffersForPlane(gl);
    if (!triangle || !plane) {
	console.log('polygon index'); return null;
    }
    var fbo = initramebufferObject(gl);
    if (!fbo) {
	console.log('cannot init framebuffer object'); return null;
    }
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D,fbo.texture);
    gl.clearColor(0,0,0,1);
    gl.enable(gl.DEPTH_TEST);
    console.log('test');
    

/*
    var lightPos = { x:0, y:40, z:2 };

    if (! tsuda.initArrayBuffer('a_Position',vertices,3,gl.FLOAT)) return;
    if (! tsuda.initArrayBuffer('a_Color',colors,3,gl.FLOAT)) return;
    if (! tsuda.initArrayBuffer('a_Normal',normals,3,gl.FLOAT)) return;

    if (! tsuda.setElementArrayBuffer(indices)) return;

    var u_MvpMatrix = gl.getUniformLocation(gl.program,'u_MvpMatrix');
    var u_NormalMatrix = gl.getUniformLocation(gl.program,'u_NormalMatrix');
    var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
    var u_LightDirection = gl.getUniformLocation(gl.program, 'u_LightDirection');
    var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
    if (! u_MvpMatrix || ! u_LightColor || ! u_LightDirection || ! u_AmbientLight
        || ! u_NormalMatrix) {
        console.log('failed to get location of uniform variable');
        return;
    }

    gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
    var lightDirection = new Vector3([1.0, 0.8, 0.5]); // world coordinates
    lightDirection.normalize();
    gl.uniform3fv(u_LightDirection, lightDirection.elements);
    gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);

    var modelMatrix = new Matrix4();
    var viewMatrix = new Matrix4();
    var projMatrix = new Matrix4();
    var mvpMatrix = new Matrix4();
    var normalMatrix = new Matrix4();

    viewMatrix.setLookAt(-3.0, 5.0, 5.0, 0, 0, 0, 0, 1, 0);
    projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
    modelMatrix.setScale(0.7, 0.3, 0.6);
    modelMatrix.rotate(45, 0, 0, 1);
    modelMatrix.translate(0, 1, 0);
    mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);
    gl.uniformMatrix4fv(u_MvpMatrix,false,mvpMatrix.elements);
    normalMatrix.setInverseOf(modelMatrix);
    normalMatrix.transpose();
    gl.uniformMatrix4fv(u_NormalMatrix,false,normalMatrix.elements);

    gl.enable(gl.DEPTH_TEST);
    gl.depthFunc(gl.LEQUAL);

    gl.clearColor(0.0, 0.0, 0.0, 1.0); // black
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.drawElements(gl.TRIANGLES, indices.length,gl.UNSIGNED_BYTE,0);
*/
}
function initArrayBufferForLateUse(gl,data,num,type) {
    var buffer = gl.createBuffer();
    if (!buffer) { console.log('cannot create BufferObject'); return null; }
    gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
    bl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);
    buffer.num = numl;
    buffer.type = type;
    return buffer;
}
function initElementgArrayBufferForLateUse(gl,data,type) {
    var buffer = gl.createBuffer();
    if (!buffer) { console.log('cannot create BufferObject'); return null; }
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,buffer);
    bl.bufferData(gl.ELEMENT_ARRAY_BUFFER,data,gl.STATIC_DRAW);
    buffer.type = type;
    return buffer;
}
function initVertexBuffersForPlane(gl) {
  //  v1------v0
  //  |        | 
  //  |        |
  //  |        |
  //  v2------v3
  var vertices = new Float32Array([
    3.0, -1.7, 2.5,  -3.0, -1.7, 2.5,  -3.0, -1.7, -2.5,   3.0, -1.7, -2.5    // v0-v1-v2-v3
  ]);
  var colors = new Float32Array([
    1.0, 1.0, 1.0,    1.0, 1.0, 1.0,  1.0, 1.0, 1.0,   1.0, 1.0, 1.0
  ]);
  var indices = new Uint8Array([0, 1, 2,   0, 2, 3]);
  var o = new Object(); // 複数のバッファオブジェクトを返すのにObjectオブジェクトを利用
  o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
  o.colorBuffer = initArrayBufferForLaterUse(gl, colors, 3, gl.FLOAT);
  o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
  if (!o.vertexBuffer || !o.colorBuffer || !o.indexBuffer) return null; 
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
  return o;
}
function initVertexBuffersForTriangle(gl) {
  //       v2
  //      / | 
  //     /  |
  //    /   |
  //  v0----v1
  var vertices = new Float32Array([-0.8, 3.5, 0.0,  0.8, 3.5, 0.0,  0.0, 3.5, 1.8]);
  var colors = new Float32Array([1.0, 0.5, 0.0,  1.0, 0.5, 0.0,  1.0, 0.0, 0.0]);    
  var indices = new Uint8Array([0, 1, 2]);
  var o = {};
  o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
  o.colorBuffer = initArrayBufferForLaterUse(gl, colors, 3, gl.FLOAT);
  o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
  if (!o.vertexBuffer || !o.colorBuffer || !o.indexBuffer) return null; 
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
  return o;
}
function initFramebufferObject(gl) {
  var framebuffer, texture, depthBuffer;
  var error = function() {
    if (framebuffer) gl.deleteFramebuffer(framebuffer);
    if (texture) gl.deleteTexture(texture);
    if (depthBuffer) gl.deleteRenderbuffer(depthBuffer);
    return null;
  }
  framebuffer = gl.createFramebuffer();
  if (!framebuffer) {
    console.log('cannot create framebuffer object'); return error();
  }
  texture = gl.createTexture();
  if (!texture) {
    console.log('cannot create texture object'); return error();
  }
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  depthBuffer = gl.createRenderbuffer();
  if (!depthBuffer) {
    console.log('cannot create renderbuffer object'); return error();
  }
  gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
  gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, OFFSCREEN_WIDTH, OFFSCREEN_HEIGHT);
  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
  gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
  var e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
  if (gl.FRAMEBUFFER_COMPLETE !== e) {
    console.log('imcomplete framebuffer object' + e.toString());
    return error();
  }
  framebuffer.texture = texture;
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  gl.bindTexture(gl.TEXTURE_2D, null);
  gl.bindRenderbuffer(gl.RENDERBUFFER, null);
  return framebuffer;
}
//]]>
  </script>
</head>
<body onload="main()">
  <canvas id="mycanvas" width="320" height="240">
    Your browser does not support Canvas TAB.
  </canvas>
</body>
</html>
a.html からLightShade.htmlへの変更点