モデルデータと頂点属性
頂点属性の意味
モデルデータを定義する最も簡単な方法の解説
頂点属性について取り扱いたいと思います。
また、モデルデータをから VBO を生成する方法も併せて解説します。
VBO は、実際のところ生成するところよりも生成してからのほうが難解だったり
頂点属性
頂点が持つ様々な要素のこと
WebGL の場合は最低限、頂点が位置情報という属性を持っている必要がある
しかし、頂点にはそれ以外の属性を持たせることも可能
色
法線
テクスチャ座標
頂点属性はプログラマが自由にそれを定義することが可能
DirectX の場合には、いわゆる頂点フォーマットと呼ばれる仕組みによってこれを実現しますが、
WebGL の場合には頂点属性という形で頂点に様々な特徴を持たせることが可能
頂点属性と VBO
頂点属性を自由に定義できるとは言っても、それは具体的にどういう手順で実現できるのでしょうか。
頂点属性の数と、生成する VBO の数はおなじになる
頂点に三つの属性を持たせようとするなら、VBO も三つ必要になります。
頂点属性一つにつき、一つの VBO が割り当てられ、頂点シェーダに通知される
VBO を生成するためには、まず先に頂点の個数に応じた配列を用意します。
これは、いわゆる普通の javascript の配列です。
たとえば、三つの頂点からなる一枚のポリゴンを定義するときの例が次のようになります。
モデルデータを格納する配列の例
code:js
var vertex_position = [
// X, Y, Z
0.0, 1.0, 0.0,
1.0, 0.0, 0.0,
-1.0, 0.0, 0.0
];
わかりやすくするために途中で改行していますが、これは単なる一次元配列になっています。
flatten
VBO の生成
単純な配列による頂点データが準備できたら、その配列を使って VBO を生成することが可能になります。
VBO を生成するには、WebGL のメソッドである createBuffer を使います。
createBuffer メソッドはその名の通りバッファを生成するメソッドです。
つまり、このメソッドは直接 VBO そのものを生成するというわけではありません。
このメソッドによって生成されるのはあくまでもバッファオブジェクト
そのバッファにどんな情報を格納したのかによって、用途が変わってくるということですね。
バッファにもvbo以外にも色々ある
バッファを操作する場合には、まず最初にバッファを WebGL にバインドする必要があります。
言うなれば、バッファという名のディスクにデータを書き込むために、WebGL という名のドライブにセットするようなイメージですね。
まあこれは古いAPIという認識で良いかmiyamonz.icon
バッファをバインドすることができたら、 bufferData メソッドを使ってバッファにデータをセットします。
そこまでの一連の流れを一つの関数にすると、以下のようになるでしょう。
VBO を生成する関数
code:js
function create_vbo(data){
// バッファオブジェクトの生成
var vbo = gl.createBuffer();
// バッファをバインドする
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
// バッファにデータをセット
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
// バッファのバインドを無効化
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// 生成した VBO を返して終了
return vbo;
}
この関数は、引数に配列を受け取り VBO を生成して返します。
バッファをバインドするには bindBuffer メソッドを使います。
このメソッドは引数を二つ取り、
第一引数にバッファの種類を表す定数を、
第二引数にバッファオブジェクトを指定します。
gl.ARRAY_BUFFER という組み込み定数を第一引数に指定することで VBO が生成できます。
bufferData メソッドの第二引数に出てきている Float32Array オブジェクト
javascript の型付配列で、
一般的な Array オブジェクトと似ていますが
浮動小数点数を扱うことを原則とした型を持つ配列オブジェクトです。
また、第三引数に出てくる gl.STATIC_DRAW という組み込み定数は、
そのバッファがどのような頻度で内容を更新されるのか定義します。
VBO の場合には、基本的にモデルデータはそのままで何度も利用することになるため、この定数を指定します。
WebGL にバインドできるバッファは、一度に一つだけです。
ですから、他のバッファに操作を加えたい場合には、適宜バッファをバインドし直す必要があります。
そういった事情があるため、
この関数の最後部分では bindBuffer メソッドを再度呼び出し、第二引数に null を与えることによって一度バインドしたバッファを無効化しています。