2017-02-14 2 views
1

次のシェーダーを作成して、同心円の束でパターンを描画しました。最終的には、回転する各球体を発光体にして、these linesに沿って何かを作りたいと思っています。WebGLシェーダを最適化しますか?

もちろん、私はちょうど異なるオブジェクトをレンダリングするための最も基本的な部分をやっています。

残念ながら、シェーダは非常に遅いです(ハイエンドのMacBookではフルスクリーン16fps)。私はこれがシェイダーで持っている多数のforループと分岐に起因すると確信しています。私は道を最適化し、より高いパフォーマンスで実現しようとしているジオメトリをやってのけることができますどのように思ったんだけど:

EDIT:あなたがここにシェーダーを実行することができます:私は行方不明ですhttps://www.shadertoy.com/view/lssyRH

1つの明白な最適化は、ということです現在、すべてのフラグメントが周囲のサークル全体24に対してチェックされています。フラグメントがダイアグラムの外側の境界と交差するかどうかをチェックするだけで、これらのチェックを完全に破棄するのはかなり簡単で簡単です。私は、ベストプラクティスがこのようなことをやっていることをどのように扱っているかを把握しようとしていると思います。

#define N 10 
#define M 5 
#define K 24 
#define M_PI 3.1415926535897932384626433832795 

void mainImage(out vec4 fragColor, in vec2 fragCoord) 
{ 
    float aspectRatio = iResolution.x/iResolution.y; 

    float h = 1.0; 
    float w = aspectRatio; 

    vec2 uv = vec2(fragCoord.x/iResolution.x * aspectRatio, fragCoord.y/iResolution.y); 

    float radius = 0.01; 
    float orbitR = 0.02; 
    float orbiterRadius = 0.005; 
    float centerRadius = 0.002; 
    float encloseR = 2.0 * orbitR; 
    float encloserRadius = 0.002; 
    float spacingX = (w/(float(N) + 1.0)); 
    float spacingY = h/(float(M) + 1.0); 
    float x = 0.0; 
    float y = 0.0; 
    vec4 totalLight = vec4(0.0, 0.0, 0.0, 1.0); 
    for (int i = 0; i < N; i++) { 
     for (int j = 0; j < M; j++) { 
      // compute the center of the diagram 
      vec2 center = vec2(spacingX * (float(i) + 1.0), spacingY * (float(j) + 1.0)); 
      x = center.x + orbitR * cos(iGlobalTime); 
      y = center.y + orbitR * sin(iGlobalTime); 
      vec2 bulb = vec2(x,y); 
      if (length(uv - center) < centerRadius) { 
       // frag intersects white center marker     
       fragColor = vec4(1.0); 
       return;    
      } else if (length(uv - bulb) < radius) { 
       // intersects rotating "light" 
       fragColor = vec4(uv,0.5+0.5*sin(iGlobalTime),1.0); 
       return; 
      } else { 
       // intersects one of the enclosing 24 cylinders 
       for(int k = 0; k < K; k++) { 
        float theta = M_PI * 2.0 * float(k)/ float(K); 
        x = center.x + cos(theta) * encloseR; 
        y = center.y + sin(theta) * encloseR; 
        vec2 encloser = vec2(x,y); 
        if (length(uv - encloser) < encloserRadius) { 
         fragColor = vec4(uv,0.5+0.5*sin(iGlobalTime),1.0); 
        return; 
        } 
       } 
      } 
     } 
    } 


} 
+0

あなたは(罪のそれらの無数を事前に計算することができます)とCOS()、代わりに、シェーダ内でそれらを計算するシェーダに何とかそれらを送信これは、60fpsの時に実行するようになりましたか? – Ripi2

+0

あなたのシェーダはうまく動作しません。少なくとも私にはアーティファクトがたくさんあります。そして、あなたには未使用の変数がたくさんあります。 –

答えて

1

あなたはフラグメントシェーダを最適化したい念頭に置いて、そして唯一のフラグメントシェーダは:

  1. sin(iGlobalTime)やループのうちcos(iGlobalTime)移動し、これらは全体のドローコールの上に静止したままそうループの繰り返しごとにそれらを再計算する必要はありません。
  2. GPUは可能な限りベクトル化された命令セット(SIMD)を採用しています。あなたは、単一のベクトル命令を使用することができ、複数のスカラーOPSを行うことによってサイクルの多くを無駄にしているあなたは本当に置き換え
  3. を、それを必要なときのためにそのSQRT(length)を保存し、
  4. があなたの半径チェックが乗います(注釈付きのコードを参照してください)フロートは
