Tuesday, March 29, 2016

OpenGLでDirectX風に頂点レイアウトを書く方法

以前の記事で、OpenGLの頂点レイアウトやInput Layout Qualifiersの事を書きましたが、もうちょっといい方法を思いついたので書きます。以下のようにDirectX11のように頂点レイアウトを構造体の配列として書きます。

この方法のキモは、elementsの配列の並び順にglBindAttribLocationで0から順に番号を割り当てることです。この例では、"POSITION"が0番、"COLOR"が1番、"TEXCOORD"が2番です。例えば以下のような頂点シェーダのための定義になります。inのところだけ見てください。glBindAttribLocationを使うので当然Input Layout Qualifiersは不要です。

この過程を同じ頂点レイアウトを使用する複数のシェーダーに適用することで、シェーダが変わっても同じVAO(Vertex Array Object)を使用できます。言い換えると、シェーダーからglGetAttribLocationで"POSITION"は何番か、と問い合わせる方法でVAOを生成してしまうとそのシェーダ専用のVAOしか作れなくなってしまう為、InputElementという仲介役を立てたと言えます。

以上を踏まえたVAOを生成する実装の実例を紹介します。長いですがやってることはInputElementの配列を解釈しながらVAOを作っているだけです。こうして作ったVAOは同じInputElement配列を指定して作った頂点シェーダならどれにでもバインドできるようになります。

afCreateVAOは、InputElement構造体の配列、インデックスバッファ、頂点バッファの配列(それぞれ順にInputElement構造体のslotに対応、OpenGLにslotという概念は無いが、DirectX風に定義)、strideの配列を受け取ってVAOを作ります。

ほぼ同じ引数を受け取るafSetVertexAttributesは、頂点バッファを現在のVAOにバインドする部分です。VAOの無いOpenGL ES 2.0でもそのまま使えるコードです。

こうすることでInput Layout Qualifiersを使う必要もなくなり、glGetAttribLocationも必要なくなり、OpenGL ES 2.0、OpenGL ES 3.0双方で同じコードが動き、DirectXとのコードの共通化にも役立ちます。