WebGL (7)


テクスチャ

物体表面にテクスチャを貼る手順について説明します。

  1. テクスチャとして用いる画像を用意する。
  2. javascriptから頂点シェーダに各頂点のテクスチャ座標系での位置を渡す。
  3. javascriptでテクスチャ画像を読み込む。
  4. WebGLでテクスチャ画像を使用できるようにする。
  5. フラグメントシェーダに、利用するテクスチャユニットの番号を渡す
  6. 頂点シェーダで、テクスチャ座標をフラグメントシェーダに転送する。
  7. フラグメントシェーダで、テクセルの色を取り出しフラグメントに設定する。

テクスチャ上の位置は テクスチャ座標 (s,t)で表し、元の画像の大きさとは関係なく 0 ≤ s ≤ 1, 0 ≤ t ≤ 1 です。 テクスチャ座標系と、画像イメージの座標系ではy軸が反転していることに注意が必要です。

   t ^
(0,1)|
     |
     |
(0,0)+--------> s
           (1,0)
           (1,0)
(0,0)+--------> x
     |
     |
     |
(0,1)v
テクスチャ座標系画像データの座標系

1. テクスチャとして用いる画像を用意する

WebGL や OpenGL/ES でテクスチャとして使用できる画像には次のような制約があります。

下のプログラムで利用しているのは1024x1024のjpegファイルです。

sakura1024.jpg

2. javascriptから頂点シェーダに各頂点のテクスチャ座標系での位置を渡す

一つのポリゴンを構成する各頂点に対して、その頂点がテクスチャ座標系で どこにあるかを与えておきます。この値をattribute変数で頂点シェーダに渡し、 頂点シェーダからvarying変数を経由してフラグメントシェーダに 渡すことで、各フラグメントごとのテクスチャ座標が補間によって求められて フラグメントシェーダに渡されます。

