前向渲染多次渲染过程

时间:2018-05-19 15:40:14

标签: opengl glsl rendering

我试图将PBR实现到我的简单OpenGL渲染器中并尝试使用多个光照通道,我每次使用一次通过渲染如下:

1-首次通过=深度

2-秒传递=环境

3- [3 .. n]用于场景中的所有灯光。

我使用混合函数glBlendFunc(GL_ONE,GL_ONE)进行传递[3..n],并且我在每个片段着色器的末尾进行Gamma校正。

但我仍然对输出图像有问题,当我使用纹理贴图时,它看起来很吵。

这些步骤有什么问题或者这个过程有什么改进吗?

1 个答案:

答案 0 :(得分:1)

所以基本上,你正在计算的是

f(x) = a^gamma + b^gamma + ...

然而,你真正想要的是(并且已经在评论中注明了@NicolBolas)

g(x) = (a + b + ...)^gamma

现在f(x)g(x)只会在gamma=1等无用的情况下相互平等。你根本无法通过这种方式累加分解非线性函数。

正确的解决方案是在线性空间中将所有内容混合在一起,然后在每个光源的线性贡献的总和上进行伽马校正。

但是,实施此操作会导致一些技术问题。首先,每个通道的标准8位不够精确,无法存储线性颜色值。使用这种格式进行累积步骤将导致强烈可见的色带伪影。有两种方法可以解决这个问题:

  1. 为累积帧缓冲使用更高的每通道位格式。您将需要单独的伽马校正通道,因此您需要通过FBO设置渲染到纹理。 GL_RGBA16F似乎是一种特别好的格式。由于您使用的是PBR照明模型,因此您还可以使用[0,1]之外的颜色值,而不是简单的伽马校正,在最终传递中应用适当的色调映射。请注意,虽然您可能不需要alpha chanell,但仍然在此处使用RGBA格式,因此GL规范中的RGB格式根本不是颜色缓冲区格式,因此可能不会普遍支持它们。 / p>

  2. 以8位/分量格式存储数据,进行伽玛校正。这里的关键是混合必须仍然在线性空间中完成,因此目标帧缓冲颜色值必须在混合之前重新线性化。这可以通过使用GL_SRGB8_ALPHA8格式的帧缓冲并启用GL_FRAMEBUFFER_SRGB来实现。在这种情况下,GPU将片段颜色写入帧缓冲区(当前是片段着色器时)会自动应用标准sRGB伽马校正,但在访问这些值时也会导致sRGB线性化,包括混合。部分" 17.3.6.1混合等式"中的OpenGL 4.6 core profile spec个状态:

      

    如果启用FRAMEBUFFER_SRGB并且帧缓冲附件的FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING值对应   目标缓冲区为SRGB(参见第9.2.3节),RGB目的地   考虑颜色值(从定点转换为浮点之后)   为sRGB色彩空间编码,因此必须在它们之前进行线性化   用于混合。每个RGB组件都以相同的方式转换   在8.24节中描述了sRGB纹理组件。

  3. 方法1将是更通用的方法,而方法2有一些缺点:

    • 线性化/描述化多次完成,可能会浪费一些GPU处理能力
    • 由于仍然只使用8位整数,整体质量会更低。在每个混合步骤之后,结果将四舍五入到下一个可表示的数字,因此您将获得更多的量化噪声。
    • 您仍然受限于[0,1]中的颜色值,并且无法(轻松)进行更有趣的色调映射和HDR渲染效果

    然而,方法2也有优势:

    • 您不需要单独的最终伽马校正传递
    • 如果您的平台/窗口系统支持sRGB帧缓冲区,您可以直接为窗口创建sRGB pixeformat / visual,并且根本不需要任何渲染到纹理的步骤。基本上,请求sRGB帧缓冲并启用GL_FRAMEBUFFER_SRGB就足以使其工作。