GLSL Gif抖动效果:优化

时间:2011-11-07 01:56:06

标签: opengl glsl fragment-shader

Image of shader result

我有一个片段着色器,它基本上读取颜色alpha并将其转换为像素的抖动效果。

然而,对于所有mod和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 个答案:

答案 0 :(得分:6)

您要做的是选择是否应根据源alpha在4x4网格上点亮每个像素。最简单的方法就是做到这一点。

首先使用像素通过所需的相应alpha来初始化4x4纹理(我选择1.0作为从未在此处显示的alpha)

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

使用repeat设置此纹理以完全避免mod,使用最近的滤镜来避免线性过滤。

然后,使用该纹理的采样来决定是否点亮像素。

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

这假设r从不是1.有简单的方法可以解决这个问题,例如:除了1.0之外,将4x4纹理中的值除以2,然后在if测试之前将抖动乘以2,但是在获取之后。

进一步的潜在优化:

  • 你可以通过使用带比较的纹理(阴影纹理)来完全避免使用if测试。
  • 您可以使用textureFetch指令
  • 来避免/ 4

答案 1 :(得分:-1)

首先,无论if语句的结果如何,你的片段颜色都是相同的,因为颜色是在开头设置的。

其次,只使用if else语句而不使用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 <1。 0.1,r&lt; 0.3,r&lt; 0.5,r&lt; 0.7,r&lt; 0.9会更有意义。

这里的重点是使代码尽可能快地退出(即if中的一个返回true)。一旦if中的一个返回true,则忽略其余部分,从而使您无需计算所有其余mod操作。