延迟Alpha混合

在本文中,我想讨论混合栅格化几何的方法。半透明对象的经典混合模型-Alpha,Additive,Multiplicative-遵循相同的绘制原理:依次绘制一个图元,然后将片段着色器输出处接收到的像素与当前缓冲区中的像素进行混合。每个新原语都会更新将其绘制到的缓冲区的区域;对于Alpha混合,较高的对象会遮盖先前绘制的对象。但是,如果您想对场景上方绘制的一组对象进行某些操作,例如通过蒙版对其进行裁剪或高亮显示它们,该怎么办?在这里,有两个决定立即浮现:要么更改其材质(即更改着色器,扩展纹理集),例如,添加另一个纹理的投影,这将负责透明蒙版。但是,如果我们有很多杂色的物体,则更改每种独特的材料将很不方便,并且充满错误。第二种选择是将我们感兴趣的所有对象绘制到一个单独的全屏目标中,并将其绘制在最终场景上。在这里,我们可以对其内容进行任何操作,但这需要分配额外的内存,并且最不愉快的是切换目标渲染。这不是移动设备上的“最便宜”的操作,将需要执行两次。如果要使用这样的多层结构?在这里,我们可以对其内容进行任何操作,但这需要分配额外的内存,并且最不愉快的是切换目标渲染。这不是移动设备上的“最便宜”的操作,将需要执行两次。如果要使用这样的多层结构?在这里,我们可以对其内容进行任何操作,但这需要分配额外的内存,并且最不愉快的是切换目标渲染。这不是移动设备上最“便宜”的操作,需要两次执行。如果要使用这样的多层结构?



还有另一种更简单,更优雅的方法来解决这些问题。我们可以反向绘制场景!

题外话提醒您经典渲染方法的工作原理
- Alpha Blending , , , . 4 RGBA, RGB — A(Alpha) — ( ). . :


ColorSrc — RGB , ( , ), ColorDst — , , Color_Result — , , , . Variable1 Variable2, ? , ( , ). , : , ... 

:


AlphaSrc — - (), OneMinusAlphaSrc, , 1.0 — AlphaSrc. : 1 * + 2 * (1 — ). alpha (). = 1, , = 0, . OpenGL .

OpenGL ES 2.0 — .

如何逐步形成图像:首先绘制背景,然后依次绘制所有对象。最后呈现的内容将覆盖先前的像素: 





诀窍是什么? 


反向渲染技术(也可以称为延迟混合)的实质如下。我们使用不同的混合公式向后绘制场景。此外,最终图像将与经典方法完全相同。

怎么运行的?


上面描述了通过我们绘制的图像的透明通道进行混合的方法。现在,我们将以另一种方式扭转它:我们将使用已绘制像素的透明度(或者将已绘制透明度与已绘制像素混合)。也就是说,我们将使用AlphaSaturate代替AlphaSrc,而不是OneMinusAlphaSrc-一个。事实证明,如果缓冲区中已经有透明度= 1的对象,则贡献将为零,并且此类像素的颜色将不会更改。如果透明度为零,让我们将两种颜色加在一起(为此,我们将需要清除零的帧缓冲区或清除零透明度的黑色)。使用此添加,结果颜色将等于绘制的颜色。最终公式如下所示:
 

(大约AlphaSaturate = min(AlphaSrc,1-AlphaDst))

需要添加透明度值:它必须逐层累积,也就是说,我们在alpha通道的混合变量中将有一个和一个。我们为什么不修改ColorDst并用零清除缓冲区?这对于添加混合是必需的,添加混合只会不同,因为它在AlphaSrc变量中将为零。它不应修改透明度,而只能修改颜色。

为了清楚起见,反向渲染方案如下所示: 

首先,我们清除帧缓冲区。然后,我们设置上面给出的混合函数,并从最顶部的对象开始绘制(在经典方法中,它们将最后绘制),一直到最底部的对象开始绘制。背景图像将最后绘制。


