【问题标题】:Heat haze/distortion effect in OpenGL (GLSL) and how it should be achievedOpenGL (GLSL) 中的热雾/失真效果以及应该如何实现
【发布时间】:2017-01-16 17:36:25
【问题描述】:

您可以跳至底部的 TL;DR 以获取结论。我更愿意提供尽可能多的信息,以帮助进一步缩小问题范围。

我一直在处理我一直在研究的热雾效应。 这是我正在考虑的那种效果,但由于这是一个相当通用的系统,它适用于任何所谓的屏幕空间折射: 雾霾效应不是我的问题所在,因为它只是采样坐标的失真,而是与采样的内容有关。我的第一种方法是将失真渲染到另一个渲染目标。这种方法相当成功,但如果您以前处理过屏幕空间纹理,则很容易预见到它的主要缺点。问题是由于采样坐标的偏移,如果一个物体在折射镜的前面,它的边缘将被纳入折射计算。 如您所见,当所有几何图形都是环境(无深度测试)或背面几何图形时,它看起来很好。 这里有一个比折射镜更近的立方体。正如你所看到的,这种效果我称之为更接近几何体的出血。

相关着色器代码供参考:

/* transparency.frag */
layout (location = 0) out vec4 out_color; // frag color
layout (location = 1) out vec4 bright; // used for bloom effect
layout (location = 2) out vec4 deform; // deform buffer
[...]
void main(void) {
    [...]
    vec2 n = __sample_noise_texture_with_time__{};
    deform = vec4(n * .1, 0, 1);
    out_color = vec4(0, 0, 0, .0);
    bright = vec4(0.0, 0.0, 0.0, .9);
}

/* post_process.frag */
in vec2 texel;

uniform sampler2D screen_t;
uniform sampler2D depth_t;
uniform sampler2D bright_t;
uniform sampler2D deform_t;
[...]
void main(void) {
    [...]
    vec3 noise_sample = texture(deform_t, texel).xyz;
    vec2 texel_c = texel + noise_sample.xy;
    [sample screen and bloom with texel_c, gama corect, output to color buffer]
}

为了解决这个问题,我尝试了一种涉及比较深度分量的技术。为此,我让透明对象像这样写它的 frag_depth tp 我的变形缓冲区的 z 分量

/* transparency.frag */
[...]
deform = vec4(n * .1, gl_FragCoord.z, 1);
[...]

然后在后处理着色器中快速检查一下确定前面是什么。

[...]
float dist = texture(depth_t, texel_c).x;
float dist1 = noise_sample.z; // what i wrote to the deform buffer z
if (dist + .01 < dist1) { /* do something liek draw debug */ }
[...]

这有点用,但当我离开时就坏了,即使我线性化了深度值并比较了距离。 编辑 3:为深度测试阶段添加了更好的屏幕截图 (在黄色的地方,它正在对前面的东西进行采样,我懒得让它也渲染多边形,所以我把它们画了进去) (这里证明它在更远的地方部分未能通过深度比较测试)

我还通过另一种技术获得了一些“乐趣”,我将颜色缓冲区直接传递给透明度着色器,并让它将样本输出到它的颜色输出。理论上,如果场景是 Z 排序的,这应该会产生所需的结果。我会让你来评判。 (我对出现的模式有一些猜测,因为它们类似于 GPU 的光栅化模式,但这并不是很相关,因为“解决方案”比任何事情都更加绝望)

TL;DR 和正式问题:根据我的知识,我尝试了一些技术,但找不到太多关于该主题的文献。所以我的问题是:您如何将 sch 效果实现为热雾/失真(我可能会添加不覆盖整个屏幕)并且是否有关于该主题的文献。有关我要查看哪种效果的参考,请参阅我的《守望先锋》屏幕截图和游戏中的所有其他类似效果。

为了完整起见,我还想提一下,我正在运行 OpenGL 4.5(在 Windows 上),大多数着色器都是 4.00 版,并且正在使用自定义引擎。

