モデルデータと頂点属性
頂点属性の意味
前回のテキストでは、シェーダの作成からコンパイル、またプログラムオブジェクトの生成とリンクまでを解説しました。今回は、モデルデータを定義する最も簡単な方法の解説と、頂点属性について取り扱いたいと思います。また、モデルデータをから VBO を生成する方法も併せて解説します。
VBO は、実際のところ生成するところよりも生成してからのほうが難解だったりするのですが、そこは追々説明していきますのでご心配なく。
さて、それではまず頂点属性について見ていきましょう。
頂点属性とは、わかりやすく表現するなら頂点が持つ様々な要素のことだと言えます。WebGL の場合は最低限、頂点が位置情報という属性を持っている必要があります。これは以前にも何度か説明しましたね。
頂点とは三次元空間上に存在する任意の一点を表すわけですから、絶対に位置情報は必要ですね。位置情報が備わっていない頂点では、その任意の一点が三次元空間のどこに存在するのか定義することができません。点が点であるために、位置情報が必要なわけです。
しかし、頂点にはそれ以外の属性を持たせることも可能です。代表的な頂点属性を挙げるとすれば、たとえば頂点色がそれにあたるでしょう。頂点自体が色という属性を持っていることによって、ポリゴンに着色したり、あるいは透明度を指定して半透明処理を行なったり、様々なことができるようになります。
その他にも、たとえば頂点の持つ法線の情報であったり、あるいはテクスチャ座標の情報であったり、それこそ頂点属性はプログラマが自由にそれを定義することが可能です。DirectX の場合には、いわゆる頂点フォーマットと呼ばれる仕組みによってこれを実現しますが、WebGL の場合には頂点属性という形で頂点に様々な特徴を持たせることが可能なのですね。
頂点属性と VBO
頂点属性を自由に定義できるとは言っても、それは具体的にどういう手順で実現できるのでしょうか。
最初から順番にテキストを読んできている人ならわかると思いますが、頂点属性の数と、生成する VBO の数は、普通、イコールになります。頂点に三つの属性を持たせようとするなら、VBO も三つ必要になります。これは、頂点属性が VBO という形で頂点シェーダに渡されるからに他なりません。VBO は別名[ 頂点バッファ ]とも呼ばれます。その名が示すとおり、頂点に関する情報を保持するためのバッファが VBO です。頂点属性一つにつき、一つの VBO が割り当てられ、頂点シェーダに通知されるわけですね。
VBO を生成するためには、まず先に頂点の個数に応じた配列を用意します。これは、いわゆる普通の javascript の配列です。もちろん、Array オブジェクトでも構いません。ただし、ハッシュを用いた連想配列ではなく、普通の一次元配列を使います。
たとえば、三つの頂点からなる一枚のポリゴンを定義するときの例が次のようになります。
モデルデータを格納する配列の例
var vertex_position = [
// X, Y, Z
0.0, 1.0, 0.0,
1.0, 0.0, 0.0,
-1.0, 0.0, 0.0
];
わかりやすくするために途中で改行していますが、カンマや大括弧の数を見ればわかるとおり、これは単なる一次元配列になっています。要素の数は見ての通り 9 個になっています。三つの頂点が、それぞれに X ・ Y ・ Z の座標情報を持っているため、頂点の数×要素の数で 3 x 3 = 9 という配列が出来上がるわけですね。
VBO の生成
単純な配列による頂点データが準備できたら、その配列を使って VBO を生成することが可能になります。VBO を生成するには、WebGL のメソッドである createBuffer
を使います。 createBuffer
メソッドはその名の通りバッファを生成するメソッドです。つまり、このメソッドは直接 VBO そのものを生成するというわけではありません。このメソッドによって生成されるのはあくまでもバッファオブジェクトであって、そのバッファにどんな情報を格納したのかによって、用途が変わってくるということですね。
バッファを操作する場合には、まず最初にバッファを WebGL にバインドする必要があります。言うなれば、バッファという名のディスクにデータを書き込むために、WebGL という名のドライブにセットするようなイメージですね。
バッファをバインドすることができたら、 bufferData
メソッドを使ってバッファにデータをセットします。そこまでの一連の流れを一つの関数にすると、以下のようになるでしょう。
VBO を生成する関数
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 を生成して返します。まず createBuffer
でバッファオブジェクトを生成したあと、バッファをバインドしてからデータをセットしています。
バッファをバインドするには bindBuffer
メソッドを使います。このメソッドは引数を二つ取り、第一引数にバッファの種類を表す定数を、第二引数にバッファオブジェクトを指定します。 gl.ARRAY_BUFFER
という組み込み定数を第一引数に指定することで VBO が生成できます。
ちなみに bufferData
メソッドの第二引数に出てきている Float32Array
オブジェクトは javascript の型付配列で、一般的な Array オブジェクトと似ていますが浮動小数点数を扱うことを原則とした型を持つ配列オブジェクトです。3D の世界では小数点以下の数値の精度が非常に大切になるので、型付の配列としてデータを渡すわけですね。また、第三引数に出てくる gl.STATIC_DRAW
という組み込み定数は、そのバッファがどのような頻度で内容を更新されるのか定義します。VBO の場合には、基本的にモデルデータはそのままで何度も利用することになるため、この定数を指定します。
WebGL にバインドできるバッファは、一度に一つだけです。ですから、他のバッファに操作を加えたい場合には、適宜バッファをバインドし直す必要があります。そういった事情があるため、この関数の最後部分では bindBuffer
メソッドを再度呼び出し、第二引数に null
を与えることによって一度バインドしたバッファを無効化しています。こうすることで WebGL にバッファがバインドされたままになって予期せぬエラーが発生しないように予防線を張っているわけですね。
まとめ
頂点にはプログラマが自由に属性を付加できます。そして、付加した属性の数だけ VBO が必要になるのでしたね。
頂点属性の各データは、まず純粋な一次元の配列データとして用意します。当然ながら、描画したい頂点の数分だけ頂点データを定義する必要があります。
VBO を生成する際には、まず最初にバッファを WebGL にバインドします。そして適切なデータを、適切なデータ型に変換した上で、組み込み定数を指定してバッファにセットします。予期せぬエラーを回避するためにも、データのセットが完了したら WebGL へのバインドを解除し無効化しておくのでしたね。
ここまでの一連の処理を経てモデルデータは頂点シェーダで利用できる形になります。次回以降、VBO をシェーダに渡すための手順などを解説することになると思いますが、まずは VBO をどのようにして準備するのか、その基本をしっかり理解しておきましょう。
さて、次回は座標変換行列の用意について解説します。