過去20年に渡り、同僚が書いたエンジンコアを使ったり、Unityのような出来合いのエンジンを使ったり、時には自分でエンジンコアを書いたりしてゲームを作ってきたが、その辺りの知見を踏まえて自分が使うゲームエンジンを作ってみようと10月半ばぐらいからちまちまと自宅でコードを書いている。いわば「ぼくのかんがえたさいきょうのゲームエンジン」を作りたいわけだ。
詳細はまあそのうちどこかで言及することにはなると思うのだけども、描画まわりはどのような形であれ必要となるため避けられない。他所様の作ったライブラリを利用する手もあったが、今回はGLES2*1を使って自前で3D描画系を実装することにした。
理由は「やってみたかったから」。
過去の仕事でもだいたいのケースにおいて描画周りには担当のプログラマがいて、俺はそれを利用するだけの立場だったわけで、俺が書くチャンスはなかなか無かったのだ。Unityの仕事とかだとさらに顕著で「Unityが既に用意してくれている」から、自分でその部分を書けないのがつまらないのだ。俺はこれまでずっと、まさにそこをこそ書きたかったのだ。
…てな感じの前置きで始まったが、幸いにしてベクトル、行列、内積、外積、クォータニオン等、3Dの取り扱いに必要な数学やその応用例についての知識は持ち合わせている(使う側の立場でも理解しておく必要はあるため当然)ので、Scenegraph の構築から基礎的なレンダリング、シェーディング、テクスチャマッピングあたりまではすんなり進行している。スペキュラとかその辺もまぁ理論はおおむね理解しているし、ゲームに必要なコリジョン等についてもさほど難しいものではないので、理論面でつまづくことは無いだろう。
しかし、そのために用いる道具の挙動でつまづくことはあるわけだ。現在スキニングを実装しているわけだが、glVertexAttribPointer() で指定したvertex buffer中のbone index情報が vertex shader に渡らず、index = 0 の matrix のみが有効になってしまう問題に直面している。
こんな形で頂点を持ち…
struct VEC3 { float x; float y; float z; }; struct UV { float u; float v; }; struct VERTEX { VEC3 vert; // 座標(4*3 = 12byte) VEC3 norm; // 法線(4x3 = 12byte) UV uv; // テクスチャUV値(8byte) u8 bone[4]; // 参照ボーンのindex u8 wght[4]; // 各ボーンの参照ウェイト u8 rgba[4]; // 頂点カラー(4byte) };
この頂点の配列を glBindBuffer() + glBufferData() で転送して使うのだけども、結果としてInterleavingな配列になるため、glVertexAttribPointer() で各情報のoffsetとstrideを指定してから描画することになる。
glVertexAttribPointer(m_env.m_a_vert, 3, GL_FLOAT, GL_FALSE, sizeof(VERTEX), (const GLvoid *)(char *)offsetof(VERTEX, vert)); glVertexAttribPointer(m_env.m_a_norm, 3, GL_FLOAT, GL_FALSE, sizeof(VERTEX), (const GLvoid *)(char *)offsetof(VERTEX, norm)); glVertexAttribPointer(m_env.m_a_uv, 2, GL_FLOAT, GL_FALSE, sizeof(VERTEX), (const GLvoid *)(char *)offsetof(VERTEX, uv)); glVertexAttribPointer(m_env.m_a_bone, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(VERTEX), (const GLvoid *)(char *)offsetof(VERTEX, bone)); glVertexAttribPointer(m_env.m_a_wght, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VERTEX), (const GLvoid *)(char *)offsetof(VERTEX, wght)); glVertexAttribPointer(m_env.m_a_rgba, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(VERTEX), (const GLvoid *)(char *)offsetof(VERTEX, rgba));
こんな感じに。
で、今起こっている問題は、この中の bone が vertex shader 側で受け取れていない、というもの。 ほぼ似た条件で転送している wght や rgba は転送されていることが確認できているのだけども…
なんでやねんw 、ということで現在調査中。
ちなみに Windows上の ANGLE と、Nexus7(2012)上のAndroid4.4.4で同様に起こっているため、特定の GLES2 実装に起因する問題ではないと思われる。