编辑:如果您想了解有关引擎软件部分的信息,请随时询问。我没有包含任何内容,因为我认为它不相关,但我很乐意提供规范和代码 sn-ps 以及更多按需着色器。

编辑 2:我想我还想提一下,这可以通过使用第二个渲染通道和一个剪切平面来实现,但是,这将是昂贵的并且感觉没有必要,因为视点是相同的。可能这是唯一的解决方案,但我不这么认为。

提前感谢您的回答!

【问题讨论】:

  • 会不会是你写的深度分辨率不好?你用什么纹理来存储中间深度? GL_LUMINANCE16?我希望您不要将深度转储到 8 位通道中并稍后进行比较。
  • @Harish 不,不幸的是不是这样,我对所有深度缓冲区都使用 GL_DEPTH_COMPONENT32 格式,这解决了我过去遇到的所有 z 战斗问题。

标签: opengl graphics glsl rendering shader


【解决方案1】:

我认为问题在于您试图扭曲被遮挡对象背后的某些东西,并且该信息不再可用,因为前面的对象已经覆盖了那里的颜色值。因此,您不能从不再存在的颜色缓冲区中扭曲信息。

您正在尝试通过深度测试并跳过属于比透明热对象更靠近相机的对象的像素来解决它,但这会导致边缘泄漏到失真中。即使你跳过边缘,如果透明物体后面有一个物体,被前面的立方体遮挡,它也不会扭曲,因为颜色信息不可用。

额外的渲染通道

  1. 正如您提到的,使用剪切平面的额外渲染通道肯定是解决此问题的一种方法。

多个渲染目标

  1. 另一个类似的解决方案是使用多个渲染目标,预先渲染透明对象的深度,测试它后面的片段,然后将它们渲染到另一个颜色缓冲区。稍后使用此缓冲区而不是全色缓冲区来扭曲。您也可以考虑延迟着色

这里是如何设置多个渲染目标的代码 sn-p。

//create your fbo
GLuint fboID;
glGenFramebuffers(1, &fboID);
glBindFramebuffer(GL_FRAMEBUFFER, fboID);

//create the rbo for depth
GLuint rboID;
glGenRenderbuffers(1, &rboID);
glBindRenderbuffer(GL_RENDERBUFFER, &rboID);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID);

//create two color textures (one for distort)
Gluint colorTexture, distortcolorTexture;
glGenTextures(1, &colorTexture);
glGenTextures(1, &distortcolorTexture);
glBindTexture(GL_TEXTURE_2D, colorTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, distortcolorTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

//attach both textures
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, distortcolorTexture, 0);
//specify both the draw buffers
GLenum drawBuffers[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, DrawBuffers);

首先渲染透明对象的深度。然后在其他对象的片段着色器中

//compute color with your lighting...
//write color to colortexture
gl_FragData[0] = color;
//check if fragment behind your transparent object
if( depth >= tObjDepth )
{
 //write color to distortcolortexture
 gl_FragData[1] = color;
}

最后为你的扭曲着色器使用扭曲颜色纹理。

像素矩阵而不是单个像素的深度测试。

  1. 我认为边缘泄漏,因为也许你不只是扭曲一个像素,而是更多的像素矩阵,也许你也可以尝试检查矩阵的最大深度(例如:以当前像素为中心的 3x3 像素)和如果深度测试失败,则丢弃它。 (注意:这仍然不会扭曲您可能想要扭曲的遮挡对象后面的对象)。

【讨论】:

  • 是的,我在编辑中解决了这个问题,但你是完全正确的。我可以做一个额外的渲染通道,但我更多的是寻找一种便宜的技术,因此为什么我正在做所有屏幕空间的东西。如果您能详细说明我如何使用额外的渲染目标来选择性地渲染特定深度的对象。我考虑过剪裁平面,但那些只适用于额外的渲染通道。 (我现在有点累,但我只是想确认你的回答,谢谢)。
  • @claudehenry 我用一些代码详细说明了我的答案的多个渲染目标部分。希望对您有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-23
  • 2016-07-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多