どうも。営業部の伊藤です。
当シリーズも今回で最後です!!!
かなり間があきましたが、、、ちゃんと書きます。
前回のブログでSTEP4の「モデルデータを用意」とSTEP5の「モデルデータからVBO(VertexBufferObject)を生成&バインド」まで書きましたので、今回は3DCG描画の仕上げである
STEP6「座標変換行列を生成&通知」
STEP7「描画命令」
STEP8「STEP6~7の再帰処理(canvasをレンダリング)」
を書きます。
モデルデータからVBO(VertexBufferObject)の生成&バインドまで終わりましたので、最後に三次元を二次元に変換してみようと思います。
通常人間が見ている物体は三次元空間に存在しますので、全てのものに縦と横と奥行きがあります。ただし、モニター上では三次元情報を出力できない(実際には出力できるものもあります)ので、シュミレーションしたモデルデータを擬似的に三次元に変換して表示する必要があります。
ここで必要となる情報、計算が「座標変換行列」です。
この行列には「モデル変換(ワールド変換)」、「ビュー変換」、「プロジェクション変換」と計3種類あり、これらを組み合わせて計算することで擬似的に三次元を二次元空間で再現する事ができます。
それぞれの変換定義を私なりに認識で簡単に書くと、以下のようになります。
○モデル変換
物体が存在するためには基準となる原点があり、またそれが相対的にどこにあるかという位置を定義する必要があります。
空間、また物体の相対的位置を定義するのがモデル変換です。
○ビュー変換
当たり前ではありますが、物体を見る為には「目」が必要になります。
ビュー変換はその「目」がどの位置にあるのか、またどこを向いているのかを定義するものです。
○プロジェクション変換
最後に、ビュー変換で定義した「目」がどこまで見えるのか、どこまで見るのかも定義が必要です。
人間の目にも視野、視力があるように、三次元を描画する際にもどの領域を表示するのか、どこまで(距離)表示するのかを定義するのがプロジェクション変換です。
ビュー変換、プロジェクション変換用の配列、計算にはPart1で紹介した「glMatrix.js」を利用させていただきました。
STEP6~STEP8の一連の処理を実行しているdrawObject(Function)で該当部分を確認していきます。
requestAnimFrame(drawObject);
gl.viewport(0, 0, canvas.width, canvas.height);
まずはrequestAnimFrameで3DCG描画処理の再帰(ループ)を命令しています。
こちらもwebgl-utilというライブラリを利用しています。
その後、viewportでWebGLに三次元空間を表示する領域を設定します。
var rad = (count % 360) * Math.PI / 180;
var x = Math.cos(rad);
var y = Math.sin(rad);
var mod = count % 4;
var s = Math.sin(rad) + 1.0;
var timeNow = new Date().getTime();
if (lastTime != 0) {
var elapsed = timeNow – lastTime;
rotate += (90 * elapsed) / 1000.0;
}
lastTime = timeNow;
count++;
mat4.identity(matrixMove);
mat4.translate(matrixMove, [x, y, -7.0]);
mat4.scale(matrixMove, [s, s, 0.0], matrixMove);
mat4.rotate(matrixMove, rotate * Math.PI / 100, [0, 1, 1]);
vertexMatrix2 = gl.getUniformLocation(shaderProgram, “vertexMatrix2”);
gl.uniformMatrix4fv(vertexMatrix2, false, matrixMove);
モデル変換で弧を描いたり、前後に移動させたりするための各種計算処理を実施後、シェーダ側に値を受け渡します。
x、yが座標用、modが瞬き用、sが大きさ、rotateが回転の値です。
mat4.perspective(75, canvas.width / canvas.height, mod, 90, matrixPerspective);
vertexMatrix1 = gl.getUniformLocation(shaderProgram, “vertexMatrix1”);
gl.uniformMatrix4fv(vertexMatrix1, false, matrixPerspective);
次にプロジェクション変換用にどの領域を表示するのか、どこまで(距離)表示するのかを設定します。
描画シーンの視野角や表示領域の横幅と高さの比、視力(非表示とする距離)を設定後、シェーダ側に値を受け渡します。
ここで特殊なのが、modの部分です。本来であればこれより近づいたら表示しない距離を設定するため固定値になると思いますが、今回は(目の)瞬きを想定して定期的に視野を無くすためmodを設定しています。
var vertexPosition = gl.getAttribLocation(shaderProgram, “vertexPosition”);
gl.bindBuffer(gl.ARRAY_BUFFER, vboPosition);
gl.enableVertexAttribArray(vertexPosition);
gl.vertexAttribPointer(vertexPosition, 3, gl.FLOAT, false, 0, 0);
vertexTexture = gl.getAttribLocation(shaderProgram, “vertexTexture”);
gl.bindBuffer(gl.ARRAY_BUFFER, vboSurface);
gl.enableVertexAttribArray(vertexTexture);
gl.vertexAttribPointer(vertexTexture, 2, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
最後に、STEP3で用意していた物体自体の座標情報、テクスチャ座標などをシェーダ側に受け渡し、描画(drawElements)します。
ここまでの一連の流れ(drawObject)を再帰的に実行することで三次元空間を表現し、且つ動きを表現します。
Part1の投稿からちょうど2ヶ月が経ちましたが、簡単なWebGLの実装については以上です。
正直なところ、色々と忘れてしまっているので…これからも定期的に復習、コーディングしようと思います。
また気が向いたら三次元POSTします!
ではでは。