GLSL Gif Эффект дизеринга: оптимизация - PullRequest
5 голосов
/ 07 ноября 2011

Image of shader result

У меня есть фрагментный шейдер, который по сути читает альфа-цвет и преобразует его в эффект размытия в пикселях.

Тем не менее, он довольно ресурсоемкий со всеми модами и операторами if. У кого-нибудь есть рекомендации по оптимизации кода ниже?

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;
        }
    }
}

Вот решение на основе обратной связи:

        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);   

        }

Ответы [ 2 ]

6 голосов
/ 07 ноября 2011

То, что вы пытаетесь сделать, это выбрать, должен ли каждый пиксель освещаться в сетке 4x4 на основе исходного альфа. Самое простое - это сделать.

Сначала инициализируйте текстуру 4x4 с соответствующими альфа-вершинами, которые необходимы для прохождения пикселя (я выбрал 1.0 в качестве альфы, чтобы никогда не показывать здесь)

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

Установите эту текстуру с повторением, чтобы полностью избежать мода, и ближайший фильтр, чтобы избежать линейной фильтрации.

Затем используйте эту выборку текстуры, чтобы решить, нужно ли освещать пиксель.

vec4 dither = texture2D(_my4x4, gl_FragCoord / 4.); // 4 is the size of the texture
if (r > dither) { gl_FragColor = color; }

Это предполагает, что r никогда не равняется 1. Существуют простые способы обойти это, например, разделите значения в текстуре 4x4 на 2, кроме 1,0, а затем умножьте дизеринг на 2 перед тестом if, но после выборки.

Некоторые дальнейшие потенциальные оптимизации:

  • Вы можете вообще избежать теста if, используя текстуру для сравнения (теневую текстуру).
  • Вы можете избежать / 4, используя инструкции textureFetch
0 голосов
/ 07 ноября 2011

Во-первых, цвет вашего фрагмента будет одинаковым независимо от результатов операторов if, поскольку цвет устанавливается в начале.

Во-вторых, использование только операторов if без других if заставляет ваш код работатьв каждом расчете, несмотря ни на что.

Гораздо лучший способ сделать это - иметь:

 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;
}

Конечно, это не поможет вам изменить вывод, так как цвет никогда не меняется(как я уже упоминал ранее).Но это должно заставить вещи работать немного быстрее.

Еще одна вещь, которую необходимо учитывать, - это какие случаи выполняются чаще всего.Я предположил, что случаи с большим r являются более распространенными, отсюда и порядок операторов if.Если малые r более распространены, то имеет смысл изменить порядок и иметь r <0.1, r <0.3, r <0.5, r <0.7, r <0.9. </p>

Весь смысл в том, чтобыкод завершается как можно быстрее (т. е. возвращает один из возвращаемых if).Как только один из if возвращает true, остальные игнорируются, что избавляет вас от расчета всех остальных операций мода.

...