//    v6----- v5
//   /|      /|
//  v1------v0|
//  | |     | |
//  | |v7---|-|v4
//  |/      |/
//  v2------v3
tsuda.cube = {};
tsuda.cube.vertices = new Float32Array([
  // x, y, z
  1.0,  1.0,  1.0,  // 0 v0 :front  
 -1.0,  1.0,  1.0,  // 1 v1	      
 -1.0, -1.0,  1.0,  // 2 v2	      
  1.0, -1.0,  1.0,  // 3 v3	      

...(略)

tsuda.cube.texCoord = new Float32Array([
  1.0, 1.0,  // v0 :front
  0.0, 1.0,  // v1
  0.0, 0.0,  // v2
  1.0, 0.0,  // v3

...(略)

3. javascriptでテクスチャ画像を読み込む

WebGLではテクスチャ用の画像はネットワーク経由でブラウザに非同期的に読み込まれます。 画像データにアクセスできるのは読み込みが全て終了した後である点に注意しましょう。

画像の読み込みと、その後の処理はjavascriptで以下のように記述します。

  1. Imageオブジェクト(画像オブジェクト)を作成する。
  2. var image = new Image();

  3. 画像を読み終えたときに呼び出されるイベントハンドラを設定する。
  4. image.onload = function() { 画像が読み込まれた後で実行すべき関数呼び出し; };
    画像の読み込みが終わると Image オブジェクトの onload プロパティに設定された イベントハンドラ(関数)が引数なしで呼び出されます。

  5. 画像の読み込みをブラウザに依頼する。
  6. image.src="画像へのパス";
    Imageオブジェクトの src プロパティに読み込みたい画像のパス(文字列)を設定すると、 ブラウザは画像の読み込みを開始します。 HTMLで<img>タグの要素のsrcプロパティで読み込む画像を設定したときの ブラウザの動作と似ています。


4. WebGLでテクスチャ画像を使用できるようにする

WebGLではテクスチャ画像をテクスチャユニットで管理します。 テクスチャユニットは少なくとも gl.TEXTURE0〜gl.TEXTURE7の8個が存在し、 同じ数だけ用意されたそれぞれの gl.TEXTURE_2D と対応しています。 テクスチャユニットに画像データを設定し、それをフラグメント シェーダに伝える手順を説明します。

  1. テクスチャオブジェクトを生成する。gl.createTexture()

  2. 指定したテクスチャユニットを有効にする。gl.activeTexture()
  3. WebGLでは複数のテクスチャを利用できます。 それぞれのテクスチャを管理するためにはテクスチャユニットを使います。 テクスチャユニットは少なくとも8個 (gl.TEXTURE0, gl.TEXTURE2, ..., gl.TEXTURE7)存在し、 それぞれに対応する TEXTURE_2D ユニットがあります。


  4. 有効にしたテクスチャユニットのターゲットにテクスチャオブジェクトをバインドする。gl.bindTexture()
  5. テクスチャユニットのターゲットには gl.TEXTURE_2D と gl.TEXTURE_CUBE_MAP があります。 2次元画像を用いる場合は gl.TEXTURE_2Dをターゲットに指定します。


  6. テクスチャオブジェクトにテクスチャパラメータを設定する。gl.texParameteri()
  7. テクスチャの拡大・縮小方法や、繰り返し方についての設定をします。

  8. テクスチャオブジェクトに画像データを設定する。gl.texImage2D()
  9. javascriptのImageオブジェクト内の画像データを、WebGLの テクスチャオブジェクトへと転送します。



5. フラグメントシェーダに、利用するテクスチャユニットの番号を渡す

フラグメントシェーダに、使用するテクスチャを保持しているテクスチャユニット番号を uniform変数を用いて渡します。1個の整数を渡すにはgl.uniform1i()を用います。


6. 頂点シェーダで、テクスチャ座標をフラグメントシェーダに転送する。

頂点シェーダでは、頂点のテクスチャ座標系での位置がattribute変数で 渡されてくるので、varying変数に代入してフラグメントシェーダに渡します。

頂点シェーダ用プログラム
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
...
    v_TexCoord = a_TexCoord;

7. フラグメントシェーダで、テクセルの色を取り出しフラグメントに設定する。

フラグメントシェーダで画像からテクセルの色を取り出し、対応するフラグメントに設定します。

テクスチャ画像を保持しているテクスチャユニットの番号はuniform変数で渡されています。 また、テクスチャ座標系でのフラグメントの位置は、補間された値がvarying変数で渡されています。

フラグメントシェーダ用プログラム
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
...
    vec4型の変数 = texture2D(u_Sampler,v_TexCoord);


フラグメントシェーダの関数
vec4 texture2D(sampler2D, vec2)
 @param sampler2d テクスチャユニット番号
 @param vec2 テクスチャ座標の値
 @return vec4 テクセルの色。画像が用意されていない場合は(0.0,0.0,0.0,1.0)。
glの関数
createTexture()
  テクスチャ画像を管理するテクスチャオブジェクトを生成する。
  @return 作成されたテクスチャオブジェクト or null(エラーの場合)
activeTexture(texUnit )
  指定したテクスチャユニット texUnit を有効にする。
  @param  texUnit : 有効にするテクスチャユニット。gl.TEXTURE[0-7]
  @return なし
bindTexture(target , texture )
  有効になっているテクスチャユニットに対応するtarget に、テクスチャオブジェクトをバインドする。
  @param target  gl.TEXTURE_2D(2次元) or gl.TEXTURE_CUBE_MAP(3次元)
  @param texture  テクスチャオブジェクト
  @return なし
texParameteri(target , pname , param )
  有効になっているテクスチャユニットに対応するtarget の、pname パラメータ値を param に設定する。
  @param target  gl.TEXTURE_2D(2次元) or gl.TEXTURE_CUBE_MAP(3次元)
  @param pname  テクスチャパラメータ名
  @param param  テクスチャパラメータの値
  @return なし
テクスチャパラメータとデフォルト値
テクスチャパラメータ 説明 デフォルト値
gl.TEXTURE_MAG_FILTER テクスチャ画像の拡大方法 gl.LINEAR
gl.TEXTURE_MIN_FILTER テクスチャ画像の縮小方法 gl.NEAREST_MIPMAP_NINEAR
gl.TEXTURE_WRAP_S s軸方向の画像の繰り返し方法 gl.REPEAT
gl.TEXTURE_WRAP_T t軸方向の画像の繰り返し方法 gl.REPEAT
gl.TEXTURE_{MAG,MIN}_FILTERに指定できる値
gl.NEAREST フラグメントの座標に最も近いテクセルの値を使う
gl.LINEAR フラグメントの座標の周囲のテクセルの色の平均して使う
gl.TEXTURE_WRAP_{ST}_FILTERに指定できる値
gl.REPEAT 画像を繰り返し使用する
gl.MIRRORED_REPEAT 画像を反転させながら繰り返し使用する
gl.CLAMP_TO_EDGE 画像の端の色を使用する
texImage2D(target , level , internalformat , format , type , image )
  有効になっているテクスチャユニットに対応するtarget にバインドされたテクスチャオブジェクトに、
  javascriptの image オブジェクトの画像を転送する。
  @param target  gl.TEXTURE_2D(2次元) or gl.TEXTURE_CUBE_MAP(3次元)
  @param level  テクスチャがミップマップの場合はそのレベルを、それ以外の場合は0を指定する
  @param internalformat  テクスチャ画像の内部フォーマット。
  @param format  テクセルのフォーマット。internalformat と同じ値。
  @param type  テクセルのデータ型
  @param image  Imageオブジェクト
  @return なし
{intenal,}formatに指定できる値
説明
gl.RGBRGB(赤、緑、青)
gl.RGBARGBとA(アルファ値)
gl.ALPHA(0.0, 0.0, 0.0, アルファ値)
gl.LUMINANCE輝度
gl.LUMINANCE_ALPHA輝度とアルファ値
typeに指定できる値
説明
gl.UNSIGNED_BYTERBGそれぞれ符号なし1バイト
gl.UNSIGNED_SHORT_5_6_5RGBがそれぞれ5,6,5ビット
gl.UNSIGNED_SHORT_4_4_4_4RGBAがそれぞれ4ビット
gl.UNSIGNED_SHORT_5_5_5_1RGBAがそれぞれ5,5,5,1ビット

ParaeelLightCube6.htmlが平行光線の元でひとつの立体を扱う最後のプログラムでした。 ParallelLightCube6.html から最小限の変更で物体表面にテクスチャを貼りつけてみます。 ここではシェーディングを考慮していません。

Texture.html
*** ParallelLightCube6.html	Fri May 11 12:29:33 2018
--- Texture.html	Fri May 11 12:54:28 2018
***************
*** 14,25 ****
--- 14,27 ----
  attribute vec4 a_Position;\n\
  attribute vec4 a_Color;\n\
  attribute vec4 a_Normal;\n\
+ attribute vec2 a_TexCoord;\n\
  uniform mat4 u_MvpMatrix;\n\
  uniform mat4 u_NormalMatrix;\n\
  uniform vec3 u_LightColor;\n\
  uniform vec3 u_LightDirection;\n\
  uniform vec3 u_AmbientLight;\n\
  varying vec4 v_Color;\n\
+ varying vec2 v_TexCoord;\n\
  void main() {\n\
      gl_Position = u_MvpMatrix * a_Position;\n\
      vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n\
***************
*** 27,39 ****
      vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n\
      vec3 ambient = u_AmbientLight * a_Color.rgb;\n\
      v_Color = vec4(diffuse+ambient,a_Color.a);\n\
  }\n\
  ";
  var FSHADER_SOURCE = "\
  precision mediump float;\n\
  varying vec4 v_Color;\n\
  void main() {\n\
!     gl_FragColor = v_Color;\n\
  }\n\
  ";
  function main() {
--- 29,44 ----
      vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n\
      vec3 ambient = u_AmbientLight * a_Color.rgb;\n\
      v_Color = vec4(diffuse+ambient,a_Color.a);\n\
+     v_TexCoord = a_TexCoord;\n\
  }\n\
  ";
  var FSHADER_SOURCE = "\
  precision mediump float;\n\
+ uniform sampler2D u_Sampler;\n\
  varying vec4 v_Color;\n\
+ varying vec2 v_TexCoord;\n\
  void main() {\n\
!     gl_FragColor = texture2D(u_Sampler,v_TexCoord);\n\
  }\n\
  ";
  function main() {
***************
*** 46,55 ****
--- 51,63 ----
      var indices = tsuda.cube.indices;
      var colors = tsuda.get3Float32Array(0.0, 1.0, 0.0, vertices.length);
      var normals = tsuda.indexNormals(vertices,indices);
+     var texCoord = tsuda.cube.texCoord;
+     var texture = gl.createTexture();
  
      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.initArrayBuffer('a_TexCoord',texCoord,2,gl.FLOAT)) return;
  
      if (! tsuda.setElementArrayBuffer(indices)) return;
  
***************
*** 58,65 ****
      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;
      }
--- 66,75 ----
      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');
+     var u_Sampler = gl.getUniformLocation(gl.program,'u_Sampler');
+ 
      if (! u_MvpMatrix || ! u_LightColor || ! u_LightDirection || ! u_AmbientLight
!         || ! u_NormalMatrix || ! u_Sampler ) {
          console.log('failed to get location of uniform variable');
          return;
      }
***************
*** 90,99 ****
      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);
  }
  //]]>
    </script>
--- 100,119 ----
      gl.enable(gl.DEPTH_TEST);
      gl.depthFunc(gl.LEQUAL);
  
!     var image = new Image();
!     image.onload = function() {
!        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1); // reverse y axis of texture image
!        gl.activeTexture(gl.TEXTURE0);
!        gl.bindTexture(gl.TEXTURE_2D,texture);
!        gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);
!        gl.texImage2D(gl.TEXTURE_2D,0,gl.RGB,gl.RGB,gl.UNSIGNED_BYTE,image);
!        gl.uniform1i(u_Sampler,0);
! 
!        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);
!     };
!     image.src = "./sakura1024.jpg";
  }
  //]]>
    </script>


