3

我正在尝试在 opengl 中为我的 2d 游戏绘制大量的 2d 圆圈。它们的大小相同,质地相同。许多精灵重叠。最快的方法是什么?

我正在制作的效果示例 http://img805.imageshack.us/img805/6379/circles.png

(需要注意的是,黑边只是由于圆圈的扩大爆炸造成的。截屏后不久就被填充了。

目前我正在使用一对带纹理的三角形来制作每个圆圈。我在纹理边缘周围有透明度,以使其看起来像一个圆圈。事实证明,为此使用混合非常慢(并且 z 剔除是不可能的,因为它们被渲染为深度缓冲区的正方形)。相反,我没有使用混合,而是让我的片段着色器丢弃任何 alpha 为 0 的片段。这有效,但这意味着早期 z 是不可能的(因为片段被丢弃)。

速度受到大量透支和 gpu 填充率的限制。绘制圆圈的顺序并不重要(前提是它不会在创建闪烁的帧之间发生变化),所以我一直在努力确保屏幕上的每个像素只能写入一次。

我通过使用深度缓冲区尝试了这一点。在每一帧开始时,它被清除为 1.0f。然后,当绘制一个圆圈时,它会将深度缓冲区的那部分更改为 0.0f。当通常在那里绘制另一个圆时,它不是因为新圆的 az 也为 0.0f。这不小于深度缓冲区中当前存在的 0.0f,因此它不会被绘制。这有效,并且应该减少必须绘制的像素数量。然而; 奇怪的是它并没有更快。我已经问过一个关于这种行为的问题(当点具有相同的深度时,opengl 深度缓冲区很慢)并且建议是使用相等的 z 值时 z 剔除没有被加速。

相反,我必须给我所有的圈子从 0 向上分开假 z 值。然后,当我使用 glDrawArrays 和默认值 GL_LESS 进行渲染时,由于 z 剔除,我们正确地获得了速度提升(尽管早期 z 是不可能的,因为碎片被丢弃以使圆圈成为可能)。然而,这并不理想,因为我不得不为 2d 游戏添加大量与 z 相关的代码,而这根本不需要它(如果可能的话,不传递 z 值会更快)。然而,这是我目前找到的最快的方式。

最后我尝试使用模板缓冲区,这里我使用

glStencilFunc(GL_EQUAL, 0, 1);
glStencilOp(GL_KEEP, GL_INCR, GL_INCR);

模板缓冲区每帧重置为 0。这个想法是在一个像素被绘制到第一次之后。然后在模板缓冲区中将其更改为非零。然后不应再次绘制该像素,从而减少透支量。然而,事实证明,这并不比仅在没有模板缓冲区或深度缓冲区的情况下绘制所有内容快。

人们发现写我正在尝试的最快方法是什么?

4

1 回答 1

3

根本问题是您的填充受限,这是 GPU 无法在您期望的时间内对您要求它绘制的所有片段进行着色。您使用深度缓冲技巧无效的原因是处理中最耗时的部分是对片段进行着色(通过您自己的片段着色器或通过固定功能着色引擎),这发生深度之前测试。使用模板也会出现同样的问题;对像素进行着色是在模板制作之前进行的。

有一些事情可能会有所帮助,但它们取决于您的硬件:

  • 使用深度缓冲从前到后渲染您的精灵。现代 GPU 通常会尝试确定一组片段是否可见,然后再将它们发送出去进行着色。粗略地说,检查深度缓冲区(或它的表示)以查看即将被着色的片段是否可见,如果不可见,则在该点终止处理。这应该有助于减少需要写入帧缓冲区的像素数量。
  • 使用片段着色器立即检查纹素的 alpha 值,并在任何其他处理之前丢弃片段,如:

    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 测试,但无法确定是否会在片段着色完成之前应用该测试)。

于 2013-02-19T18:03:18.060 回答