メインコンテンツへ移動

buffer で複数の点を描画_WebGL ノート 5

無料2015-12-24#JS#WebGL#WebGL buffer#类型化数组#webgl array buffer#webgl vertex buffer#webgl绘制多个点#webgl drawArrays

buffer は複数の頂点情報を格納でき、一度に描画できます。buffer があって初めて三角形を描画でき、複雑な図形を構築できます

前言

[前回のノート](/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 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 プログラミングガイド》

コメント

コメントはまだありません

コメントを書く