VAOでテキスチャーも貼ってみた

続いて、vertex arrayにテキスチャーの頂点座標も含めてみる。

・ソースコード
polygon_sphere_test3.c

・コンパイル方法
前のエントリー参照

・実行画面

例によって、ドラッグで回転する。

前回は頂点座標の配列と法線ベクトルの配列を別にして、それぞれのVBOを作るような形にした(法線ベクトルは不要になったのでコメントアウトした)が、今回は頂点座標と法線ベクトルとテキスチャー座標を1つの配列にまとめて、VBOも1つにした。つまり、

struct {
 GLfloat vertex[3];
 GLfloat normal[3];
 GLfloat texCoord[2];
};
こういう型の配列である。こうすると、それぞれを別の配列にするのと比べ、少なくとも1つの頂点の処理中は頂点座標も法線ベクトルもテキスチャー座標も同じキャッシュラインに乗る確率が上がるので、CPUやGPUのメモリキャッシュの効率が上がると言われている。


ただ、デメリットとして、コードが若干複雑になる。特にglXxxPointer()の呼び出しがスマートに書きにくい。配列を別々にすれば

glVertexPointer(3, GL_FLOAT, 0, 0);
glNormalPointer(GL_FLOAT, 0, 0);
glTexCoordPointer(2, GL_FLOAT, 0, 0);
こう書けるのに対して、データ種類毎に次の要素までのバイト数と先頭の要素までのバイト数の指定が必要になるので、
glVertexPointer(3, GL_FLOAT, sizeof(GLfloat)*8, 0);
glNormalPointer(GL_FLOAT, sizeof(GLfloat)*8, (void *)sizeof(GLfloat)*3);
glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat)*8, (void *)sizeof(GLfloat)*5);
という感じのコードを書く必要がある。特に直値を含む最後の引数の記述は一目ではわかりづらいので、何とか説明的な記述にしたいが、これがなかなか難しい。gccだと__builtin_offsetofという構造体のメンバ変数のメモリ上の位置が取れるマクロがあるので
typedef struct {...} myVertexAttrib;
#define myVertexAttribPointer(MEMBER) (void *)__builtin_offsetof(myVertexAttrib, MEMBER)

glVertexPointer(3, GL_FLOAT, sizeof(myVertexAttrib), myVertexAttribPointer(vertex));
glNormalPointer(GL_FLOAT, sizeof(myVertexAttrib), myVertexAttribPointer(normal));
glTexCoordPointer(2, GL_FLOAT, sizeof(myVertexAttrib), myVertexAttribPointer(texCoord));

と書けるが、それ以外の方法は思い付かなかった。巷のコード例に"(void *)sizeof(GLfloat)*3"のような書き方が多いことにも納得した。
正直、この理由だけで、よっぽどチューンアップしたい場合以外は、頂点配列を1つにまとめるのはやりたくないと思った。