OpenGL中年泣かせの落とし穴集その1

やあ子供たち。最近おじさんはまた性懲りもなくSurfacePro3でOpenGLの勉強を夜な夜なしているわけだけれども、いちいち壁にぶつかっているよ。そのたびに
「やっぱSurfacePro3のスペックじゃだめかー、勉強に使えないのならもう売ろうかな」なんて思い込んで、本当に買い取り価格を調べそうなところまで行ったりして実に危ないところだった!なんていうことがよくある今日この頃。
それでも粘り強く格闘していると結局は
「SurfacePro3のせいじゃなくて自分が新しいOpenGL使えてねかっただけでした」みたいなことになった事例を今日は二つほどメモしておくぞ。
●glUniform*()はglUseProgram()の後ろに書け!
これは「うえーん、シェーダープログラムがうまく切り替わらないよー」と思い込んだところから始まった。現象としては、モデル①をシェーダーAで描画し、モデル②をシェーダーBで描画して、というのをやろうとしただけなのだが、これがいきなりうまく行かない。マルチシェーダー(multi shader)にチャレンジ!の基本みたいなコードが動かない!モデル①の描画を終えてモデル②の描画をした途端に、モデル①の描画が消えたりゴミ描画になったりする。シェーダーを使い分けず、どちらか片方のシェーダーで両モデルを描画すれば問題はないのだが、それぞれでシェーダーを使い分けようとすると、どういうわけかうまく行かない。
新しいOpenGLの素人であるおじさんはここで、「シェーダーの切替がうまく行かない」という思い込みだけで2日以上悩んでしまったというわけさ。でも問題はちょっと違うところにあった。モデル①と②の描画をおじさんがどう実装していたかというと、それは以下のような感じ。

// モデル①を描画
glUniformMatrix4fv() で投影行列、モデルビュー行列を「シェーダーAに(のつもりで)」セット
glUseProgram("シェーダーA")呼び出し
glBindVertexArray("モデル①描画情報")呼び出し
glDrawArrays("モデル①描画頂点バッファ")呼び出し
// モデル②を描画
glUniformMatrix4fv() で投影行列、モデルビュー行列を「シェーダーBに(のつもりで)」セット
glUseProgram("シェーダーB")呼び出し
glBindVertexArray("モデル②描画情報")呼び出し
glDrawArrays("モデル②描画頂点バッファ")呼び出し

そう。これではとてもまずいというかダメなのである。
何故かというと、glUniformMatrix4fv()は、シェーダープログラムを指定する引数を持たないことに注意しよう。そう、こいつは、今現在UseProgram()されているシェーダープログラムを対象にしているのだ。
なので、上記例だと、2回目のglUniformMatrix4fv()呼び出しでは、シェーダーBではなくむしろシェーダーAのそれを書き換えていることになってしまうのだ!書き換える先の位置もシェーダーBのつもりでシェーダーAの中に書きに行ってしまうので、それはもうプログラムやデータもめちゃめちゃにこわれてしまうというわけ。
正しくはこうなる。(コメント内「のつもりで」が消えていることに気付いてくれ

// モデル①を描画
glUseProgram("シェーダーA")呼び出し
glUniformMatrix4fv() で投影行列、モデルビュー行列をシェーダーAにセット
glBindVertexArray("モデル①描画情報")呼び出し
glDrawArrays("モデル②描画頂点バッファ")呼び出し
// モデル②を描画
glUseProgram("シェーダーB")呼び出し
glUniformMatrix4fv() で投影行列、モデルビュー行列をシェーダーBにセット
glBindVertexArray("モデル②描画情報")呼び出し
glDrawArrays("モデル②描画頂点バッファ")呼び出し

はいそうだね、とにかくglUseProgram()を先に呼ぶことが大事ということのようだ。
●シェーダーにてGLSLのバージョンを指定せよ!
上述のプログラムでは、それぞれのシェーダに同じ、投影行列と、モデルビュー行列をいちいちセットしているわけだが、UniformBufferObjectというものを使えば、値の変更は一括ですむということなので、トライしてみた。そこで、やれやれUniformBufferObjectか勉強しなきゃならんことがいっぱいあるなという感じで以下UniformBlockを定義してみたところ、

layout(std140) uniform Matrices
{
	mat4 prjMatrix;
	mat4 mdvMatrix;
};

こんなシェーダのコンパイルエラーが出たよ。

compiler_log = 0x000000afcab27890 "WARNING: 0:21: 'GL_ARB_explicit_attrib_location' :  extension is not available in current GLSL version
WARNING: 0:21: 'std140' : symbol not available in current GLSL version 


うわー!なんじゃこりゃーww!!
これを見たとき、この内容からしてすっかりこれはもう今度こそSurfacePro3のハードの限界かなと思ってしまったのだが、私のやつはIntelHDGraphics4400を搭載しているので、OpenGL4.3までは対応してるはず、、泣き寝入りか?と思いながらも粘り強く検索をかけていくと、それはこういうものをシェーダープログラムの先頭におまじないのように入れておくと問題なくエラーが出なくなった。

#version 330 
#extension GL_ARB_explicit_attrib_location : require 

そう、いろんなシェーダのサンプルの冒頭に出てくるこいつらは、地味にそこに置かれていて何の解説もされていないことが多い気がするのだが、#(シャープ)から始まっているとはいえ、「こいつらはコメントではない」らしいということを覚えておくと、いいことたくさんありそうだぜ!
というわけで今日はこの辺でおさらばだ。
チャオ!