我试图将PBR实现到我的简单OpenGL渲染器中并尝试使用多个光照通道,我每次使用一次通过渲染如下:
1-首次通过=深度
2-秒传递=环境
3- [3 .. n]用于场景中的所有灯光。
我使用混合函数glBlendFunc(GL_ONE,GL_ONE)进行传递[3..n],并且我在每个片段着色器的末尾进行Gamma校正。
但我仍然对输出图像有问题,当我使用纹理贴图时,它看起来很吵。
这些步骤有什么问题或者这个过程有什么改进吗?
答案 0 :(得分:1)
所以基本上,你正在计算的是
f(x) = a^gamma + b^gamma + ...
然而,你真正想要的是(并且已经在评论中注明了@NicolBolas)
g(x) = (a + b + ...)^gamma
现在f(x)
和g(x)
只会在gamma=1
等无用的情况下相互平等。你根本无法通过这种方式累加分解非线性函数。
正确的解决方案是在线性空间中将所有内容混合在一起,然后在每个光源的线性贡献的总和上进行伽马校正。
但是,实施此操作会导致一些技术问题。首先,每个通道的标准8位不够精确,无法存储线性颜色值。使用这种格式进行累积步骤将导致强烈可见的色带伪影。有两种方法可以解决这个问题:
为累积帧缓冲使用更高的每通道位格式。您将需要单独的伽马校正通道,因此您需要通过FBO设置渲染到纹理。 GL_RGBA16F
似乎是一种特别好的格式。由于您使用的是PBR照明模型,因此您还可以使用[0,1]之外的颜色值,而不是简单的伽马校正,在最终传递中应用适当的色调映射。请注意,虽然您可能不需要alpha chanell,但仍然在此处使用RGBA
格式,因此GL规范中的RGB
格式根本不是颜色缓冲区格式,因此可能不会普遍支持它们。 / p>
以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节),R
,G
和B
目的地 考虑颜色值(从定点转换为浮点之后) 为sRGB色彩空间编码,因此必须在它们之前进行线性化 用于混合。每个R
,G
和B
组件都以相同的方式转换 在8.24节中描述了sRGB纹理组件。
方法1将是更通用的方法,而方法2有一些缺点:
然而,方法2也有优势:
GL_FRAMEBUFFER_SRGB
就足以使其工作。