(gl_FragColorへの書き込みではない)あなたのシェーダで未定義の動作を持っていない
  • (インテリジェントシェーダコンパイラは既にこの、ではないが頼りに何かを行います)フロート定数と定数の(あなたのループの制限を)キャスト

    シェーダの最適化された注釈付きバージョンです(まだ未定義の動作が含まれています.jusあなたが提供したもののように)。注釈がの形である:私はもう少し思考をした

    // annotation 
    // old code, if any 
    new code 
    
    #define N 10 
    // define float constant N 
    #define fN 10. 
    #define M 5 
    // define float constant M 
    #define fM 5. 
    #define K 24 
    // define float constant K 
    #define fK 24. 
    #define M_PI 3.1415926535897932384626433832795 
    // predefine 2 times PI 
    #define M_PI2 6.28318531 
    
    void mainImage(out vec4 fragColor, in vec2 fragCoord) 
    { 
        float aspectRatio = iResolution.x/iResolution.y; 
    
        // we dont need these separate 
        // float h = 1.0; 
        // float w = aspectRatio; 
    
        // use vector ops(2 divs 1 mul => 1 div 1 mul) 
        // vec2 uv = vec2(fragCoord.x/iResolution.x * aspectRatio, fragCoord.y/iResolution.y); 
        vec2 uv = fragCoord.xy/iResolution.xy; 
        uv.x *= aspectRatio; 
    
        // most of the following declarations should be predefined or marked as "const"... 
    
        float radius = 0.01; 
        // precalc squared radius 
        float radius2 = radius*radius; 
        float orbitR = 0.02; 
        float orbiterRadius = 0.005; 
        float centerRadius = 0.002; 
        // precalc squared center radius 
        float centerRadius2 = centerRadius * centerRadius; 
        float encloseR = 2.0 * orbitR; 
        float encloserRadius = 0.002; 
        // precalc squared encloser radius 
        float encloserRadius2 = encloserRadius * encloserRadius; 
    
        // Use float constants and vector ops here(2 casts 2 adds 2 divs => 1 add 1 div) 
        // float spacingX = w/(float(N) + 1.0); 
        // float spacingY = h/(float(M) + 1.0); 
        vec2 spacing = vec2(aspectRatio, 1.0)/(vec2(fN, fM)+1.); 
    
        // calc sin and cos of global time 
        // saves N*M(sin,cos,2 muls) 
        vec2 stct = vec2(sin(iGlobalTime), cos(iGlobalTime)); 
        vec2 orbit = orbitR * stct; 
    
        // not needed anymore 
        // float x = 0.0; 
        // float y = 0.0; 
    
        // was never used 
        // vec4 totalLight = vec4(0.0, 0.0, 0.0, 1.0); 
    
        for (int i = 0; i < N; i++) { 
         for (int j = 0; j < M; j++) { 
          // compute the center of the diagram 
          // Use vector ops 
          // vec2 center = vec2(spacingX * (float(i) + 1.0), spacingY * (float(j) + 1.0)); 
          vec2 center = spacing * (vec2(i,j)+1.0); 
    
          // Again use vector opts, use precalced time trig(orbit = orbitR * stct) 
          // x = center.x + orbitR * cos(iGlobalTime); 
          // y = center.y + orbitR * sin(iGlobalTime); 
          // vec2 bulb = vec2(x,y); 
          vec2 bulb = center + orbit; 
          // calculate offsets 
          vec2 centerOffset = uv - center; 
          vec2 bulbOffset = uv - bulb; 
          // use squared length check 
          // if (length(uv - center) < centerRadius) { 
          if (dot(centerOffset, centerOffset) < centerRadius2) { 
           // frag intersects white center marker     
           fragColor = vec4(1.0); 
           return;    
          // use squared length check 
          // } else if (length(uv - bulb) < radius) { 
          } else if (dot(bulbOffset, bulbOffset) < radius2) { 
           // Use precalced sin global time in stct.x 
           // intersects rotating "light" 
           fragColor = vec4(uv,0.5+0.5*stct.x,1.0); 
           return; 
          } else { 
           // intersects one of the enclosing 24 cylinders 
           for(int k = 0; k < K; k++) { 
            // use predefined 2*PI and float K 
            float theta = M_PI2 * float(k)/fK; 
            // Use vector ops(2 muls 2 adds => 1 mul 1 add) 
            // x = center.x + cos(theta) * encloseR; 
            // y = center.y + sin(theta) * encloseR; 
            // vec2 encloser = vec2(x,y); 
            vec2 encloseOffset = uv - (center + vec2(cos(theta),sin(theta)) * encloseR); 
            if (dot(encloseOffset,encloseOffset) < encloserRadius2) { 
             fragColor = vec4(uv,0.5+0.5*stct.x,1.0); 
             return; 
            } 
           } 
          } 
         } 
        } 
    } 
    
  • +0

    LJにありがとう - これはとても役に立ちます。間違いなく多くの改善が計算に加えられます。 –

    0

    ...私はそれを最適化するための最良の方法は、そのよう実際に交差テストを行う前にロジックを変更することで実現しました小さな円では、サークルのグループの境界をチェックします。ここ

    例: https://www.shadertoy.com/view/lssyRH

    関連する問題