opengl-es - 如何转换为 HDR 渲染器?

标签 opengl-es glsl webgl game-engine hdr

我正在将我的 webgl 延迟渲染器转换为使用高动态范围的渲染器。我从网上的各种来源阅读了很多有关该主题的内容,并且有一些问题希望能够得到澄清。我完成的大部分阅读内容都涉及 HDR 图像渲染,但我的问题与渲染器可能需要如何更改才能支持 HDR 相关。

据我了解,HDR 本质上是试图捕捉更高的光线范围,以便我们可以在极亮或黑暗的场景中看到细节。通常在游戏中,我们使用强度 1 表示白光,使用强度 0 表示黑色。但在 HDR/现实世界中,范围要多样化得多。 IE。引擎中的太阳可能比 10 的灯泡亮 10000 倍。

为了应对这些更大的范围,您必须将渲染器转换为使用浮点渲染目标(或者理想情况下是半浮点,因为它们使用更少的内存)进行光 channel 。

我的第一个问题是关于照明的。除了浮点渲染目标之外,这是否仅仅意味着如果以前我有一个代表太阳的光,其强度为 1,那么它现在可以/应该表示为 10000?即

float spec = calcSpec();
vec4 diff = texture2D( sampler, uv );
vec4 color = diff * max(0.0, dot( N, L )) * lightIntensity + spec; //Where lightIntensity  is now 10000?
return color;

照明系统是否还有其他根本性变化(除了 float 纹理和更高的范围)?

接下来,我们现在有了一个 float 渲染目标,它已经累加了所有灯光值(在如上所述的较高范围内)。此时,我可能会使用诸如Bloom之类的东西对渲染目标进行一些后期处理。完成后,现在需要对其进行色调映射,然后才能将其发送到屏幕。这是因为光线范围必须转换回我们显示器的范围。

因此,对于色调映射阶段,我可能会使用后期处理,然后使用色调映射公式将 HDR 光照转换为低动态范围。我选择的技术是《神秘海域 2》中的 John Hables:

const float A = 0.15;
const float B = 0.50;
const float C = 0.10;
const float D = 0.20;
const float E = 0.02;
const float F = 0.30;
const float W = 11.2;

vec3 Uncharted2Tonemap(vec3 x)
{
    return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
}

... // in main pixel shader

vec4 texColor = texture2D(lightSample, texCoord );
texColor *= 16;  // Hardcoded Exposure Adjustment 
float ExposureBias = 2.0;
vec3 curr = Uncharted2Tonemap( ExposureBias * texColor.xyz );
vec3 whiteScale = 1.0 / Uncharted2Tonemap(W);
vec3 color = curr * whiteScale;
 // Gama correction
color.x = pow( color.x, 1.0 /2.2 );
color.y = pow( color.y, 1.0 /2.2 );
color.z = pow( color.z, 1.0 /2.2 );
return vec4( color, 1.0 );

Tone mapping article

我的第二个问题与色调映射阶段有关。除了这个技术之外,还有更多的东西吗?仅仅使用更高的光强度并调整曝光就可以被认为是 HDR——还是还有更多?据我所知,有些游戏具有自动曝光功能来计算平均发光度,但在最基本的层面上是否需要这样做?想必您可以手动调整曝光?

许多文档中讨论的其他内容是 Gamma 校正。 Gamma 校正似乎是在两个区域进行的。首先是在读取纹理时,然后是在将纹理发送到屏幕时再次读取纹理。当读取纹理时,它们必须简单地更改为如下所示:

vec4 diff = pow( texture2D( sampler, uv), 2.2 );

然后在上述色调映射技术中,输出校正是通过以下方式完成的:

pow(color,1/2.2);

在 John Hables 的演讲中,他说并非所有纹理都必须像这样进行校正。漫反射纹理必须如此,但法线贴图之类的东西不一定必须如此。

我的第三个问题是关于 Gamma 校正的。为了让它发挥作用,这是必要的吗?这是否意味着我必须在读取漫反射贴图的所有地方更改引擎?

这是我目前对此次转换所涉及内容的理解。它是否正确?我是否有任何误解或错误?

最佳答案

