技術

OpenGL 3.2(GLSL 1.50)対応

今までほとんど確認用のレンダリングのためだけにしか使ってなかったので、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点。

  1. glValidateProgramの問題の修正
  2. Vertex Array Object(VAO)対応
  3. シェーダの書き換え
  4. フラグメントシェーダの出力先の設定

1. glValidateProgramの問題の修正

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の実装だけの話かもしれない。

2. Vertex Array Object(VAO)対応

すでにglGenVertexArraysAPPLEとかを使ってたので、基本的にはそれらをglGenVertexArraysとかに書き換えただけ。

3. シェーダの書き換え

GLSLのバーションが1.20から1.50に変わったのでそのままではシェーダがコンパイル出来なくなった。
ただ、機械的に変更するだけなのでたいして大変ではなかった。
変更した内容は次のような感じ。

  • シェーダの1行目に#version 150を追加。
  • 頂点シェーダのattributeをin、varyingをoutに、フラグメントシェーダのvaryingをinに書き換え。
  • フラグメントシェーダでgl_FragColorやgl_FragDataが使えなくなったため、出力先としてout vec4 fragColor0;みたいな宣言を追加してそこに書き込むようにした。
  • texture2D関数が無くなったのでtexture関数に変更。
  • それに合わせtextureという変数名を使ってた部分を変更。

4. フラグメントシェーダの出力先の設定

フラグメントシェーダに out vec4 fragColor0; とか宣言したけど、これだけではダメで、どの変数が何番目の出力先に対応するのか、OpenGLの関数で教えてあげないといけないみたい。
(この仕様って出力先の変数名を自由にできる以外の利点って何かあるのかな?)
とりあえず以下のように固定的に設定するようにした。
これをシェーダプログラムのリンク直後に行う。

glBindFragDataLocation(program, 0, "fragColor0");
glBindFragDataLocation(program, 1, "fragColor1");
glBindFragDataLocation(program, 2, "fragColor2");
glBindFragDataLocation(program, 3, "fragColor3");

うちの環境でしか確認してないけど、シェーダにfragColor0しか書いてなくても、つまり存在しない変数に対してglBindFragDataLocationを呼んでも、特に動作がおかしくなったりすることは無いみたい。
以前のように固定的にしたいってときに、これでいちいち問題が出てたら面倒だもんね。

コメント

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください