2011-11-07 11 views
5

Image of shader resultGLSL Gif Roztrząsanie Efekt: Optymalizacja

mam shader fragment, który zasadniczo odczytuje alfa kolorów i przekłada go na drgającego efekt całej pikseli.

Jest jednak dość intensywny procesor z wszystkimi modami i instrukcjami if. Czy ktoś ma jakieś zalecenia dotyczące optymalizacji kodu poniżej?

varying vec2 the_uv; 
varying vec4 color; 

void main() 
{ 
    // The pixel color will correspond 
    // to the uv coords of the texture 
    // for the given vertice, retrieved 
    // by the Vertex shader through varying vec2 the_uv 

    gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); 
    vec4 tex = texture2D(_MainTex, the_uv); 
    tex = tex * color ; 
    float r = tex.a; 

    if (r > 0.1) { 
     if ((mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y, 4.0)) > 6.00) { 
      gl_FragColor = color; 
     } 
    } 

    if (r > 0.5) { 
     if ((mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y, 4.0)) > 6.00) { 
      gl_FragColor = color; 
     } 
    } 

    if (r > 0.7) { 
     if ((mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0)) > 6.00) { 
      gl_FragColor = color; 
     } 
    } 

    if (r > 0.9) { 
     if ((mod(gl_FragCoord.x + 1.0, 4.001) + mod(gl_FragCoord.y + 1.0, 4.0)) > 6.00) { 
      gl_FragColor = color; 
     } 
    } 

    if (r > 0.3) { 
     if ((mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0)) > 6.00) { 
      gl_FragColor = color; 
     } 
    } 
} 

Oto rozwiązanie oparte na informacji zwrotnej:

 varying vec2 the_uv; 
     varying vec4 color; 

     void main() 
     { 
      color = gl_Color; 
      gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 
      the_uv = gl_MultiTexCoord0.st; 
     } 
     #endif 

     #ifdef FRAGMENT 
     uniform sampler2D _MainTex; 
     uniform sampler2D _GridTex; 
     varying vec2 the_uv; 
     varying vec4 color; 

     void main() 
     { 
      if (texture2D(_MainTex, the_uv).a * color.a > texture2D(_GridTex, vec2(gl_FragCoord.x, gl_FragCoord.y)*.25).a) gl_FragColor = color; 
      else gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); 

     } 
+0

Masz bardzo ogólne nazwy dla swoich zmiennych. Byłoby łatwiej dowiedzieć się, jaki jest twój algorytm, jeśli używałeś bardziej ekspresyjnych nazw. –

Odpowiedz

6

Próbujesz wybrać, czy każdy piksel powinien się świecić na siatce 4x4 w oparciu o źródło alfa. Najprościej to zrobić.

pierwsze zainicjować 4x4 tekstury z odpowiednimi alfa, które są wymagane, aby mieć przepustkę pikseli (wybrałem 1.0 jako alfa za nie pokazano tutaj)

1.0 0.5 1.0 0.1 
1.0 1.0 0.9 1.0 
1.0 0.3 1.0 0.7 
1.0 1.0 1.0 1.0 

konfiguracja Ten tekstury z powtórzeniem, aby uniknąć mod całkowicie i najbliższy filtr, aby uniknąć filtrowania liniowego.

Następnie użyj tej próbkowania tej tekstury, aby zdecydować, czy chcesz oświetlić piksel.

Zakłada się, że r nigdy nie ma 1. Istnieją proste sposoby obejścia tego problemu, np. Podziel wartości w teksturach 4x4 na 2, z wyjątkiem 1.0, a następnie pomnóż dithering przez 2 przed testem if, ale po pobraniu.

Niektóre dalsze potencjalne optymalizacje:

  • można uniknąć, jeśli w ogóle za pomocą testu tekstury z cienia porównania (tekstury).
  • można uniknąć/4 przy użyciu instrukcji textureFetch
+0

dziękuję! moje końcowe shadery wyglądają jak: – miketucker

-1

Przede wszystkim, kolor fragment będzie taki sam, niezależnie od wyników działań if ponieważ kolor jest u początek.

Po drugie, używanie tylko wtedy, gdy instrukcje bez żadnych innych elementów powodują, że kod przechodzi przez każde obliczenie, niezależnie od tego.

O wiele lepszym sposobem na to byłoby mieć:

if (r > 0.9) { 
    if ((mod(gl_FragCoord.x + 1.0, 4.001) + mod(gl_FragCoord.y + 1.0, 4.0)) > 6.00) { 
     gl_FragColor = color; 
    } 
} 
else if (r > 0.7) { 
    if ((mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0)) > 6.00) { 
     gl_FragColor = color; 
    } 
} 
else if (r > 0.5) { 
    if ((mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y, 4.0)) > 6.00) { 
     gl_FragColor = color; 
    } 
} 
else if (r > 0.3) { 
    if ((mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0)) > 6.00) { 
     gl_FragColor = color; 
    } 
} 
else if (r > 0.1) { 
    if ((mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y, 4.0)) > 6.00) { 
     gl_FragColor = color; 
    } 
} 
else{ 
    gl_FragColor = color; 
} 

Oczywiście to nie pomoże Ci zmienić wyjście ponieważ kolor nigdy nie zmienia (jak wspomniałem wcześniej). Ale to powinno sprawić, że sprawy będą działać nieco szybciej.

Jeszcze jedną rzeczą, którą należy wziąć pod uwagę, jest to, jakie sprawy najczęściej są wykonywane. Przyjąłem, że przypadki z dużą wartością r są częstsze, stąd kolejność instrukcji if. Jeśli małe r są bardziej powszechne, to odwrócenie kolejności i posiadanie r < 0.1, r < 0.3, r < 0.5, r < 0.7, r < 0.9 miałoby więcej sensu.

Chodzi o to, aby kod został jak najszybciej zakończony (tzn. Gdy jedno z poleceń atrybutu "if" zwróci wartość true, reszta zostanie zignorowana, co uniemożliwi obliczenie wszystkich pozostałych operacji modowych.

+3

"Po pierwsze, twój kolor fragmentu będzie taki sam, niezależnie od wyników instrukcji if, ponieważ kolor jest ustawiony na początku." To nieprawda. Możesz ustawić kilka stopni wyjścia shadera; tylko ostatni jest brany. Możesz nawet wprowadzać w nich modyfikacje na miejscu. –

+0

Przepraszam, jeśli nie byłam jasna. Chodziło mi o to, że ponieważ kolor jest przypisany poza pętlami if, wynik każdej pojedynczej pętli if będzie taki sam niezależnie od tego, który z nich jest prawdziwy (r> 0,9 daje ten sam kolor fragu co r> 0,1). (Chyba że jest coś całkowicie nie tak z moim zrozumieniem działania shaderów). – NickLH

+0

Ale ustawiają tylko ten kolor, jeśli mają ten dodatkowy warunek "mod". W przeciwnym razie używają domyślnego koloru 0. To nie jest po prostu 'r> X'. –