参考webglorg;
原則として上記ページを見ながら学習した記録です。
複数モデルのレンダリング
頂点バッファの再利用
VBOを再利用し、座標変換行列を操作して複数モデルの描写を表現する。
座標変換行列の再利用
座標変換行列も再利用します。
実際、複数モデルを違う位置にレンダリングするとき、操作するのはモデル変換行列のみ。
手順としては
- ビュー・プロジェクションのりょう左表変換行列を用意
- あらかじめ二つは掛け合わせておく(pv)
- 一つのモデル座標変換行列を用意(m1)
- m1にpvを掛け合わせて uniform に登録
- 一つ目のモデルを描写
- 二つ目のモデル座標変換行列を用意(m2)
- m2 に pv を掛け合わせて uniform 登録
- 二つ目のモデルを描写
- コンテキストのリフレッシュをして再描写
つまりどうなるか
一部抜粋。
// 各種行列の生成と初期化
var mMatrix = m.identity(m.create());
var vMatrix = m.identity(m.create());
var pMatrix = m.identity(m.create());
var tmpMatrix = m.identity(m.create());
var mvpMatrix = m.identity(m.create());
// ビュー×プロジェクション座標変換行列
m.lookAt([0.0, 0.0, 3.0], [0, 0, 0], [0, 1, 0], vMatrix);
m.perspective(90, c.width / c.height, 0.1, 100, pMatrix);
m.multiply(pMatrix, vMatrix, tmpMatrix);
// 一つ目のモデルを移動するためのモデル座標変換行列
m.translate(mMatrix, [1.5, 0.0, 0.0], mMatrix);
// モデル×ビュー×プロジェクション(一つ目のモデル)
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// uniformLocationへ座標変換行列を登録し描画する(一つ目のモデル)
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// 二つ目のモデルを移動するためのモデル座標変換行列
m.identity(mMatrix);
m.translate(mMatrix, [-1.5, 0.0, 0.0], mMatrix);
// モデル×ビュー×プロジェクション(二つ目のモデル)
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// uniformLocationへ座標変換行列を登録し描画する(二つ目のモデル)
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// コンテキストの再描画
gl.flush();
再帰処理と移動・回転・拡大縮小
今回は合計3つのポリゴンを描写する。
VBOや一部の座標変換行列を使い回すことによって、少ないリソースで大量のモデルを描写することが可能。
恒常ループ処理を実装する
変化の過程を常に更新し続ける、つまりアニメーションを実装する。
基本は setTimeout メソッドを使うことにする。
setTimeout メソッドは、第一引数に呼び出すことになる関数(処理)を、第二引数に関数を呼び出す経過時間をミリ秒で指定。
arguments クラスと callee プロパティ
arguments クラスの callee プロパティを参照することで、関数自身への山椒を得ることが可能。
再帰関数の中に詰め込む処理を選別
- 画面のクリア
- モデル座標変換行列の生成
- uniform への座標変換行列の登録
- 描写命令
- 画面の更新
- setTimeout + arguments.callee
つまりこうなる。
// minMatrix.js を用いた行列関連処理
// matIVオブジェクトを生成
var m = new matIV();
// 各種行列の生成と初期化
var mMatrix = m.identity(m.create());
var vMatrix = m.identity(m.create());
var pMatrix = m.identity(m.create());
var tmpMatrix = m.identity(m.create());
var mvpMatrix = m.identity(m.create());
// ビュー×プロジェクション座標変換行列
m.lookAt([0.0, 0.0, 5.0], [0, 0, 0], [0, 1, 0], vMatrix);
m.perspective(45, c.width / c.height, 0.1, 100, pMatrix);
m.multiply(pMatrix, vMatrix, tmpMatrix);
// カウンタの宣言
var count = 0;
// 恒常ループ
(function(){
// canvasを初期化
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// カウンタをインクリメントする
count++;
// カウンタを元にラジアンを算出
var rad = (count % 360) * Math.PI / 180;
// モデル1は円の軌道を描き移動する
var x = Math.cos(rad);
var y = Math.sin(rad);
m.identity(mMatrix);
m.translate(mMatrix, [x, y + 1.0, 0.0], mMatrix);
// モデル1の座標変換行列を完成させレンダリングする
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// モデル2はY軸を中心に回転する
m.identity(mMatrix);
m.translate(mMatrix, [1.0, -1.0, 0.0], mMatrix);
m.rotate(mMatrix, rad, [0, 1, 0], mMatrix);
// モデル2の座標変換行列を完成させレンダリングする
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// モデル3は拡大縮小する
var s = Math.sin(rad) + 1.0;
m.identity(mMatrix);
m.translate(mMatrix, [-1.0, -1.0, 0.0], mMatrix);
m.scale(mMatrix, [s, s, 0.0], mMatrix)
// モデル3の座標変換行列を完成させレンダリングする
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// コンテキストの再描画
gl.flush();
// ループのために再帰呼び出し
setTimeout(arguments.callee, 1000 / 30);
})();