【问题标题】:Using a single FrameBuffer for multiple Shader effects leads to artifacts对多个 Shader 效果使用单个 FrameBuffer 会导致伪影
【发布时间】:2014-05-20 12:22:36
【问题描述】:

我正在使用 libGDX,并创建了我自己的 Sprite 类和渲染器(它在后台使用了 libGDX 的 SpriteBatch)。渲染器非常“智能”,它按图层/着色器/纹理/混合对精灵进行排序,并且精灵可以包含子对象。

现在我正在尝试添加对 FrameBuffers 的支持,但遇到了一个小问题。

我正在尝试通过着色器向我的游戏中的“根”精灵添加多个后处理效果。

  1. 将根 Sprite 渲染为 FBO,(背景和所有实体)
  2. 为同一个 FBO 渲染“漩涡”效果
  3. 为同一个 FBO 渲染绽放效果
  4. 为同一个 FBO 渲染水平模糊
  5. 为同一个 FBO 渲染垂直模糊
  6. 通过 Sprite 将 FBO 的最终结果渲染到屏幕中

这一切都非常简单地实现,每个效果都有自己的 Sprite 对象。它们通过 setLayer(int) 分层,纹理/FBO/着色器通过 setTexture(Texture) / setTarget(Texture) / setShader(Shader) 设置。

我尝试并希望使用相同的纹理作为结果和目标。 例如:

Texture fbo = Video.newTarget();

...

bloom.setTexture(fbo);
bloom.setTarget(fbo);

blurH.setTexture(fbo);
blurH.setTarget(fbo);

....

如果我以这种方式使用相同的 FBO,我会在最终图像中看到一些难以描述的奇怪伪影。我要么在纹理的某处得到一些奇怪的线条,要么只是一些奇怪的扭曲。

如果我为每个效果创建一个单独的 FBO,一切都会正常工作。

例如:

Texture fbo1, fbo2, fbo3, fbo4;

...

bloom.setTexture(fbo1);
bloom.setTarget(fbo2);

blurH.setTexture(fbo2);
blurH.setTarget(fbo3);

...

如果可能的话,我真的很想避免这种情况,因为它会导致代码混乱,并损害性能。

我真的不太精通 OpenGL/GLSL(我什至不太了解渲染管道... :),但是,根据我的解释,有没有人知道可能导致这些伪影的原因?

我也想过有两个 FBO 和它们之间的“乒乓球”,但由于某种原因,我看到的都是黑色的.. 现在.. :) 我将不得不对此进行更多试验。

【问题讨论】:

  • 更重要的是,它会导致未定义的行为。 GL 为这种情况起了一个名字,它是“反馈循环”。在较新的 NV 硬件上,您可以使用纹理屏障和几何着色器来明智地读取和写入相同的缓冲区,或者在 GL4+ 中使用图像加载/存储和内存屏障。但除此之外,这是未定义的,您描述的那些工件是最终结果。

标签: java opengl libgdx


【解决方案1】:

您的问题来自管道中的相关处理步骤。

将根 Sprite 渲染为 FBO,(背景和所有 实体)

渲染“漩涡”效果

那你用同一个FBO来做:

渲染绽放效果

如果您的 GPU 驱动程序尚未完成处理您在绽放效果中覆盖的所有内容,则您在漩涡效果步骤中所做的所有内容。

最好的方法是在独立的阶段分解你的管道,即使这意味着使用多个 FBO。

在我看来如果您不需要原始 FBO 并且所有效果都需要输入相同的纹理大小:

Render the scene in FBO A.
Render Swirl effect in FBO B.
Render Bloom in FBO A using the color texture from FBO B.
Render Vertical Blur in FBO B using the color texture from FBO A.
....

【讨论】:

    【解决方案2】:

    OpenGL ES 不支持同时读取和写入同一纹理,这就是您在第一个示例中所做的。你需要两个 FBO,你可以在它们之间打乒乓球。

    Texture fbo1, fbo2;
    
    ...
    
    bloom.setTexture(fbo1);
    bloom.setTarget(fbo2);
    
    blurH.setTexture(fbo2);
    blurH.setTarget(fbo1);
    
    ...
    

    【讨论】:

    • ...我才意识到我重复了你的最后一句话。我什至用了同一个词!无论如何,这就是这样做的方法。不知道为什么你只看到黑色。
    • 记得将最后一个目标设置为屏幕缓冲区
    【解决方案3】:

    我有一个非常相似的问题,我很高兴我找到了这个主题,这很痛苦,我一直在尝试调试我的 glsl 代码......

    我正在使用带有单个缓冲区的递归着色器进行元胞自动机计算,它遇到了同样的问题。我使用 libgdx 的人工制品是:

    • 从左下角到右上角的对角伪影线
    • 每 256 像素的水平/垂直伪影线

    我通过每帧交换缓冲区来修复它

    private void swapBuffers() {
        FrameBuffer tmp = buffer1;
        buffer1 = buffer2;
        buffer2 = tmp;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-08
      • 2020-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多