ポリゴンのレンダリング
レンダリングへの一連の流れ
script.js のコード全文の流れ
canvas, webglコンテキストの準備
シェーダの用意、登録、 VBOの用意と登録
座標変換行列の用意と登録
glに命令して描画
canvas, webglコンテキストの準備
code:script.js
onload = function(){
// canvasエレメントを取得
var c = document.getElementById('canvas');
c.width = 300;
c.height = 300;
// webglコンテキストを取得
var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
// canvasを初期化する色を設定する
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// canvasを初期化する際の深度を設定する
gl.clearDepth(1.0);
// canvasを初期化
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
初期化処理
一番最初にやっていることは canvas への参照を取得
canvas のサイズを縦横 300 px に設定
その後、WebGL コンテキストを取得し、クリアする色を設定しています。
次に出てくるのがクリアする深度の設定です。
clearDepth メソッドはコンテキストをクリアする際の深度を設定するために使います。
以前のサンプルでは色を初期化するだけだったので clearColor メソッドしか使いませんでしたが、
実際に三次元空間を扱う場合には、奥行きに関する情報もクリアする必要があるので、
clearDepth メソッドも併せて実行します。
同様の理由により clear メソッドに与える引数も変化します。
色だけでなく深度に関してもクリアするようにするために、
gl.DEPTH_BUFFER_BIT という組み込み定数を引数に追加しています。
シェーダの用意, vboへ頂点の登録
code:script.js
// 頂点シェーダとフラグメントシェーダの生成
var v_shader = create_shader('vs');
var f_shader = create_shader('fs');
// プログラムオブジェクトの生成とリンク
var prg = create_program(v_shader, f_shader);
// attributeLocationの取得
var attLocation = gl.getAttribLocation(prg, 'position');
// attributeの要素数(この場合は xyz の3要素)
var attStride = 3;
// モデル(頂点)データ
var vertex_position = [
0.0, 1.0, 0.0,
1.0, 0.0, 0.0,
-1.0, 0.0, 0.0
];
// VBOの生成
var vbo = create_vbo(vertex_position);
// VBOをバインド
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
// attribute属性を有効にする
gl.enableVertexAttribArray(attLocation);
// attribute属性を登録
gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0);
code:script.js
// 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 mvpMatrix = m.identity(m.create());
// ビュー座標変換行列
// プロジェクション座標変換行列
m.perspective(90, c.width / c.height, 0.1, 100, pMatrix);
// 各行列を掛け合わせ座標変換行列を完成させる
m.multiply(pMatrix, vMatrix, mvpMatrix); // mvp <- p * v
m.multiply(mvpMatrix, mMatrix, mvpMatrix); // mvp <- (p*v) * m
// uniformLocationの取得
var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix');
// uniformLocationへ座標変換行列を登録
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
行列の用意, uniform attributeとしてデータを渡す
code:script.js
// モデルの描画
gl.drawArrays(gl.TRIANGLES, 0, 3);
// コンテキストの再描画
gl.flush();
シェーダとプログラムオブジェクトの生成
頂点シェーダとフラグメントシェーダを生成し、
プログラムオブジェクトでリンクさせます。
復習
create_program 関数の内部では、プログラムオブジェクトの生成と、シェーダのリンクを行なっています。
続いて、二つの変数を定義しています。
ここで定義している attLocation と attStride という二つの変数は、このあと頂点シェーダにデータを渡す際に必要となる情報を保持するのに使います。
ここで少し話が戻りますが、以前のテキストで示した今回使用する頂点シェーダのソースを見てみましょう。
そうすると、二つの変数を定義した理由が少しだけ見えてきます。
今回使用する頂点シェーダのソース
code:c
attribute vec3 position;
uniform mat4 mvpMatrix;
void main(void){
gl_Position = mvpMatrix * vec4(position, 1.0);
}
今回使用する頂点シェーダでは、利用する attribute 変数は一つだけです。 position がそれにあたりますね。この変数 position は、ご覧の通り vec3 として宣言されていますね。これは、三つの要素を持つベクトルであることを意味します。
ここでのポイントは
利用する attribute 変数は一つでその名前は position であるということと、
その変数は vec3 型の変数である
という二つの事実です。
実は attribute 変数としてシェーダにデータを渡す際には、
そのデータが何番目の attribute 変数なのかということと、
その変数はいくつの要素から成るのか
ということの二つの情報が必要になります。
getAttribLocationでprogramオブジェクトから、positionという名前で取得ができる
つまり変数 attLocation は、そのデータが何番目のデータなのかということを保持するために使い、
変数 attStride はデータがいくつの要素から成るのかを保持するために使うわけです。
WebGL コンテキストのメソッドである getAttribLocation には、
第一引数に対象となるプログラムオブジェクトを、
第二引数に取得したい attribute 変数の名前を指定します。
戻り値として返される数値が、
頂点シェーダにデータを渡す際のインデックスとしての役割を果たします。
つまり何番目の変数なのかということを教えてくれるわけですね。
変数 attStride は、頂点シェーダ内の attribute 変数である position が、三つの要素を持つ vec3 型の変数であることを示すために使う変数です。
頂点バッファ( VBO )の生成と通知
頂点データ(モデルデータ)を定義して VBO を生成します。
その後、VBO を頂点シェーダに関連付けるためにバインドし登録します。
VBO の生成と通知に関する処理の抜粋
code:js
// モデル(頂点)データ
var vertex_position = [
0.0, 1.0, 0.0,
1.0, 0.0, 0.0,
-1.0, 0.0, 0.0
];
// VBOの生成
var vbo = create_vbo(vertex_position);
// VBOをバインド
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
// attribute属性を有効にする
gl.enableVertexAttribArray(attLocation);
// attribute属性を登録
gl.vertexAttribPointer(attLocation, attStride, gl.FLOAT, false, 0, 0);
以前のテキスト(モデルデータと頂点属性)でも詳しく解説したとおり、頂点のデータは単純な配列として定義します。
その配列データから、オリジナル関数である create_vbo を使って頂点バッファ( VBO )を生成します。
頂点バッファと頂点シェーダ内の attribute 変数を紐付けするために、まずは WebGL に VBO をバインドします。その後、先ほど取得した attribute 属性のインデックスを使って、その attribute 属性を有効にします。WebGL のメソッドである enableVertexAttribArray を使うことで、目的の属性を有効にできます。
次に、WebGL のメソッドである vertexAttribPointer を使いシェーダにデータを登録します。
先に定義してあった二つの変数 attLocation と attStride はここでも使うのですね。
vertexAttribPointer の
第一引数には attribute の何番目であるのかを示すインデックスを、
第二引数にはその要素数を、
第三引数にはそのデータがどんなデータ型であるかを示す組み込み定数を指定します。
gl.FLOAT は浮動小数点数を示す定数です。
第四~第六引数は基本的にはほとんど変更することはありませんが、
メモリ上の参照を駆使して特殊なデータの受け渡し方をしたい場合には使うこともあります。
注意点としては、
vertexAttribPointer を実行する際には、対象となる VBO を必ずバインドしておくようにします。
どの VBO をどの attribute 属性に関連付けるかは、bind経由で伝える
座標変換行列の生成と通知
座標変換行列の生成と通知に関する処理を抜粋
code: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 mvpMatrix = m.identity(m.create());
// ビュー座標変換行列
// プロジェクション座標変換行列
m.perspective(90, c.width / c.height, 0.1, 100, pMatrix);
// 各行列を掛け合わせ座標変換行列を完成させる
m.multiply(pMatrix, vMatrix, mvpMatrix);
m.multiply(mvpMatrix, mMatrix, mvpMatrix);
// uniformLocationの取得
var uniLocation = gl.getUniformLocation(prg, 'mvpMatrix');
// uniformLocationへ座標変換行列を登録
gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
今回の場合は、モデル変換行列については初期化してそのまま
モデルを動かさない
モデル・ビュー・プロジェクションの各変換行列を掛け合わせて、最終的には mvpMatrix という座標変換行列を完成させます。
WebGL に uniform 変数として登録するには、
attribute 変数のときと同じ要領でまずはロケーションを取得します。
WebGL のメソッドである getUniformLocation にプログラムオブジェクトと変数の名称を与えて呼び出すと、何番目の uniform 変数なのかというインデックスが得られます。
得られたインデックスを元に、今度は頂点シェーダにデータが正しく受け渡されるように登録する作業を行ないます。
これには、WebGL のメソッドである uniformMatrix4fv を使います。
第一引数にはロケーションのインデックスを、
第二引数には行列を転置するのかどうかを真偽値で
(ただし true にするとクラッシュする場合があります)、
第三引数には実際に登録する行列を指定します。
uniform 系メソッドについて
今回登場した uniformMatrix4fv に代表される uniform 系のメソッドには、たくさんの種類があります。
大別すると、次のような種類があります。
uniform 系
uniform 系には、uniform1 ~ uniform4 までがあり、
それぞれ一つから四つの要素を頂点シェーダに登録するために使います。
渡されるデータのタイプが整数なのか浮動小数点数なのかによって、
数字の後ろに i (int)や f (float)などの小文字アルファベットを付加します。
例としては、
二つの要素を持つ浮動小数点数のデータを渡したい場合には、uniform2f を使えばいいということになります。
記述例 : gl.uniform2f(uniformLocation, data1, data2);
uniform v 系
この系統は、基本的には前述の uniform 系と大差ありませんが、データを配列として渡す場合に使います。
uniform 系と同様、1 ~ 4 までがあり、扱うデータタイプに応じて i もしくは f を付加します。
記述例 : gl.uniform3iv(uniformLocation, Array);
uniformMatrix 系
その名の通り、行列を扱う場合にはこちらの系統を用います。
当然のことながら行列を扱うわけですから 1 はなく、2 ~ 4 までが存在します。
行列のデータは基本的に浮動小数点数のデータですので、i も存在しませんし、
データは配列として扱うことを前提としているため、無印と v 付きの区別もありません。
記述例 : gl.uniformMatrix4fv(uniformLocation, false, Matrix);
モデルの描画とコンテキストの再描画
シェーダ、頂点データ、座標変換行列と、いろいろな作業を経てきましたが、いよいよ描画命令です。
描画命令とリフレッシュに関する処理の抜粋
code:js
// モデルの描画
gl.drawArrays(gl.TRIANGLES, 0, 3);
// コンテキストの再描画
gl.flush();
WebGL のメソッド drawArrays が呼び出されるとモデルがバッファ上に描画されます。
ここであえて バッファ上に と書いたのには理由があって、
drawArrays が呼び出された時点では、画面上にはポリゴンはまだ描画されません。
画面上にレンダリングされたモデルを描画するためには、コンテキストをリフレッシュする必要があり、WebGL のメソッドである flush が実行されて初めて、レンダリング結果が画面上に反映されます。
ここで登場した drawArrays メソッドには、
第一引数に頂点をどのように利用して描画するのかを指定するための組み込み定数を、
第二引数には何番目の頂点から利用するかのオフセットを表す数値を、
第三引数にいくつの頂点を描画するのかを数値で指定します。
今回の場合は gl.TRAIANGLES を指定しているので、頂点は純粋な三角形ポリゴンとして三つの頂点を利用して描画されます。