はじめに
[前回のノート](/articles/マウスクリック位置に点を描画する-webgl ノート 3/) では、attribute 変数を通じてマウスクリック位置の座標情報を頂点シェーダーに渡し、クリックした位置に点を描画する効果を実現しましたが、描画される点はすべて同じ色でした。点の色を動的に設定したい場合、uniform 変数が必要です。なぜならattribute 変数は頂点シェーダーでのみ使用可能で、色情報はフラグメントシェーダー中にあるからです
一.uniform 変数と attribute 変数
uniform と attribute は類似しており、どちらも記憶限定子です。主な違いは以下の通り:
- attribute
頂点シェーダーでのみ使用可能。頂点ごとのデータ(各頂点でこの値が異なる)を表すために使用。例えば前面で使用した attribute vec4 a_Position
- uniform
頂点シェーダーでもフラグメントシェーダーでも使用可能。一貫した、不変のデータ(各頂点でこの値が同じ)を表すために使用。例えば今後テクスチャマッピングで使用する uniform Sampler2D u_Sampler、および私たちがまもなく使用する uniform vec4 u_FragColor
P.S. uniform と attribute には其它の違いもあり、もう 1 つの記憶限定子は varying です。これらの内容はすべて GLSL ES 構文ノートで展開します
二.点の色を設定
座標の設定と類似し、まず uniform 変数を宣言して色情報を受け取り、次に色値を渡して描画します
###1.フラグメントシェーダー
着色のプロセスはフラグメントシェーダー中で行われます。フラグメントシェーダーソースプログラムを修正する必要があります:
// フラグメントシェーダーソースプログラム
//!!! 浮動小数点精度を宣言する必要があります。否则エラー No precision specified for (float) が報告されます
var fsSrc = 'precision mediump float;' +
'uniform vec4 u_FragColor;' +
'void main() {' +
'gl_FragColor = u_FragColor;' + // 色を設定
'}';
用法は attribute 変数と完全に一致しますが、特に注意が必要な点が 1 つあります:浮動小数点精度を宣言する必要があります。否则フラグメントシェーダーはコンパイルに失敗し、以下のエラーを報告します:
ERROR: 0:1: '' : No precision specified for (float)
###2.色を設定
gl.getAttribLocation と gl.vertexAttrib3f に類似し、ここでは gl.getUniformLocation と gl.uniform4f を使用します:
// 色を設定
// uniform 変数の記憶位置を取得
var u_FragColor = gl.getUniformLocation(glUtil.program, 'u_FragColor');
var color = item.color;
// 色値を uniform 変数に渡す
gl.uniform4f(u_FragColor, color.r, color.g, color.b, color.a);
色値 rgba はすべて 0.0~1.0 であることに注意。この区間にない数値は自動的に 0.0 または 1.0 に切り捨てられますが、エラーにはなりません
###3.複数の点を描画
ここでは直接 gl.drawArrays(gl.POINTS, 0, 1); をループして実現しました。方法は科学的ではありませんが効果は実現できます。より科学的方法は buffer と配合する必要があります。これは次回のノートの内容で、私たちはまだロックを解除していません
var arrPos = [];
webgl.addEventListener('click', function(e) {
// 座標を変換
var x = (e.offsetX - webgl.width / 2) / (webgl.width / 2);
var y = (webgl.height / 2 - e.offsetY) / (webgl.height / 2);
console.log(x, y);
// ランダムな色
var color = {
r: Math.random(),
g: Math.random(),
b: Math.random(),
a: Math.random()
};
// 座標、色を記録
arrPos.push({
x: x,
y: y,
color: color
});
// canvas をクリア
gl.clear(gl.COLOR_BUFFER_BIT);
// 記録したすべての座標位置に点を描画
arrPos.forEach(function(item) {
// 2.attribute 変数に値を代入
// attribute 変数の記憶位置を取得
var a_Position = gl.getAttribLocation(glUtil.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return;
}
// 頂点位置を attribute 変数に渡す
gl.vertexAttrib3f(a_Position, item.x, item.y, 0.0);
// 色を設定
// uniform 変数の記憶位置を取得
var u_FragColor = gl.getUniformLocation(glUtil.program, 'u_FragColor');
var color = item.color;
// 色値を uniform 変数に渡す
gl.uniform4f(u_FragColor, color.r, color.g, color.b, color.a);
// 点を描画
gl.drawArrays(gl.POINTS, 0, 1);
});
});
新しいものはなく、ただ乱暴なループです。唯一注意が必要な問題はループ前の clear です。前回のノートでこの問題に言及しました:描画完了後、カラーバッファはリセットされ、その中の内容は失われます。効果から見ると、clear は毎回描画前に黒色背景を設定しています。clear がない場合、背景は透明になります(vec4(0.0, 0.0, 0.0, 0.0))
三.DEMO
上記コードを含む完全な例は、以下を参照:http://www.ayqy.net//temp/webgl/uniform/index.html
四.まとめ
私たちは点点点点で燦爛な星空を点き出す効果を実現しました。パフォーマンスを考慮しない場合、このように実現するのは悪くありません。なぜなら次回のノートで紹介する buffer はより面倒に見えるが、利点もあるからです。buffer があれば 1 回で一組のデータを渡すことができ、今後三角形を描画し、三角形に色を塗り、テクスチャを貼るのが非常に便利になります
loop draw a point は結局長久之計ではありません。1 つ 1 つの点で三角形を拼うことはできません。まして 3D モデルもあります。buffer は���常に必要です
参考資料
- 《WebGL プログラミングガイド》
コメントはまだありません