前言
[前回のノート](/articles/uniform 変数とフラグメントシェーダー-webgl ノート 4/) では、比較的愚かな方法で複数の点を描画しましたが、効率が低く、三角形のような基本図元を描画することもできず、複雑な図形はなおさら論外でした
一.buffer の理解
buffer は WebGL システム内部で区切られた領域で、1 組の頂点データを格納できます。例えば頂点座標など。頂点データを buffer に書き込んだ後、シェーダープログラム実行時に buffer からデータを読み取ることができ、データ量が大きい時に特に便利です
シェーダープログラム中では直接 buffer を読み取ることはできませんが、まず API を通じてシェーダー変数値を buffer のアドレスに指定し、その後データ読み取り方法を指定できます。例えばどこから読み取るか?いくつ読み取るか?次の頂点のデータはどこにあるか?間接的に buffer からデータを読み取ります
二.型付き配列
頂点データの準備は本来单独で討論する必要はありませんでしたが、このステップは非常に間違えやすく、正しいコードは以下の通り:
// 一次性传递一组顶点数据
//!!! 注意:不是一般数组,需要 new 类型化数组,否则报错
// GL ERROR :GL_INVALID_OPERATION :glDrawArrays: attempt to access out of range vertices in attribute 0
// var arrVtx = [-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 0.0, 0.0];
var arrVtx = new Float32Array([-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 0.0, 0.0]);
特に注意:buffer に書き込む準備をするデータは通常の配列ではなく、型付き配列を new する必要があります。否则エラーが発生し、エラー情報は以下の通り:
GL ERROR :GL_INVALID_OPERATION :glDrawArrays: attempt to access out of range vertices in attribute 0
エラー情報だけから頂点データに遡るのは難しいため、特に注意が必要です
GLSL ES は強型言語で、js は弱型です。js 配列要素には制限がなく、GLSL ES は非常に厳格なデータを必要とするため、型付き配列が生まれました。WebGL には、型付き配列が以下の 8 種類あります:
-
Int8Array
-
UInt8Array
-
Int16Array
-
UInt16Array
-
Int32Array
-
UInt32Array
-
Float32Array
-
Float64Array
違いは各要素が占めるバイト数が異なることです(型付き配列には BYTES_PER_ELEMENT 属性があり、配列内の各要素が占めるバイト数を取得できます。例えば Float32Array は各要素が 4 バイトを占めます)。通常の配列と比較して、型付き配列は push と pop メソッドをサポートしませんが、「大量の要素がすべて同じ型」に対して最適化されています
三.buffer の使用
buffer の作成から 1 組の頂点データの書き込みまで、5 つのことを行う必要があります
###1.buffer の作成
直接 API を呼び出して buffer を作成します。以下の通り:
// 1.创建 buffer
var vBuffer = gl.createBuffer();
if (!vBuffer) {
console.log('Failed to create buffer');
return;
}
作成に失敗すると null を返しますが、この種の関数のエラー率は比較的低いため、ここでは厳格なエラー判断を行いません(エラー率の高い部分は必ず厳格な判断を行う必要があります。例えばシェーダーソースプログラムのコンパイル)
###2.バッファ領域オブジェクトをターゲットにバインド
バッファ領域を作成した後、バッファ領域オブジェクトを WebGL システム中にすでに存在する target にバインドする必要があります
// 2.把缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
buffer はすでに作成されましたが、次のステップはデータを書き込むべきではありませんか?ターゲットをバインドして何をするのでしょうか?
私たちが直接バッファ領域にデータを書き込むことができないため、target に書き込むしかないからです。したがってバッファ領域にデータを書き込むには、まず target と buffer をバインドする必要があります。target はバッファ領域オブジェクトの用途を表し、値は gl.ARRAY_BUFFER または gl.ELEMENT_ARRAY_BUFFER です。前者はバッファ領域オブジェクトが頂点データを含んでいることを表し、後者は頂点のインデックス値を含んでいることを表します
###3.バッファ領域オブジェクトにデータを書き込む
gl.bufferData(target, data, usage) を通じてデータを書き込みます
// 3.向缓冲区对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, arrVtx, gl.STATIC_DRAW);
その中で data は型付き配列で、前面ですでに紹介しました
usage はバッファ領域データの用途を表し、WebGL は用途に応じて最適化を行います。値は gl.STATIC_DRAW(バッファ領域オブジェクトに 1 回のみデータを書き込むが、何度も描画する必要がある)、gl.STREAM_DRAW(バッファ領域オブジェクトに 1 回のみデータを書き込み、その後数回描画する)または gl.DYNAMIC_DRAW(バッファ領域オブジェクトに複数回データを書き込み、何度も描画する)です。違いはあまり明確ではなく、しかもパフォーマンスに影響する可能性のみあり、最終結果には影響しません
###4.バッファ領域オブジェクトを a_Position 変数に割り当てる
buffer の準備ができました。今度はシェーダー変数に値を割り当て、シェーダーにこれらのデータを後でどのように使用するかを伝える必要があります
// 4.将缓冲区对象分配给 a_Position 变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 等价于
// gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, arrVtx.BYTES_PER_ELEMENT * 2, 0);
gl.vertexAttribPointer(location, size, type, normalized, stride, offset) を通じて attribute オブジェクトにバッファポインタを割り当てます。パラメータの意味は以下の通り:
gl.vertexAttribPointer(location, size, type, normalized, stride, offset)
---
size バッファ中各頂点の成分数(1~4)、各頂点データ中シェーダー変数に割り当てる成分数を表す
type データ形式、値は gl.UNSIGNED_BYTE、gl.SHORT、gl.UNSIGNED_SHORT、gl.INT、gl.UNSIGNED_INT、gl.FLOAT の 5 種
normalized true|false、非浮動小数点型データを [0, 1] または [-1, 1] 区間に正規化するかどうかを表す
stride 隣接する 2 つの頂点間のバイト数を指定、デフォルトは 0
offset バッファ領域オブジェクト中のオフセット量、バッファの offset 位置から書��込み開始、最初からなら 0
特に注意:stride パラメータは比較的特殊で、各頂点が n 個のデータを持つ場合、stride を arr.BYTES_PER_ELEMENT * n に設定する効果は 0 と同じです。現在の頂点データ読み取り終了後、stride が 0 なら、現在の頂点データ終了の位置から次の頂点のデータを読み取り始め、stride が 0 でないなら、現在の頂点データ開始の位置から stride が指定するバイト数をスキップしてから読み取りを開始します
P.S.理解しにくいかもしれませんが、事実はこうです。今後の具体例でさらに説明します
###5.a_Position 変数とそれに割り当てられたバッファ領域オブジェクトを接続
buffer の準備ができ、シェーダーにデータの使用方法も伝えました。最後に変数とバッファ領域を接続する必要があります
//!!! 注意:分配完还要 enable 连接
// 5.连接 a_Position 变量和分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position);
このステップは非常に忘れやすく、論理的にはすでに完全で、このステップは純粋に余分ですが、API はこうなっており、このステップは下図のスイッチのようなものです:
[caption id="attachment_901" align="alignnone" width="893"]
webgl-buffer[/caption]
###6.複数の点を描画
// 绘制点
gl.drawArrays(gl.POINTS, 0, arrVtx.length / 2);
ようやく 3 番目のパラメータを使用しました。もはや draw a point ではなく、3 番目のパラメータは描画する頂点の個数を表します。本例中配列長さ arrVtx.length を各頂点のデータ数 2 で割ると、arrVtx 中のすべての頂点(合計 5 個)を描画することを表します
四.DEMO
上記のコードを含む完全な例は、以下を参照:http://www.ayqy.net/temp/webgl/buffer/index.html
五.まとめ
本例では、buffer を通じて 5 つの点の座標(4 つの角および中心)を伝送し、一度に描画しました。最も基本的な入力データの問題を解決しました
buffer をアンロックした後、図元およびやや複雑な図形に触れることができます。次のノートでは最も不思議な図元——三角形を描きます。このように三角形を褒めるのは本当に間違いではありません。事実上、三角帯で全世界を拼い出すことができます。人物モデル、BOSS モデル、花草樹木などすべて三角帯で拼い出されたものです
参考資料
- 《WebGL プログラミングガイド》
コメントはまだありません