3

私は(iOSで)GLSLを試していますが、2つの円(、、、)の色の値とパラメーターを受け取る単純なシェーダーを作成しcenterましradiusedgeSmoothing。画面全体に単一のクワッドを使用して描画され、シェーダーはgl_FragCoord各ポイントが円の内側にあるか外側にあるかを使用して決定します-円の内側に1.0のアルファを計算し、外側に0.0までスムーズにシェーディングしradius + edgeSmoothing、ミラーを適用します-クランプをアルファ(偶数奇数のフィルルール効果を得るための三角波)にスタイル設定し、を設定しますgl_FragColor = mix(vec4(0.0), color, alpha);

glUniformこれは問題なく機能しますが、5つの異なる色で10個の円が必要なので、すべてのシェーダーユニフォームを呼び出しglDrawElements、クワッドを5回別々に描画します(異なる色と円のパラメーターを使用)。ブレンドモードは加法であるため、異なる色が追加されます。私が望むパターンを与えるためにうまくアップしてください、完璧です!

これは実験であるため、円を描くだけでなく、GLとGLSLについても学ぼうとしています。

centers[10]これで、クワッドを1回だけ描​​画し、10個の円すべてのパラメーターを均一な配列( 、radii[10]など)に渡して、GLSLでループし、シェーダーで生成される色を合計する方がはるかに効率的だと思います。 。そこで、このシェーダーを作成し、コードをリファクタリングして、すべての円パラメーターを一度に渡します。正しい結果が得られます(出力はまったく同じに見えます)が、フレームレートが15fpsから約3fpsに低下します-5倍遅くなります!!

シェーダーコードにループが追加されましたが、同じ計算を使用して、円の各ペアのアルファ値を計算します。なぜこれほど遅いのですか?確かに、画面全体を5回塗りつぶし、GLが5回加法ブレンド(つまり、ピクセル値の読み取り、ブレンド、書き戻し)を行うよりも少ない作業を行っていますか?今、私は蓄積された色を計算し、画面全体を一度だけ塗りつぶしていますか?

最適化だと思ったことが逆の効果をもたらした理由を誰かが説明できますか?

更新:このコードをShaderToyに貼り付けて、私が話していることを確認してください。

#ifdef GL_ES
precision highp float;
#endif

uniform float time;

void main(void)
{
    float r, d2, a0, a1, a2;
    vec2 pos, mid, offset;
    vec4 bg, fg;

    bg = vec4(.20, .20, .40, 1.0);
    fg = vec4(.90, .50, .10, 1.0);
    mid = vec2(256.0, 192.0);

    // Circle 0
    pos = gl_FragCoord.xy - mid;
    d2 = dot(pos, pos);
    r = 160.0;
    a0 = smoothstep(r * r, (r + 1.0) * (r + 1.0), d2);

    // Circle 1
    offset = vec2(110.0 * sin(iGlobalTime*0.8), 110.0 * cos(iGlobalTime));
    pos = gl_FragCoord.xy - mid + offset;
    d2 = dot(pos, pos);
    r = 80.0;
    a1 = smoothstep(r * r, (r + 1.0) * (r + 1.0), d2);

    // Circle 2
    offset = vec2(100.0 * sin(iGlobalTime*1.1), -100.0 * cos(iGlobalTime*0.7));
    pos = gl_FragCoord.xy - mid + offset;
    d2 = dot(pos, pos);
    r = 80.0;
    a2 = smoothstep(r * r, (r + 1.0) * (r + 1.0), d2);

    // Calculate the final alpha
    float a = a0 + a1 + a2;
    a = abs(mod(a, 2.0) - 1.0);

    gl_FragColor = mix(bg, fg, a);
}
4

1 に答える 1

3

フラグメントシェーダーの操作の複雑さが増すと、レンダリング時間に非線形の影響を与える可能性があります。単純に見える分岐操作を1つ追加するだけでも、シェーダーの速度が10倍遅くなる場合があります。

特にループはiOSデバイスのフラグメントシェーダー内でひどいので、絶対に避けたいと思います。そのループを展開して、均一な値に対する一連のチェックを行うと、パフォーマンスが向上するはずです。

ただし、ユニフォームに対して10回のチェックを実行すると、ステップまたはスムーズステップが含まれるように聞こえますが、フレームバッファ内のすべてのピクセルに適用すると、非常にコストがかかります。また、画面の大部分が特定の円で覆われないため、かなり無駄になります。

個別のglDrawElements()呼び出しを使用して個々の円を描画したり、画面サイズの四角形を描画したりする必要はありません。この回答の中で、オープンソースアプリケーションで球体の詐欺師を描画するために使用するプロセスについて説明します。この回答では、最新のiOSデバイスで60 FPSで画面上に数千の円(球体)を描画できます。そのために、私は各円のクワッドを渡します。これは、その円を含み、それより大きくないだけの大きさです。これらのクワッドはすべて配列にまとめられ、一度に描画されます。各円の追加パラメータは、頂点データと一緒に属性として渡されます。たとえば、頂点に沿って(-1、-1)から(1、1)までの詐欺師の空間座標を使用し、簡単な計算を行って点が円内にあるかどうかを判断するため、半径を指定する必要はありません。

各円に必要なフラグメントのみを描画し、それ以上描画しない場合は、パイプラインのフラグメント処理部分から多くの負荷を取り除くことになります。ブレンディングモードを有効にする必要がありますが、クワッドサイズの縮小と、フラグメントシェーダーで実行される操作の簡素化を組み合わせることで、全体的なパフォーマンスが大幅に向上します。

于 2012-10-30T03:47:31.177 に答える