5

着色器结果的图像

我有一个片段着色器,它基本上读取颜色 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);   

        }
4

2 回答 2

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

使用重复设置此纹理以完全避免 mod,并设置最近的过滤器以避免线性过滤。

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

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,然后在 if 测试之前但在获取之后将抖动乘以 2。

一些进一步的潜在优化:

  • 您可以通过使用带有比较的纹理(阴影纹理)来完全避免 if 测试。
  • 您可以使用 textureFetch 指令避免 /4
于 2011-11-07T13:59:20.483 回答
-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 < 0.1、r < 0.3、r < 0.5、r < 0.7、r < 0.9 会更有意义。

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

于 2011-11-07T02:38:08.317 回答