前言
[前回のノート](/articles/ 用 webgl 绘制一个矩形-webgl 笔记 1/) では頂点シェーダーソースプログラム内で gl_Position をハードコーディングしました。点の位置を修正したい場合はシェーダーソースプログラムを修正する必要があり、これはもちろん意味がありません。私たちはより柔軟な方式が必要です。例えば attribute 変数です
一.util のカプセル化
[前回のノート](/articles/ 用 webgl 绘制一个矩形-webgl 笔记 1/) では疑似矩形(少し大きな点)を描画するために 70 行以上のコードを書きましたが、エラーチェックのみで、互換性処理はまだ行っていません。すべてのステップで WebGL ネイティブ API を呼び出しており、フローは非常に明確ですが、実際には面倒すぎます。1 つの点で 70 行、2 つの点で 140 行ではダメなので、まず util をカプセル化します
始める前に、いくつかの問題に注意する必要があります:
###1.getContext の互換性の問題
getContext('webgl') は標準方式ですが、低バージョンのブラウザでは異なる文字列を渡す必要があります。以下の通り:
/**
* Creates a webgl context.
* @param {!Canvas} canvas The canvas tag to get context
* from. If one is not passed in one will be created.
* @return {!WebGLContext} The created context.
*/
var create3DContext = function(canvas, opt_attribs) {
var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
var context = null;
for (var ii = 0; ii < names.length; ++ii) {
try {
context = canvas.getContext(names[ii], opt_attribs);
} catch(e) {}
if (context) {
break;
}
}
return context;
}
上記のコードは google チームの webgl-utils.js から抜粋したもので、create3DContext の他に requestAnimationFrame の互換バージョンも提供しており、後でアニメーションでも使用するため、私たちの util は直接 webgl-utils.js を include します
###2.program オブジェクトの参照を保持
gl.createProgram() が返す program オブジェクトはシェーダーの初期化プロセス中で使用されるだけでなく、これから行う attribute 変数の値渡しでも使用します。program オブジェクトは js とシェーダー内部通信の架け橋ですが、問題はWebGL が gl.getProgram() のような getter を提供していないため、util をカプセル化する際に必ず program オブジェクトの参照を保持し、外部で使用できるようにする必要があります。例えば:
function initShaders(vsSrc, fsSrc) {
var program = _createProgram(vsSrc, fsSrc);
if (!program) {
log('Failed to create program');
return false;
}
gl.useProgram(program);
// Save program object
//!!! because there is no getter like gl.getProgram()
glUtil.program = program;
return true;
}
上記のコードは筆者の gl-util.js から抜粋したもので、必要であれば直接使用できます。インターフェース説明は以下の通り:
// 全局变量:glUtil
return {
program: null, // runtime assignment
getContext: getContext,
initShaders: initShaders,
debug: function(onOrOff) {
if (typeof onOrOff === 'boolean') {
debug = onOrOff;
}
}
};
二.用語および API 説明
###1.シェーダー
シェーダーは頂点シェーダーとフラグメントシェーダーに分かれます:
- 頂点シェーダー
頂点の特性(位置、色など)を記述するプログラム。頂点とは二次元または三次元空間中の 1 つの点を指し、例えば二次元または三次元図形の端点または交点
- フラグメントシェーダー
逐フラグメント処理プロセス(照明など)を行うプログラム。フラグメントは WebGL 用語で、簡単に言えばピクセル(画像のユニット)と理解できます
P.S. 実際にはフラグメントは画面に表示される 1 つのピクセルを表し、そのピクセル点の位置、色および他の情報も含みます
###2.頂点シェーダーソースプログラム
GLSL ES で書かれたプログラムで、文字列形式で WebGL システム内部に渡されます
// 顶点着色器源程序
var vsSrc = 'void main() {' +
'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' + // 设置坐标
'gl_PointSize = 200.0;' + // 设置尺寸
'}';
その中で、gl_Position に値を割り当てるのは必須で、否则シェーダーは正常に動作しません。gl_Position は vec4 タイプで、つまり四次元ベクトル(同次座標、最後の成分は通常 1.0 を取る)
gl_PointSize にはデフォルト値 1.0 があり���値を割り当てる必要はありません。gl_PointSize は float タイプで、受け付けません3f(C 言語中で 3f は浮動小数点数 3 を表す)のような形式で、しかも孤立点を描画する時のみ有効です(孤立点の描画とは何か、後で説明します)
###3.フラグメントシェーダーソースプログラム
頂点シェーダーソースプログラムと同じですが、制御するのはフラグメントシェーダーです
// 片元着色器源程序
var fsSrc = 'void main() {' +
'gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75);' + // 设置颜色
'}';
gl_FragColor は唯一の組み込み変数で、vec4 タイプですが、rgba 色値を表します。rgba の取值はすべて 0~1 ので、CSS 中の rgb が 0~255 のとは異なります
###4.描画操作
描画操作には 1 つのインターフェースのみですが、機能は非常に強力です:
// 绘制矩形(一个点,但点的尺寸略大)
gl.drawArrays(gl.POINTS, 0, 1);
API の具体情報は以下の通り:
gl.drawArrays(mode, first, count)
mode 指定绘制方式,接受常量:
---
gl.POINTS 孤立点(v0, v1...)
gl.LINES 孤立线段(v0v1, v2v3...)
gl.LINE_STRIP 连续线段(v0v1, v1v2...)
gl.LINE_LOOP 连续线段连成圈(v0v1, v1v2...vnv0)
gl.TRIANGLES 孤立三角形(v0v1v2, v3v4v5...)
gl.TRIANGLE_STRIP 连续三角带(v0v1v2, v2v1v3, v2v3v4...),用来构造复杂模型,如球、树
注意:第二个三角形是 v2v1v3,不是 v1v2v3,这是为了保证第二个三角形的绘制也按照逆时针顺序
gl.TRIANGLE_FAN 三角扇(v0v1v2, v0v2v3, v0v3v4),也可以用来构造复杂模型,但在实际应用中不常见,因为需要求出所有三角形的公共顶点 v0
first 指定从哪个顶点开始绘制
---
0 表示从第一个开始,配合缓冲区对象使用(缓冲区对象中可以存放多个顶点信息)
count 指定需要绘制点的个数,同样配合缓冲区对象使用
---
调用 gl.drawArray 时,顶点着色器会被执行 count 次,每次处理一个顶点,顶点着色器执行完毕后片元着色器开始执行,给片元涂色(中间还有光栅化的过程)
特に注意:頂点シェーダーは逐頂点実行され、フラグメントシェーダーは逐フラグメント実行され、1 回のみ実行されるわけではありません
三.attribute 変数
シェーダーソースプログラム中で変数を宣言し、その後 js がこの変数に値を渡し、さらに描画することで、点の位置を動的に修正できます。簡単に言えばこうです
###1.attribute 変数の宣言
// 顶点着色器源程序
var vsSrc = 'attribute vec4 a_Position;' +
'void main() {' +
'gl_Position = a_Position;' + // 设置坐标
'gl_PointSize = 200.0;' + // 设置尺寸
'}';
// 片元着色器源程序
//...不变
C 言語の変数宣言方式に類似し、attribute は保存限定子と呼ばれ、vec4 は変数タイプです。変数名 a_Position 中の a_ プレフィックスは変数が attribute 変数であることを表し、もちろんこれは習慣ですが、採用することをお勧めします
P.S.なぜ頂点シェーダーソースプログラム中で attribute 変数を宣言するのか、フラグメント...ではないのか。私たちが点の座標を動的に修正したいからで、座標情報は頂点シェーダーソースプログラム中の gl_Position から来ており、フラグメントシェーダーとは関係がないからです
注意:頂点シェーダーのみが attribute 変数を使用でき、フラグメントシェーダーでは uniform 変数を使用する必要があります。さらに varying 変数もあり、具体の違いは GLSL ES ノートでさらに詳細に説明します
###2.シェーダーの初期化
直接 util を呼び出し、1 行のコードで残りの 7 つの事(createShader から useProgram までの 7 つのステップ)を完了できます
// 1.初始化着色器
glUtil.initShaders(vsSrc, fsSrc);
###3.attribute 変数に値を割り当てる
変数への値割り当ては 2 ステップに分かれ、まず保存位置を取得し、その後値を割り当てます
// 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, 0.5, 0.0, 0.0);
###4.描画
// 绘制点
gl.drawArrays(gl.POINTS, 0, 1);
最初のパラメータに gl.POINTS を渡すと孤立点の描画を表し、線分、三角形なども描画できます。後で説明します
四.DEMO
上記のコードを含む完全な例は、以下を参照:http://www.ayqy.net/temp/webgl/attribute/index.html
五.まとめ
現在私たちは頂点座標を動的に修正できますが、もちろん、これは非常にシンプルなステップで、後にはさらに:
- マウスクリック位置で点を描画するには?
答えは想象的に那么简单ではなく、canvas と WebGL 座標系が異なり、座標を変換する必要があります
- 点の色を修正するには?
uniform 変数で十分です
- 複数の点を描画するには
gl.drawArrays(gl.POINTS, 0, 1); をループ?できますか?
これらは後のノートで討論する問題で、まず焦らないでください
参考資料
- 《WebGL プログラミングガイド》
コメントはまだありません