私はいくつかのFPS測定コードをWebGLでセットアップしました(このSO回答に基づいて)、フラグメントシェーダーのパフォーマンスに奇妙な点を発見しました。このコードは、1024x1024のキャンバス上に単一のクワッド(または2つの三角形)をレンダリングするだけなので、すべての魔法はフラグメントシェーダーで発生します。
このシンプルなシェーダー(GLSL。頂点シェーダーは単なるパススルーです)を検討してください。
// some definitions
void main() {
float seed = uSeed;
float x = vPos.x;
float y = vPos.y;
float value = 1.0;
// Nothing to see here...
gl_FragColor = vec4(value, value, value, 1.0);
}
したがって、これは白いキャンバスをレンダリングするだけです。私のマシンでは平均で約30 fpsです。
それでは、数値演算を増やし、数オクターブの位置依存ノイズに基づいて各フラグメントを計算しましょう。
void main() {
float seed = uSeed;
float x = vPos.x;
float y = vPos.y;
float value = 1.0;
float noise;
for ( int j=0; j<10; ++j)
{
noise = 0.0;
for ( int i=4; i>0; i-- )
{
float oct = pow(2.0,float(i));
noise += snoise(vec2(mod(seed,13.0)+x*oct,mod(seed*seed,11.0)+y*oct))/oct*4.0;
}
}
value = noise/2.0+0.5;
gl_FragColor = vec4(value, value, value, 1.0);
}
上記のコードを実行する場合は、この実装をsnoise
使用しています。
これにより、fpsが7のようになります。これは理にかなっています。
奇妙な部分...ノイズ計算を次の条件でラップすることにより、16個のフラグメントごとに1つだけをノイズとして計算し、他のフラグメントを白のままにしてみましょう。
if (int(mod(x*512.0,4.0)) == 0 && int(mod(y*512.0,4.0)) == 0)) {
// same noise computation
}
これははるかに高速であると予想されますが、それでもまだわずか7 fpsです。
もう1つのテストでは、代わりに、次の条件でピクセルをフィルターします。
if (x > 0.5 && y > 0.5) {
// same noise computation
}
これにより、以前とまったく同じ数のノイズピクセルが得られますが、現在ではほぼ30 fpsに戻っています。
ここで何が起こっていますか?16番目のピクセルを除外する2つの方法で、まったく同じサイクル数が得られないのですか?そして、なぜすべてのピクセルをノイズとしてレンダリングするほど遅いのはなぜですか?
ボーナス質問:これについて私は何ができますか?私が実際に行う場合、恐ろしいパフォーマンスを回避する方法はありますかいくつかの高価なフラグメントだけでキャンバスに斑点をたいか?
(確かに、16ピクセルごとに白ではなく黒をレンダリングすることにより、実際のモジュロ計算がフレームレートにまったく影響しないことを確認しました。)