轻量计算/累加 是的,您通常可以保持闪电计算相同,并且增加方向光的强度(例如超过 1.0)当然没问题。该值可以超过一个的另一种方法是简单地将多个灯光的贡献加在一起。

色调映射

你当然理解这个概念。有很多不同的方法来进行实际的映射,从更简单/天真的方法color = clamp(hdrColor * exposure)到您发布的更复杂(更好)的内容。

自适应色调映射很快就会变得更加复杂。同样,天真的方法是通过使用最亮的像素来简单地标准化颜色,这肯定会使得很难/不可能感知图像较暗部分的细节。您还可以平均亮度和钳位。或者您可以保存最后几帧的整个直方图并在映射中使用它们。

另一种方法是仅使用相邻像素的值来标准化每个像素,即“局部色调映射”。这通常不会在实时渲染中完成。

虽然听起来很复杂,但您发布的公式将产生非常好的结果,因此可以使用它。一旦你有了一个可行的实现,请随意在这里进行实验。还有一些很棒的论文可供使用:)

Gamma 现在,即使您不使用 hdr 渲染, Gamma 校正也很重要。但不用担心,这并不难。

最重要的是始终了解您正在使用的色彩空间。就像没有单位的数字一样,没有色彩空间的颜色几乎没有意义。现在我们喜欢在着色器中使用线性 (rgb) 颜色空间,这意味着具有两倍 rgb 值的颜色应该是两倍的亮度。然而,这不是监视器的工作方式。

相机和照片编辑软件通常只是向我们隐藏所有这些,只是以显示器喜欢的格式(称为 sRGB)保存图片。

sRGB 还有一个额外的优势,那就是压缩。我们通常以每 channel 每像素 8/16/32 位保存图像。如果您在线性空间中保存图片,并且图像中存在小但非常亮的点,则您的 8/16/32 位可能不够精确,无法保存图像较暗部分的亮度差异,并且如果您再次显示它们(当然 Gamma 正确)细节可能会在黑暗中丢失。

您可以更改许多相机和程序中保存的图像的色彩空间,即使它有时有点隐藏。因此,如果您告诉艺术家将所有图像保存在线性 (rgb) 色彩空间中,则根本不需要对图像进行 Gamma 校正。由于大多数程序(如 sRGB 和 sRGB)提供更好的压缩,因此通常最好将描述颜色的图像保存为 sRGB,因此需要对这些图像进行 Gamma 校正。描述值/数据的图像(如法线贴图或凹凸贴图)通常保存在线性颜色空间中(如果您的法线 [1.0, 0.5, 0.0] 没有 45 度角,每个人都会感到困惑;对于非颜色,压缩优势也毫无意义)。

如果您想使用 sRGB 纹理,只需告诉 OpenGL,它就会为您将其转换为线性颜色空间,而不会影响性能。

void glTexImage2D(  GLenum target,
  GLint level,
  GLint internalFormat,  // Use **GL_SRGB** here
  GLsizei width,
  GLsizei height,
  GLint border,
  GLenum format,
  GLenum type,
  const GLvoid * data);

哦,当然,您必须对发送到显示器的所有内容进行 Gamma 校正(因此从线性更改为 sRGB 或 Gamma 2.2)。您可以在色调映射或其他后处理步骤中执行此操作。或者让 OpenGL 为您做这件事;参见glEnable(GL_FRAMEBUFFER_SRGB)

关于opengl-es - 如何转换为 HDR 渲染器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27942736/

相关文章:

javascript - 将 FBO 值投影到屏幕空间以从深度纹理中读取

"-webkit-transform"的 iOS Safari 内存使用情况

windows - 在 Win 7 上使用带有 ANGLE 的 Qt 时出现空白窗口

android - 当设备进入锁定模式并恢复时,着色器在 Android 设备中无法工作

java - Java中的OpenGL,手动创建gluLookAt矩阵

opengl - 在 C++ 中使用 GLSL 和 OpenGL 中的 2D PNG 中的 3D 查找表

c++ - OpenGL 着色器存储缓冲区/memoryBarrierBuffer

python - Convert_obj_two.py 语法错误

javascript - 使用 WebGL 纹理作为 Three.js 纹理贴图

android - Rajawali 中具有可编程颜色的纹理