インターリーブとは
インターリーブ (interleave) とは、一般的なコンピュータ用語で、データを交互に配置していくことを示します。 例えば、3D モデルの各頂点の「XY座標」「RGBカラー」をインターリーブして配列に詰めると、3 つの頂点の情報は、
のようにレイアウトされることになります。
WebGL (OpenGL) では、このようにインターリーブされた頂点属性データを扱えるようになっています。 インターリーブする主な頂点属性は、頂点座標 (XYZ)、テクスチャ座標 (UV)、頂点カラー (RGBA) などです。
ここでは、頂点座標 (XY) と頂点カラー (RGB) をインターリーブした配列を JavaScript 側の Float32Array
として作成し、それを WebGL 側のバッファオブジェクトに詰める方法を示します。
シェーダー側の実装
まずは、頂点シェーダーとフラグメントシェーダーを用意します。
頂点シェーダーでは、バッファオブジェクトから XY 座標を取り出すための attribute 変数 a_Position
と、RGB カラーを取り出すための a_Color
を定義します。
カラー値は最終的にはフラグメントシェーダー側で必要になるので、varying 変数の v_Color
を介して値をフラグメントシェーダーへ渡します。
フラグメントシェーダー側では、まず float の精度を設定しておかなければならないので、ここでは中精度 (mediump
) に設定しています。
実際に使用している変数の型は vec4
ですが、これは float
を 4 つセットにしたものなので、float
の精度だけ設定しておけば OK です。
頂点シェーダー側と同じ名前で定義した varying 変数 v_Color
からは、頂点シェーダー側で代入されたカラー値を受け取ることができます。
実際には、各頂点の中間に位置するフラグメントでは、補間されたカラー値が取り出されます(なのでこのサンプルではグラデーションになって見えます)。
あとは、この値を gl_FragColor
に代入すれば、それぞれのフラグメントに異なる色の付いた図形が描画されます。
アプリ側の実装
アプリ (CPU) 側では、まず、インターリーブされた配列データを用意します(ここではハードコーディングしていますが、複雑なモデルの場合は glTF 形式などの外部ファイルを使用します)。
ELEM_BYTES
は、配列の 1 要素当たりのバイト数で、ここでは float 型の値なので 4 になります。
この値は後ほど、WebGL の API を呼び出すときに使います。
次に、頂点情報を格納するためのバッファオブジェクトを WebGL 側に生成し、上記の配列データを転送します。 座標値とカラー値をインターリーブしているので、バッファオブジェクトは 1 つだけで済みます。
次の処理が重要で、インターリーブされた座標値とカラー値を、異なる attribute 変数(a_Position
と a_Color
)で取り出せるように設定します。
インターリーブされた情報を扱うには、gl.vertexAttributePointer()
の第5引数 (stride) と第6引数 (offset) をうまく設定する必要があります。
第5引数 (stride) では、1 頂点あたりの使用バイト数を指定します。
ここでは、1 頂点につき 5 要素 (X,Y,R,G,B) があり、それぞれ float の 4 バイト (= ELEM_BYTES) を使用するので、20 (ELEM_BYTES * 5
) を指定しています。
配列の要素数 (5) ではなく、バイト数で指定しないといけないことに注意してください。
最後の第6引数 (offset) は、取り出すべき最初の要素が、バッファー内のどこから始まるかを示すバイトオフセット値です。
座標値の場合は先頭から始まっているので 0 を指定し、カラー値の場合は、その前に配置された 2 つの座標値分だけオフセットして取り出したいので、ELEM_BYTES * 2
を指定しています。
ここで指定されたオフセット位置から、stride パラメータのバイト数ずつずらしながら情報が取り出されていくことになります。
vec4
型で取り出していることに気が付いたかもしれません。
4 要素に満たないデータを vec4
で取り出そうとすると、3 要素目に 0.0、4 要素目に 1.0 というデフォルト値が格納されます。
このデフォルト値は、vec4
変数を座標値として扱う場合も、RGBA 値として扱う場合も都合がよい値になっています。最後に、gl.drawArrays()
で描画命令を WebGL に送ってやれば、バッファーオブジェクトに渡した頂点情報を元に描画が行われます。