性能 - 在OpenGL中绘制多个2D圆形

3
我正在尝试在OpenGL中为我的2D游戏绘制大量2D圆。它们大小相同且具有相同的纹理。许多精灵重叠。最快的方法是什么?
(这应该注意到,黑边缘只是由于圆形扩散而产生的。此屏幕截图后不久就被填充了。)
目前,我使用一对带纹理的三角形来制作每个圆。我在纹理的边缘周围使用透明度以使其看起来像一个圆。对于这个问题使用混合效果非常慢(并且无法进行z剔除,因为它们被渲染为深度缓冲区的正方形)。相反,我没有使用混合效果,而是让我的片段着色器丢弃任何alpha为0的片段。这有效,但意味着早期的z不可能(因为片段被丢弃)。
速度受到大量过度绘制和GPU填充率的限制。圆形的绘制顺序并不重要(只要在帧之间不改变,避免闪烁),因此我一直在尝试确保屏幕上的每个像素只能写入一次。
我尝试使用深度缓冲区来实现这一点。在每帧开始时,它被清除为1.0f。然后当绘制一个圆时,它会将深度缓冲区的该部分更改为0.0f。当应该绘制另一个圆时,由于新圆也具有0.0f的z值,所以它不会被绘制。这比较有效,应该减少需要绘制的像素数量。然而,奇怪的是,它并没有变快。我已经问过关于这种行为的问题(opengl depth buffer slow when points have same depth),建议是在使用相等的z值时,z剔除没有被加速。
相反,我必须为我的所有圆形分别指定从0开始的虚假z值。然后,当我使用glDrawArrays进行渲染并使用GL_LESS默认设置时,由于z剔除,我们可以正确地获得速度提升(尽管无法实现早期z,因为要丢弃片段以使圆形成为可能)。但是,这并不理想,因为我不得不添加大量与z相关的代码来制作一个简单的2D游戏(如果可能,不传递z值会更快)。然而,这是我目前找到的最快的方法。
最后,我尝试使用模板缓冲区,在这里我使用了
glStencilFunc(GL_EQUAL, 0, 1);
glStencilOp(GL_KEEP, GL_INCR, GL_INCR);

每帧都会将模板缓冲区重置为0。其思想是,在像素第一次绘制后,将其更改为模板缓冲区中的非零值。然后该像素不应再次绘制,从而减少了过度绘制的数量。然而,证明这并不比仅使用深度缓冲区或不使用任何缓冲区来绘制所有内容更快。
人们发现最快的方法是什么?

你是在一个批次中绘制它们吗? - Justin Meiners
是的,每帧只需一个 glDrawArrays(GL_TRIANGLES, 0, 100001*(2*3)); 就可以绘制它们所有。 - Ellipsis
嗯,我预计会有相当不错的结果 - 当窗口较小时,速度更快吗? - Justin Meiners
是的,它要快得多。 - Ellipsis
1个回答

3
基本问题是您受到了填充限制,这是GPU无法在您期望的时间内着色所有要求绘制的片段。您的深度缓冲技巧无效的原因是处理最耗时的部分是着色片段(通过自己的片段着色器或通过固定功能着色引擎),这发生在深度测试之前。使用模板的同样问题也会出现;在模板之前进行像素着色。
有一些东西可能会有所帮助,但它们取决于您的硬件:
  • render your sprites from front to back with depth buffering. Modern GPUs often try to determine if a collection of fragments will be visible before sending them off to be shaded. Roughly speaking, the depth buffer (or a represenation of it) is checked to see if the fragment that's about to be shaded will be visible, and if not, it's processing is terminated at that point. This should help reduce the number of pixels that need to be written to the framebuffer.
  • Use a fragment shader that immediately checks your texel's alpha value, and discards the fragment before any additional processing, as in:

    varying vec2 texCoord;
    uniform sampler2D tex;
    
    void main()
    {
        vec4 texel = texture( tex, texCoord );
    
        if ( texel.a < 0.01 ) discard;
    
        // rest of your color computations
    }
    

您也可以在固定功能片段处理中使用alpha测试,但无法确定测试是否会在片段着色完成之前应用。


我可能表达不清楚。如果我在z轴上为每个圆赋予不同的值,实际上比根本不使用深度缓冲快两倍(在我的测试中)。然而,对于具有相同z值的圆形进行深度缓冲似乎并没有比根本不进行z缓冲更快。(这看起来应该同样有效?)这可能是因为您提到的第一个问题。(这很有趣,因为听起来像早期的z?如果在片段着色器中丢弃,我认为这是不可能的?)我的片段着色器就像那样。谢谢您的回复。 - Ellipsis
使用深度缓存比不使用更有意义:至少您可以节省大量读取-修改-写入循环到颜色和深度缓冲区,所以我对此并不感到惊讶。至于使用相同的z值,我不知道。可能存在一个快速模式用于小于深度测试,或者等于模式产生的结果与非深度测试基本相同。最后,还有一些架构如果您使用此技巧,则会禁用早期Z,但这不是所有GPU的通用特征-它是一种实现问题,并且通过测试才能发现。同时,不客气。 - radical7

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接