OpenGL(GLSL)中的热雾/失真效应及其实现方式

时间:2016-09-08 17:17:32

标签: opengl graphics glsl rendering shader

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

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

相关着色器代码供参考:

/* 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 */ }
[...]
虽然我已经将深度值线性化并比较了距离,但是这种方法有所改善,但在我离开时发生了故障。 color keyed debug 编辑3:为深度测试阶段添加了更好的截图 better color keyed debug (在黄色的地方,它正在采样前面的东西,不能打扰它使它渲染多边形,所以我画了它们) failing from further away (这里证明它部分地使深度对比测试部分失败了)

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

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

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

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

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

提前感谢您的回答!

1 个答案:

答案 0 :(得分:1)

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

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

  

额外的渲染通行证

  1. 正如您所提到的,带剪裁平面的其他渲染过程肯定是解决此问题的一种方法。
  2.   

    多个渲染目标

    1. 另一个类似的解决方案是使用多个渲染目标,手工渲染透明对象的深度,测试其后面的碎片,并将它们渲染到另一个颜色缓冲区。稍后使用此缓冲区来扭曲而不是全色缓冲区。您还可以考虑 deffered shading
    2. 以下是如何设置多个渲染目标的代码段。

      //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;
      }
      

      最后使用distortcolortexture作为扭曲着色器。

        

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

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