Pocket

OpenGL ESのシェーダとは、グラフィックスを操作するためのC言語ベースのプログラムです。例えば、cyberagent製のGPUImageのGPUImageContrastFilterのフラグメントシェーダは、次のようなものです。

この中の5行目、uniform lowp float contrast; がuniform変数です。uniform変数は、値を動的に変更できます。

まず、GPUImageFilterのonInit()で、変数名を指定して、uniform変数のロケーション番号を取得します。見つからなければ、-1、見つかれば、0以上の数値が返ってきます。

次に、UIでスライダーなどが操作されたタイミングで、uniform変数の値を更新します。

uniform変数の型に合わせて、setInteger、setFloatVec3、setFloatArrayなどのメソッドも用意されています。

さて、GPUImageFilterのサブクラスを作り、独自のシェーダを作っていたときのことです。まず、何も加工しないシェーダを用意しました。いずれ、使う予定のuniform変数data1も宣言しました。

次に、シェーダのmain()の処理を書く前に、onInit()でuniform変数data1のロケーションを取得できているかをトレースで確認しました。0か1が返ってくるだろうと。

予想に反して、data1Locationに -1 が返ってきました!変数名の長さを短くしたり、キャメルケース、スネークケースを試したり、x や contrastにしても、-1です。

そこで、GPUImageContrastFilter が動くことを確認してから、contrastの下にdata1を宣言しました。

onInit()で、contrastとdata1のuniformロケーション番号を取得しました。すると、

contrastLocationには、0が返ってきますが、data1Locationには、-1が返ってきました。

シェーダ内のuniform変数の宣言順序を入れ替えてみました。

結果は同じでした。

ここにいたって、data1は宣言だけして、値を参照していないから?ということに気づきました。

試しに、main()内の、contrastをdata1; で置き換えてみました。

すると、contrastLocationに -1、data1Locationに 0が返ってきました。

シェーダがコンパイルされるとき、コンパイラーの最適化で、参照していない変数は削除されてしまうんですね。

この記事は実際より簡略化したので、すんなり原因がわかったように見えます。実際は、7個のuniform変数を宣言し、mainでも少し処理を書いていたので、原因がわかるまで、かなりさまよいました。