OpenGL(GLSL)中的热雾/失真效应以及如何实现

cla*_*nry 8 opengl graphics shader rendering glsl

你可以跳到TL;底部的DR得出结论.我倾向于提供尽可能多的信息,以便进一步缩小问题的范围.

我一直在研究我一直在研究的热雾效应问题.这是我想到的那种效果,但由于这是一个相当普遍的系统,它将适用于任何所谓的屏幕空间折射:守望((C)2016)自行车上的热雾 雾霾效应不是我的问题所在,因为它只是采样坐标的失真,而是采样的内容.我的第一种方法是将扭曲渲染到另一个渲染目标.这种方法相当成功,但如果您之前处理过屏幕空间纹理,那么很容易预见到这种方法.问题是由于采样坐标的偏移,如果一个物体在折射器前面,它的边缘将被带入折射计算. 看起来很好 正如你所看到的那样,当所有几何体都是环境(无深度测试)或背面几何体时,它看起来很好. 看起来不好这里有一个比折射器更近的立方体.正如你所看到的那样,有这种效应我称之为更接近几何体的流血.

相关着色器代码供参考:

/* 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);
}
Run Code Online (Sandbox Code Playgroud)
/* 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]
}
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,我尝试了一种涉及比较深度成分的技术.为此,我让透明对象将其frag_depth tp写入我的变形缓冲区的z组件,就像这样

/* transparency.frag */
[...]
deform = vec4(n * .1, gl_FragCoord.z, 1);
[...]
Run Code Online (Sandbox Code Playgroud)

然后确定在后期处理着色器中快速检查的内容.

[...]
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 */ }
[...]
Run Code Online (Sandbox Code Playgroud)

这有点工作但随着我离开而崩溃,甚至线性化了深度值并比较了距离. 颜色键控调试 编辑3:为深度测试阶段添加了更好的截图 更好的颜色键控调试 (在黄色的地方,它正在采样前面的东西,不能打扰它使它渲染多边形,所以我画了它们) 失败了 (并在这里证明它部分失败了深度比较测试从更远的地方)

我还有一些"有趣"的另一种技术,我将颜色缓冲区直接传递给透明度着色器,并将样本输出到其颜色输出.理论上,如果场景是Z排序的,这应该产生所需的结果.我会让你成为那个判断. 在此输入图像描述 (我猜测出现的模式是什么,因为它们类似于GPU的光栅化模式,但这并不是非常相关的正弦,"解决方案"更多的是绝望的努力而不是任何事情)

TL; DR和正式问题:基于我的知识,我已经掌握了一些技术,并且未能找到关于该主题的大量文献.所以我的问题是:你如何实现作为热雾霾/失真的sch效应(不包括整个屏幕可能我添加),并且有关于这个主题的文献.有关我将要查看的效果的参考,请参阅我的Overwatch屏幕截图以及游戏中的所有其他类似效果.

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

编辑:如果你想要引擎的软件部分的信息随时问.我没有包含任何内容,因为我认为它并不相关,但是我很乐意提供规格和代码片段以及更多着色器.

编辑2:我想我也会提到这可以通过使用第二个渲染通道和剪切平面来实现,但是,由于视点相同,这将是昂贵的并且感觉不必要.可能是这是唯一的解决方案,但我不相信.

感谢您提前的答案!

Har*_*ish 1

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

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

附加渲染通道

  1. 正如您提到的带有剪切平面的附加渲染通道肯定是解决此问题的一种方法。

多个渲染目标

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

以下是如何设置多个渲染目标的代码片段。

//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);
Run Code Online (Sandbox Code Playgroud)

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

//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;
}
Run Code Online (Sandbox Code Playgroud)

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

对像素矩阵而不是单个像素进行深度测试。

  1. 我认为边缘正在泄漏,因为也许您不只是简单地扭曲一个像素,而是扭曲更多的像素矩阵,也许您也可以尝试检查矩阵的最大深度(例如:以当前像素为中心的 3x3 像素)并在以下情况下丢弃它:它未通过深度测试。(注意:这仍然不会扭曲您可能想要扭曲的遮挡对象后面的对象)。