每个对象的后期处理

时间:2017-01-31 16:29:25

标签: opengl-es post-processing

假设我需要渲染以下场景:

  1. 两个立方体,一个黄色,另一个红色。
  2. 红色立方体需要发光'红灯亮,黄灯不发光。
  3. 立方体围绕共同的重心旋转。
  4. 相机位于 这样一种方式,当红色,发光的立方体靠近相机时, 它部分阻碍黄色立方体,当黄色立方体是 靠近相机,它部分阻挡了红色的,发光的。
  5. 如果不是发光,那么渲染场景将是微不足道的。随着光芒,我可以看到至少2种渲染方式:

      

    第1天

         
        
    1. 将黄色立方体渲染到屏幕上。
    2.   
    3. 计算红色立方体最终会在屏幕上显示的位置(很简单,我们有顶点+模型视图矩阵),因此将其渲染到屏幕外   FBO足够大(为发光留下余量);一定要保存   深度到纹理。
    4.   
    5. 对FBO进行后处理并发光。
    6.   
    7. 现在困难的部分:将FBO与屏幕合并。我们需要考虑深度(我们已经存储在纹理中)所以看起来   我们需要做以下事情:

        a)渲染四边形,用FBO的颜色附件进行纹理化   b)适当地设置ModelView矩阵(   我们需要通过一些向量来移动纹理,因为我们是故意的   在步骤2中将红色立方体渲染为小于屏幕FBO(对于   速度原因!)))c)合并'片段着色器,我们需要写   来自FBO的深度附件纹理的gl_FragDepth(而不是来自   FragCoord.z)
    8.         

      WAY2

           
          
      1. 将两个立方体渲染到屏幕外的FBO;设置模板,以便红色立方体的无障碍部分标记为1。
      2.   
      3. 对FBO进行后处理,使标记区域变得模糊,并将其混合以产生光晕
      4.   
      5. 将FBO Blit到屏幕
      6.   

    WAY 1有效,但主要问题是速度,即步骤4c。在片段着色器中写入gl_FragDepth会禁用早期的z-test。

    WAY 2也有点工作,看起来应该快得多,但它不会给出100%正确的结果。 问题是当红色立方体被黄色部分部分遮挡时,红色立方体中接近黄色立方体的像素会变黄“黄色”。当我们模糊它们时,即更接近的黄色立方体“悄悄地”。发光。

    我想我可以解决上述问题,当我模糊时,当我正在阅读的像素突然在深度减少时停止模糊(意味着我们只是从另一个物体跳到更近的物体)但这意味着模糊时纹理访问量的两倍(除了获取COLOR纹理,我们需要继续获取DEPTH纹理),以及模糊片段着色器中的条件语句。我没有尝试,但我不相信它会比第1天更快,甚至不会给出100%正确的结果(靠近黄色立方体边界的红色像素只是受红色立方体可见部分的影响,而不是整个(-blurRadius,+ blurRadius)区域,所以在这个区域内,发光不会100%相同)。

    有人会建议如何最好地实现这样的对象后期处理' ?

    修改

    我写的是一种用于图形效果的OpenGL ES库。客户端可以给它一些指令,例如'取这个Mesh,用它构造它,将它的ModelView矩阵应用于下面的矩阵变换,将下面的扭曲应用到它的顶点,下面的一组片段效果,渲染到下面的Framebuffer'。

    在我的图书馆里,我已经拥有了我所说的基质效应' (修改模型视图)'顶点效果' (各种顶点扭曲)和“片段效应”#39; (RGBA每片段的各种变化)。 现在我正在尝试添加我称之为“后处理”的内容。效果,这个' GLOW'成为他们中的第一个。我定义了效果,我完全按照你的描述进行了视觉。

    效果应用于整个网格;因此,现在我需要我称之为“每个对象的后期处理”。

    图书馆主要针对的是2.5D'用法,如移动应用程序中的GPU加速UI,2-2.5D游戏(想想Candy Crush)等等。我怀疑人们实际上会将它用于任何真正的3D大型游戏。 所以FPS虽然总是很重要,但通常不那么重要。

    我非常努力地保持API' Mesh-local',即渲染管道只知道它正在渲染的当前Mesh。关于上述内容的主要抱怨是它必须知道我们将要渲染到给定Framebuffer的整个集合网格。话虽这么说,如果'网格局部'使用后处理效果是不可能或无法有效完成的,那么我想我必须放弃它(并使我的教程更复杂)。

    昨天我在想这个:

    # 'Almost-Mesh-local' algorithm for rendering N different Meshes, some of them glowing
    
    Create FBO, attach texture the size of the screen to COLOR0, another texture 1/4 the size of the screen to COLOR1.
    Enable DEPTH test, clear COLOR/DEPTH
    
    FOREACH( glowing Mesh )
       {
       use MRT to render it to COLOR0 and COLOR1 in one go
       }  
    
    Detach COLOR1, attach STENCIL texture
    Set up STENCIL so that the test always passes and writes 1s when Depth test passes
    Switch off DEPTH/COLOR writes
    
    FOREACH( glowing Mesh )
       {
       enlarge it by N% (amount of GLOW needs to be modifiable!) 
       render to STENCIL   // i.e. mark the future 'glow' regions with 1s in stencil   
       }
    
    Set up STENCIL so that test always passes and writes 0 when Depth test passes
    Switch on DEPTH/COLOR writes
    
    FOREACH( not glowing Mesh )
       {
       render to COLOR0/STENCIL/DEPTH  // now COLOR0 contains everything rendered, except for the GLOW. STENCIL marks the unobstructed glowing areas with 1s 
       }
    
    Blur the COLOR1 texture with BLUR radius 'N'
    Merge COLOR0 and COLOR1 to the screen in the following way:
    
    IF ( STENCIL==0 ) take pixel from COLOR0
    ELSE              blend COLOR0 and COLOR1
    END
    

    这不是Mesh-local(我们仍然需要能够处理所有'发光' Meshes优先)虽然我称它为“几乎是Mesh-local'因为它只根据应用于它们的效果来区分网格,而不是哪个网格在哪里或哪个会阻碍它们。

    当两个GLOWING Meshes相互阻塞时,它也可能会出现问题(混合不必按正确的顺序进行),尽管GLOW是半透明的,我希望最终的外观或多或少都可以。

    看起来它甚至可以变成一个完全' Mesh-local'做一个巨人的算法

    FOREACH(Mesh) 
      {
      if( glowing )
         {
    
         }
      else 
         {
    
         }
      }
    

    虽然代价是必须在FBO中附加和分离内容并在每次循环迭代时以不同方式设置STENCILS。

1 个答案:

答案 0 :(得分:0)

一个下意识的建议是做混合动力:

  1. 计算红色立方体最终会在屏幕上显示的位置,因此将其渲染到足够大的屏幕外FBO(或与屏幕尺寸相同的一个,因为在蹄上创建FBO可能效率不高);不要担心深度,它只是你所追求的颜色;
  2. 将两个立方体渲染到离屏FBO;设置模板,以便红色立方体的无障碍部分标有1s;
  3. 使用(2)原始像素在模板为0的情况下对屏幕进行后处理,或者在模板为1的情况下通过采样(1)计算模糊像素。