跳转到内容

GLSL 编程/Unity/顺序无关透明度

来自维基教科书,开放的书籍,开放的世界
“Where Have You Bean” by flickr 用户 Ombligotron。标题中的错别字指的是描绘的雕塑“云门”又名“豆子”。

本教程涵盖顺序无关混合

它继续讨论“透明度”部分,并解决标准透明度的一些问题。如果你没有读过那个教程,你应该先读一下。

“84 – 父子” by Ben Newton。双重曝光的例子。

顺序无关混合

[编辑 | 编辑源代码]

“透明度”部分所述,混合结果通常(特别是对于标准 alpha 混合)取决于三角形渲染的顺序,因此如果三角形没有从后到前排序(它们通常没有),就会导致渲染伪像。术语“顺序无关透明度”描述了各种避免此问题的技术。其中一项技术是顺序无关混合,即使用不依赖于三角形光栅化顺序的混合方程。有两种基本可能性:叠加混合和相乘混合。

叠加混合

[编辑 | 编辑源代码]

叠加混合的标准示例是本节中的图像中的双重曝光:颜色被添加,以至于不可能(或至少非常困难)说出照片拍摄的顺序。叠加混合可以用“透明度”部分中介绍的混合方程来表征

vec4 result = SrcFactor * gl_FragColor + DstFactor * pixel_color;

其中SrcFactorDstFactor由 Unity 的 ShaderLab 语法中的行确定

Blend {SrcFactor 的代码} {DstFactor 的代码}

对于叠加混合,DstFactor 的代码必须为OneSrcFactor 的代码不能依赖于帧缓冲区中的像素颜色;也就是说,它可以是OneSrcColorSrcAlphaOneMinusSrcColorOneMinusSrcAlpha

一个例子是

Shader "GLSL shader using additive blending" {
   SubShader {
      Tags { "Queue" = "Transparent" } 
         // draw after all opaque geometry has been drawn
      Pass { 
         Cull Off // draw front and back faces
         ZWrite Off // don't write to depth buffer 
            // in order not to occlude other objects
         Blend SrcAlpha One // additive blending

         GLSLPROGRAM
               
         #ifdef VERTEX
         
         void main()
         {
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
         }
         
         #endif


         #ifdef FRAGMENT
         
         void main()
         {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 0.3); 
         }
         
         #endif

         ENDGLSL
      }
   }
}

相乘混合

[编辑 | 编辑源代码]

摄影中相乘混合的一个例子是使用多个均匀的灰色滤镜:滤镜放在相机上的顺序对于图像的最终衰减无关紧要。就三角形的光栅化而言,图像对应于三角形光栅化之前帧缓冲区的内容,而滤镜对应于三角形。

在使用以下行在 Unity 中指定相乘混合时

Blend {SrcFactor 的代码} {DstFactor 的代码}

SrcFactor 的代码必须为ZeroDstFactor 的代码必须依赖于片段颜色;也就是说,它可以是SrcColorSrcAlphaOneMinusSrcColorOneMinusSrcAlpha。一个典型的使用片段的 alpha 分量指定的透明度来衰减背景的示例将使用OneMinusSrcAlpha作为DstFactor 的代码

Shader "GLSL shader using multiplicative blending" {
   SubShader {
      Tags { "Queue" = "Transparent" } 
         // draw after all opaque geometry has been drawn
      Pass { 
         Cull Off // draw front and back faces
         ZWrite Off // don't write to depth buffer 
            // in order not to occlude other objects
         Blend Zero OneMinusSrcAlpha // multiplicative blending 
            // for attenuation by the fragment's alpha

         GLSLPROGRAM
               
         #ifdef VERTEX
         
         void main()
         {
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
         }
         
         #endif


         #ifdef FRAGMENT
         
         void main()
         {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 0.3); 
               // only A (alpha) is used
         }
         
         #endif

         ENDGLSL
      }
   }
}

完整着色器代码

[编辑 | 编辑源代码]

最后,将相乘混合用于衰减背景并将叠加混合用于添加三角形的颜色组合在一个着色器中是有意义的,方法是组合上面介绍的两个通道。如果忽略三角形网格颜色本身的衰减,这可以被认为是对小透明度,即小的 alpha 值的 alpha 混合的近似。

Shader "GLSL shader using order-independent blending" {
   SubShader {
      Tags { "Queue" = "Transparent" } 
         // draw after all opaque geometry has been drawn
      Pass { 
         Cull Off // draw front and back faces
         ZWrite Off // don't write to depth buffer 
            // in order not to occlude other objects
         Blend Zero OneMinusSrcAlpha // multiplicative blending 
            // for attenuation by the fragment's alpha

         GLSLPROGRAM
               
         #ifdef VERTEX
         
         void main()
         {
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
         }
         
         #endif


         #ifdef FRAGMENT
         
         void main()
         {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 0.3); 
               // only A (alpha) is used
         }
         
         #endif

         ENDGLSL
      }

      Pass { 
         Cull Off // draw front and back faces
         ZWrite Off // don't write to depth buffer 
            // in order not to occlude other objects
         Blend SrcAlpha One // additive blending to add colors

         GLSLPROGRAM
               
         #ifdef VERTEX
         
         void main()
         {
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
         }
         
         #endif


         #ifdef FRAGMENT
         
         void main()
         {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 0.3);
         }
         
         #endif

         ENDGLSL
      }
   }
}

请注意,两个通道的顺序很重要:首先衰减背景,然后添加颜色。

恭喜你,你已经完成了本教程。我们已经了解了

  • 什么是顺序无关透明度和顺序无关混合。
  • 两种最重要的顺序无关混合类型是什么(叠加和相乘)。
  • 如何实现叠加混合和相乘混合。
  • 如何将两个通道组合用于叠加和相乘混合,以实现对 alpha 混合的顺序无关近似。

进一步阅读

[编辑 | 编辑源代码]

如果你仍然想了解更多

  • 关于着色器代码,你应该阅读“透明度”部分.
  • 关于另一种顺序无关透明度技术,即深度剥离,你可以阅读 Cass Everitt 的技术报告:“交互式顺序无关透明度”,该报告可在网上获取。


< GLSL 编程/Unity

除非另有说明,否则本页上的所有示例源代码均授予公共领域。
华夏公益教科书