uniform変数で図形単位の平行移動

前回、複数の頂点を移動しながら描画してみましたが、そこではattribute変数で「頂点ごとに」に適用する移動量を設定していました。しかし、三角形など図形(プリミティブ)単位の平行移動では、すべての頂点に同じ移動量を適用するので、本来は移動量を表す(頂点の座標と同次元の)変数一つで済むはずです。

OpenGLとWebGLのシェーダーには、そうした複数の頂点(複数回のシェーダー処理)で同じ値を参照できるuniform変数という仕組みが用意されています。

頂点座標と移動量をx/yといった二次元(vec2型)で指定する形で、三角形を平行移動しながら描く場合を考えてみましょう。attribute変数で頂点座標と移動量を指定するなら、頂点座標と移動量に対してそれぞれ頂点3つ分6個の数値を設定する必要がありました。一方、移動量をuniform変数で共通化してやれば、移動量については二つの数値(一つのvec2型変数)で済むわけです。

uniform変数は、attribute変数と同様にGLSL側で宣言しておいて、JavaScriptではWebGLコンテキストからuniform変数の管理番号を取得し、その番号を指定してデータを書き込みます。今回は、x/y座標の移動量をvec2型のuniform変数tPosとしてやり取りする形にしてみました。

// バーテックスシェーダーにソースコードを設定
gl_context.shaderSource(vshader, 'attribute vec2 vPos; uniform vec2 tPos; void main() { gl_Position = vec4(vPos + tPos, 0.0, 1.0); }');

まず、バーテックスシェーダーでuniform変数tPosを宣言し、vPosとtPosで頂点座標を設定するコードを書いておきます。続いて、JavaScriptでvPosとtPosを設定し、描画コマンドを実行すると、シェーダーのコードが実行されます。

// attribute変数vPos
var vPosLocation = gl_context.getAttribLocation(gl_program, 'vPos');

// vPosを有効化
gl_context.enableVertexAttribArray(vPosLocation);

// 頂点データをFloat32配列として作成
var vlist = new Float32Array(new Array(0.0, 0.3, 0.3, -0.3, -0.3, -0.3));

// バッファ作成
var vbuf = gl_context.createBuffer();

// vPos用バッファをバインドしてデータ領域を初期化・頂点データを転送する
gl_context.bindBuffer(gl_context.ARRAY_BUFFER, vbuf);
gl_context.bufferData(gl_context.ARRAY_BUFFER, vlist, gl_context.STATIC_DRAW);

// データを書き込んだバッファをvPosのデータとして設定
gl_context.vertexAttribPointer(vPosLocation, 2, gl_context.FLOAT, false, 0, 0);

// tPosを取得
tPosLocation = gl_context.getUniformLocation(gl_program, 'tPos');

// tPosにvec値(0.0, 0.0)を設定
gl_context.uniform2f(tPosLocation, 0.0, 0.0);

// vPosとtPosで三角形を描画
gl_context.drawArrays(gl_context.TRIANGLES, 0, 3);

// tPosを取得
tPosLocation = gl_context.getUniformLocation(gl_program, 'tPos');

// tPosにvec値(0.0, 0.6)を設定
gl_context.uniform2f(tPosLocation, 0.0, 0.6);

// vPosとtPosで三角形を描画
gl_context.drawArrays(gl_context.TRIANGLES, 0, 3);

今回は、頂点の座標を保持するattribute変数vPosに(0.0, 0.3)、(0.3, -0.3)、(-0.3, -0.3)という頂点3つ分6つの値を設定し、図形全体の移動量を保持するuniform変数tPosには最初の描画時に(0.0, 0.0)、二回目の描画時には(0.0, 0.6)というそれぞれ二つの値を設定してみました。

これで、シェーダーのコード実行時にはvPosで指定された各頂点の座標にtPosの値が加算され、頂点座標として出力されるようになります。

uniform変数は、バーテックスシェーダーだけでなくフラグメントシェーダーでも参照できるので、フラグメントシェーダーでのピクセル単位の色設定時にも、uniform変数tPosの値を加えてみました。vec2型変数は、「.y」とすると二番目の数値を取得できるので、この値をG成分に設定します。

// フラグメントシェーダーにソースコードを設定
gl_context.shaderSource(fshader, 'uniform mediump vec2 tPos; void main() { gl_FragColor = vec4(1.0, tPos.y, 0.0, 1.0); }');

変数の設定にmediumpとあるのは、精度の設定です。フラグメントシェーダーのfloat値は、任意の精度を設定してから利用することになっているので、今回は中程度の精度を指定してみました。

テストHTML実行

実行してみると、(tPosに0.0, 0.0が入り)WebGLの描画領域中心部分に描かれる三角形は、赤く着色され、(tPosに0.0, 0.6が入り)その上に描かれる三角形はtPosの2番目の値0.6がG成分に反映され黄色く着色されるのが確認できます。


創作プログラミングの街