シェーディングを考慮したプログラムに変更しましょう。 頂点シェーダで計算したdiffuse成分とambient成分 の和が輝度(明るさ)となりますので、 この値をフラグメントシェーダに渡し、 フラグメントシェーダではテクセルの値に輝度を乗算してそのピクセルの色を決定します。

以下のプログラムでは、 ポリゴンの色は必要ないので、javascriptからWebGLに渡すのを止めています。 また、頂点シェーダ内で 「光の向きと面の法線から求めたデフューズ成分」と 「環境光からのくるアンビェント成分」を加算して輝度 (v_Brightness) としてフラグメントシェーダに渡しています。 フラグメントシェーダ内では、テクスチャからの色に輝度をそれぞれ 乗算して色を計算します。

Texture2.html
*** Texture.html	Fri May 11 12:54:28 2018
--- Texture2.html	Sat Jun 23 01:19:06 2018
***************
*** 12,18 ****
      //<![CDATA
  var VSHADER_SOURCE = "\
  attribute vec4 a_Position;\n\
- attribute vec4 a_Color;\n\
  attribute vec4 a_Normal;\n\
  attribute vec2 a_TexCoord;\n\
  uniform mat4 u_MvpMatrix;\n\
--- 12,17 ----
***************
*** 20,44 ****
  uniform vec3 u_LightColor;\n\
  uniform vec3 u_LightDirection;\n\
  uniform vec3 u_AmbientLight;\n\