如何使用?


我将以我们的项目为例,描述用这种方法解决的几个任务:

  1. 用透明蒙版剪切对象。游戏室的平滑裁剪:


    绘制完运动场之后,足以清除我们要隐藏图像的那些位置中的透明度。这是使用混合公式完成的,其中绘制的蒙版对象以其自身的透明度反向覆盖颜色和透明度,并且可以连续调整纯化度。在这种情况下,以下几何图形用于裁剪:


    当相机在房间之间移动时,它会改变形状。清洗的混合配方如下:

    ColorSrc = GL_ZERO,
    ColorDst = GL_ONE_MINUS_SRC_ALPHA,
    AlphaSrc = GL_ZERO,
    AlphaDst = GL_ONE_MINUS_SRC_ALPHA

    您可以使用具有任何纹理的任何几何体,从需要清洁的层开始清洁:

  2. 场的平滑消失类似地完成。发行价为一期DrawCall。
  3. :


    , UI, . , «» , , « », . , : , . :

    ColorSrc = GL_SRC_ALPHA,
    ColorDst = GL_ONE_MINUS_SRC_ALPHA,
    AlphaSrc = GL_ZERO,
    AlphaDst = GL_ONE
  4. , :


  5. «»:



    . , . — 2 DrawCalls.



Ambient occlusion





有一个缺点,或者说是一个限制:对于这种技术,并非所有的混合物都可以重复。完全可以进行Alpha混合和添加,但是您必须适应或不使用自己的特殊混合物。但是有一个出路:您可以分离场景渲染的各个阶段。它的一部分应该通过反向方法完成,一部分是通常的方法,这就是我们在现场和后期处理中为特殊效果所做的。

叠加渲染和混合渲染技术的重要一点:如果在反向渲染通道之前进行绘制,并且纹理中没有透明度信息(诸如“黑色背景上的白色斑点”的纹理),则此类对象将覆盖透明度。在“返回”段落中,有关此部分的信息将丢失,并且在视觉上看起来像是“黑色正方形”或亮点周围的黑色边框:


这可以通过在混合Alpha通道方面修改添加剂混合来克服:

AlphaSrc = GL_ONE_MINUS_DST_ALPHA
AlphaDst = GL_ONE

但这并不适合所有类型的混合,并且修改纹理本身会更加可靠。含义:

如果存在以下形式的纹理: 


然后,您需要执行以下一项操作:


即,必须将颜色通道的亮度转换为透明度,并与透明度相反地拉伸颜色。产生的旧纹理在黑色背景上看起来应该相同。手动操作不太可能成功,因此选择自动转换器是有意义的。在这种情况下,通道转换伪代码将如下所示:

RGB_old = Texel_in.rgb
A_old = Texel_in.a
A_middle = 1.0 / ((RGB_old) / 3.0) * A_old // linear color space
RGB_new = RGB_old * A_middle;
A_shift = minimum( 1.0 / RGB_new.r, 1.0)
A_shift = minimum( 1.0 / RGB_new.g, A_shift)
A_shift = minimum( 1.0 / RGB_new.b, A_shift)
RGB_result = RGB_new * A_shift; 
A_result = (RGB_result) / 3.0)
Texel_out = Vector4(RGB_result, A_result)



在这里,我将介绍渲染项目场景的步骤
 
  1. . , -.

  2. , , , «» :


  3. UI:

  4. , , , :

  5. :

  6. :

  7. , .




结论


使用Alpha通道作为遮罩,该方法可以在绘制场景的各层时非常简单且廉价地工作。在已经工作的项目中实现它相对简单:它不需要深度修改图形子系统代码,足以改变渲染顺序和混合公式。在某些地方,它可以大大节省性能。有限制,但是在大多数情况下,您可以接受它们。

Source: https://habr.com/ru/post/undefined/


All Articles