今までほとんど確認用のレンダリングのためだけにしか使ってなかったので、OpenGL 2.1(GLSL 1.20)のままでやってきたけど、GPGPU的に使うようになったのでOpenGL 3.2で実行するように変更した。
やっぱ少しでも柔軟性は高いほうがいいだろうし、今のところ全くアイデアはないけどジオメトリシェーダでなにか出来るかもしれないし。
OpenGLのバージョンの変更に伴い、コードの修正が必要になった。
元々シェーダ全開で固定機能の関数は使ってなかったので楽に対応できたと思う。
バージョン等は以下のコードで調べた。
printf("Renderer %s\n", glGetString(GL_RENDERER)); printf("Vendor %s\n", glGetString(GL_VENDOR)); printf("OpenGL version %s\n", glGetString(GL_VERSION)); printf("GLSL version %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
この実行結果は以下のとおり。
変更前が上、変更後が下。
Renderer NVIDIA GeForce 9400M OpenGL Engine Vendor NVIDIA Corporation OpenGL version 2.1 NVIDIA-8.10.44 304.10.65f03 GLSL version 1.20
Renderer NVIDIA GeForce 9400M OpenGL Engine Vendor NVIDIA Corporation OpenGL version 3.2 NVIDIA-8.10.44 304.10.65f03 GLSL version 1.50
今回行った修正は以下の4点。
glValidateProgram が失敗し、 glGetProgramInfoLog で調べると Validation Failed: No vertex array object bound. というメッセージが得られる問題。
問題というかこれは私がglValidateProgramの用途を勘違いしてただけだけど。
では、 glValidateProgram は何のための関数なのか。
http://www.opengl.org/sdk/docs/man3/xhtml/glValidateProgram.xmlを読むと解るとおり、現在のOpenGLステートでシェーダプログラムが実行可能か(実行可能であっても非効率ではないか等も)を調べる関数。
つまり、これはシェーダの生成フェーズで使うものではなく、シェーダの実行前、つまり描画コマンドの実行前に呼ぶべきものである。
開発中には便利に使える関数だが、描画ループごとシェーダごとに毎回呼び出してたら効率が悪いので、問題ないということが確認できたら何か事情が無い限り最終的には呼び出さないようにすべきだろう。
これを勘違いしてシェーダのリンク直後に行っていた。
OpenGL 3.2ではVAOが必須になったが、シェーダの初期化段階、つまりまだVAOのバインドをしてない段階で実行していたため、エラーが起こるようになったみたい。
シェーダの初期化段階では glValidateProgram を行わないようにした。
あと、OpenGL 3.2で描画にVAOが必須になったと書いたけど厳密には違うらしい。
誰かがそう書いていたのをチラッと見ただけで、どこに書いてあったか忘れてしまったので、真偽の程は分からない。
必須なのはAppleのOpenGLの実装だけの話かもしれない。
すでにglGenVertexArraysAPPLEとかを使ってたので、基本的にはそれらをglGenVertexArraysとかに書き換えただけ。
GLSLのバーションが1.20から1.50に変わったのでそのままではシェーダがコンパイル出来なくなった。
ただ、機械的に変更するだけなのでたいして大変ではなかった。
変更した内容は次のような感じ。
フラグメントシェーダに out vec4 fragColor0; とか宣言したけど、これだけではダメで、どの変数が何番目の出力先に対応するのか、OpenGLの関数で教えてあげないといけないみたい。
(この仕様って出力先の変数名を自由にできる以外の利点って何かあるのかな?)
とりあえず以下のように固定的に設定するようにした。
これをシェーダプログラムのリンク直後に行う。
glBindFragDataLocation(program, 0, "fragColor0"); glBindFragDataLocation(program, 1, "fragColor1"); glBindFragDataLocation(program, 2, "fragColor2"); glBindFragDataLocation(program, 3, "fragColor3");
うちの環境でしか確認してないけど、シェーダにfragColor0しか書いてなくても、つまり存在しない変数に対してglBindFragDataLocationを呼んでも、特に動作がおかしくなったりすることは無いみたい。
以前のように固定的にしたいってときに、これでいちいち問題が出てたら面倒だもんね。
コメント