- varying vec4 v_Color;\n\
  varying vec2 v_TexCoord;\n\
  void main() {\n\
      gl_Position = u_MvpMatrix * a_Position;\n\
      vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n\
      float nDotL = max(dot(u_LightDirection,normal),0.0);\n\
-     vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n\
-     vec3 ambient = u_AmbientLight * a_Color.rgb;\n\
-     v_Color = vec4(diffuse+ambient,a_Color.a);\n\
      v_TexCoord = a_TexCoord;\n\
  }\n\
  ";
  var FSHADER_SOURCE = "\
  precision mediump float;\n\
  uniform sampler2D u_Sampler;\n\
- varying vec4 v_Color;\n\
  varying vec2 v_TexCoord;\n\
  void main() {\n\
!     gl_FragColor = texture2D(u_Sampler,v_TexCoord);\n\
  }\n\
  ";
  function main() {
--- 19,43 ----
  uniform vec3 u_LightColor;\n\
  uniform vec3 u_LightDirection;\n\
  uniform vec3 u_AmbientLight;\n\
  varying vec2 v_TexCoord;\n\
+ varying vec4 v_Brightness;\n\
  void main() {\n\
      gl_Position = u_MvpMatrix * a_Position;\n\
      vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n\
      float nDotL = max(dot(u_LightDirection,normal),0.0);\n\
      v_TexCoord = a_TexCoord;\n\
+     v_Brightness = vec4(u_LightColor * nDotL + u_AmbientLight,1.0);\n\
  }\n\
  ";
  var FSHADER_SOURCE = "\
  precision mediump float;\n\
  uniform sampler2D u_Sampler;\n\
  varying vec2 v_TexCoord;\n\
+ varying vec4 v_Brightness;\n\
  void main() {\n\
!     vec4 color = texture2D(u_Sampler,v_TexCoord);\n\
!     vec3 c = color.rgb * v_Brightness.rgb;\n\
!     gl_FragColor = vec4(c.rgb,color.a);\n\
  }\n\
  ";
  function main() {
***************
*** 49,61 ****
  
      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 texCoord = tsuda.cube.texCoord;
      var texture = gl.createTexture();
  
      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.initArrayBuffer('a_TexCoord',texCoord,2,gl.FLOAT)) return;
  
--- 48,58 ----


Computer Graphics 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への変更点