「じゃあES2.0だけでいいや」では時代遅れになるので、ES2.0でもモダンなフリをしながら両バージョンでコードを共有する方法を模索してみます。
複数のuniform変数を配列化してひとつに
http://on-demand.gputechconf.com/gtc/2013/presentations/S3032-Advanced-Scenegraph-Rendering-Pipeline.pdf の24ページ目でuniform block(UBO)を使えない場合に、複数のuniformを配列にまとめて使う方法が提案されています。これは効率的であるのみならず、C側では構造体で転送するデータを管理できるので、UBOを使ったES 3.0のコードとのアーキテクチャの差を吸収できそうです。
https://devtalk.nvidia.com/default/topic/777618/scenix/announcing-nvpro-pipeline-a-research-rendering-pipeline/ この掲示板に動画もありますね。
mat4かvec4かfloatか
複数のuniformをまとめる際、どのような型の配列として渡すか決めなければなりません。行列だけのバッファならmat4の配列、色やベクトルだけならvec4配列にします。
mat4とvec4の混合のバッファを作りたい場合もあるかもしれません。そういうときはvec4の配列とするとよさそうです。mat4配列でもよいのですがバッファサイズがfloat16個単位になってしまうので柔軟性に欠けるからです。次のようにしてGLSL側でvec4を4つまとめてmat4を復元します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
uniform vec4 uniformBuffer[9]; | |
void main() { | |
mat4 matV = mat4(uniformBuffer[0], uniformBuffer[1], uniformBuffer[2], uniformBuffer[3]); | |
mat4 matP = mat4(uniformBuffer[4], uniformBuffer[5], uniformBuffer[6], uniformBuffer[7]); | |
float etc1 = uniformBuffer[8].x; | |
float etc2 = uniformBuffer[8].y; | |
float etc3 = uniformBuffer[8].z; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct UniformBuffer | |
{ | |
Mat matP, matV; | |
float etc1, etc2, etc3, padding; | |
}; | |
static UniformBuffer buf; | |
void Write() | |
{ | |
glUniform4fv(glGetUniformLocation(shaderId, "uniformBuffer"), sizeof(buf) / (sizeof(GLfloat) * 4), (GLfloat*)&buf); | |
} |
ならば、vec4配列よりfloat配列のほうが柔軟そうですが、float4つを1単位とする癖をつけたほうが良いかもしれません。以下のポストはDirectX11のもので、NVIDIAによるものなので他のGPU全てに当てはまるかはわかりませんが、float4つ単位にしなかった場合の速度低下の可能性を示唆しています。
https://developer.nvidia.com/content/understanding-structured-buffer-performance
UBOとuniform vec4配列の違い
上のように、ES3.0のUBOのようなものをES 2.0で実現してみましたが、同一ではありません。UBOはシェーダから独立したバッファで、複数シェーダで共有可能です。uniformはシェーダの一部であってC側から書き換え可能な変数です。あるuniform vec4配列を複数のシェーダ間で共有したい場合、シェーダを切り替える度にglUniform4fvでバッファを転送してあげなければなりません。
OpenGL ES特有のprecisionの問題
ES特有のつまづきそうなポイントです。uniformはシェーダ間で共有できませんが、同じprogramに属するvertex shaderとfragment shaderに限って同じ名前のuniform変数を宣言すると共有されます。しかし、precisionが違うと共有できません(glLinkProgramでエラーになります)。vertex shaderではhighp、fragment shaderではmediump、のような使い方をすることが多いと思うので注意します。(fragment shaderでhighpがエラーになる機種もある)
No comments:
Post a Comment