GLSL隔行扫描

时间:2011-06-25 14:23:19

标签: c++ optimization opengl glsl interlacing

我想使用GLSL以隔行模式高效渲染。

我可以这样做:

vec4 background = texture2D(plane[5], gl_TexCoord[1].st);
if(is_even_row(gl_TexCoord[1].t))
{
    vec4 foreground = get_my_color();
    gl_FragColor = vec4(fore.rgb * foreground .a + background .rgb * (1.0-foreground .a), background .a + fore.a);
}
else
   gl_FragColor = background;

但是,据我所知,GLSL中分支的性质是两个分支实际上都会被执行,因为“even_row”被认为是运行时值。

我可以在这里使用任何技巧,以避免不必要地调用相当重的函数“get_color”吗? is_even_row的行为非常静态。

还是有其他方法可以做到这一点吗?

注意:glPolygonStipple不起作用,因为我的GLSL代码中有自定义混合函数。

2 个答案:

答案 0 :(得分:5)

(根据要求回答评论)

隔行扫描的问题是GPU在2x2集群中运行着色器,这意味着你不会从隔行扫描中获得任何东西(一个好的软件实现可能只会执行所需的实际像素,除非你要求偏导数)。

充其量,隔行扫描以相同的速度运行,最糟糕的是,由于隔行扫描的额外工作,隔行扫描速度较慢。几年前,ShaderX4中有一篇文章提出隔行扫描渲染。我在六个显卡上尝试了这种方法(每个“两大”制造商的3代硬件),并且在每种情况下都运行得慢(有时略微,有时高达50%)。

你可以做的是以垂直分辨率的1/2进行所有昂贵的渲染,这将使像素着色器工作(和纹理带宽)减少1/2。然后,您可以升级纹理(GL_NEAREST),并丢弃每隔一行。

模板测试可用于在执行像素着色器之前丢弃像素。当然硬件仍然以2x2组运行着色器,所以在这个传递中你没有获得任何东西。然而,如果它只是最后一次传递并不重要,这是一个简单的着色器写出一个获取的纹理元素。更昂贵的合成着色器(重要的那些!)以半分辨率运行 您可以在此处找到包含代码的详细说明:fake dynamic branching。此演示通过使用模板丢弃那些超出光线范围的像素来避免照明像素。

另一种不需要模板缓冲区的方法是使用“显式Z剔除”。事实上,这可能更容易,更快 为此,清除Z,禁用颜色写入(glColorMask),并绘制一个全屏四边形,其顶点具有一些“接近”的Z坐标,并使着色器在每个奇数行中杀死碎片(或使用弃用的alpha测试,如果你想要什么,或者其他什么)。 gl_FragCoord.y是一种非常简单的方法,可以知道要杀死的线,使用包裹的小纹理将是另一种(如果必须使用GLSL 1.0)。
现在绘制另一个全屏四边形,顶点中有“远处”Z值(当然还有深度测试)。只需获取半分区纹理(GL_NEAREST过滤),然后将其写出来。由于深度缓冲区的值在每隔一行中“更接近”,因此它将丢弃这些像素。

glPolygonStipple与此相比如何?多边形点画是一种不推荐使用的功能,因为硬件不直接支持它,并且必须通过“秘密”重写着色器以包含额外逻辑或者回退到软件来模拟驱动程序。

答案 1 :(得分:3)

这可能不是进行隔行扫描的正确方法。如果您确实需要实现此效果,请不要在片段着色器中执行此操作。相反,这是你可以做的:

  1. 初始化一个全屏1位模板缓冲区,其中每个位存储相应行的奇偶校验。

  2. 像往常一样将场景渲染到临时FBO,垂直分辨率为1/2。

  3. 打开模板测试,根据要绘制的扫描线组切换模板功能。

  4. 将上述fbo(包含帧内容)的重新缩放版本blit到模板缓冲区。

  5. 请注意,您可以跳过屏幕外FBO步骤并使用模板缓冲区直接绘制,但这会浪费一些填充率测试那些正在剪切的像素。如果你的程序很重要,我刚才提到的解决方案将是最佳的。如果不是,你可能最终略微更好地直接画到屏幕上。