(六)透明度问题简析

1.前言

曾经做过一个项目,要求一个实体模型能够半透明显示,并高亮其中的某一部分。采用unity标准(standard)材质,通过将RenderMode改为transparent,然后通过MeshRender组件更改颜色透明通道进行实现。但是结果不尽人意,这是由于模型之间交互覆盖。很难周全,再次对Unity混合问题进行简析。

2.渲染队列

Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}

渲染队列,表示那部分先渲染,并非完全按照深度值来进行。在同一个渲染队列的对象会按深度进行渲染,关闭深度写入的,则按游戏对象位置来处理。
Background:顾名思义,表示背景队列,此渲染队列的物体最先被渲染。
Geometry:表示正常的结合对象,在Backgroud队列后被渲染。
AlphaTest:透明度测试,通过clip可以对color的alpha值进行处理,当大于某一值时就discard掉(可以关闭深度写入ZWrite Off)。
Transparent:表示半透明队列,此队列的对象一般都是需要进行半透明处理,并关闭深度写入。并且为了保证叠加效果,其在Geometry和AlphaTest之后渲染。半透明处理时要开启混合,即Blend SrcAlpha OneMinusSrcAlpha,关闭混合则为Blend Off。
Overlay:跟Backgound一样,顾名思义,是最后覆盖之意,表示最后被渲染的一层。

3.透明度混合

上文中在Transparent中简单说明透明度混合的关闭开启,再次详细说明一下。混合的意思为上一次buffer中颜色与新计算出来的颜色是否叠加或者覆盖的意思。

3.1 Blend命令格式

基本格式为Blend+参数:

Blend参数形式含义
Off 关闭混合
factorA factorB 表示Blend后面加两个参数,以空格分开,将源颜色成以factorA与目标颜色成以factorB相加结果存入颜色缓冲区
factorA factorB alphaA alphaB 表示Blend后面加四个参数,以空格分开将源颜色成以factorA与目标颜色成以factorB相加结果存入颜色缓冲区,但是混合通道用后两个参数来叠加

如Blend SrcAlpha OneMinusSrcAlpha。其中SrcAlpha为factorA,OneMinusSrcAlpha为factorB。
factorA,factorB以及alphaA值如下所示。

3.2 Blend参数释义

Blend参数意义
One/Zero 1/0
SrcColor/DstColor 源颜色值/目标颜色值
SrcAlpha/DstAlpha 源颜色透明通道值/目标颜色值透明通道值
OneMinusSrcAlpha/OneMinusDstAlpha 1-源颜色透明通道值/1-目标颜色值透明通道值
OneMinusSrcColor/OneMinusDstColor 1-源颜色值/1-源颜色值

如Blend SrcAlpha OneMinusSrcAlpha表示用源颜色的透明通道值乘以源颜色+目标颜色值乘以(1-源颜色透明通道值)。

3.3 Blend混合方式

上述所说的混合方式均为相加方式,是默认方式,也可以通过BlendOp命令更改为相减或者其他。如下所示:
BlendOp Sub//相减
Blend One One

还有RevSub,Min,Max等。

4.实例分析

4.1 透明度测试

透明度测试用于根据透明程度决定是否剔除此像素(也可以在不透明渲染时采用某一颜色值去处理)。此时要开启透明度测试,否则用像素alpha进行判断无效。如下所示,下述代码关闭了剔除功能,如果不需要看到物体内部,可以开启剔除,只渲染一面。

Shader "LL/AlphaTest"
{
	Properties
	{
		_MainTex("Main Texture",2D)="white"{}
		//_DissolveTex("Dissolve Cutoff",2D)="white"{}
		_Cutoff("Dissolve Cutoff",Range(0,1))=1
	}

	SubShader
	{
		Tags {"Queue"="AlphaTest" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}

		pass
		{			
			Tags {"LightMode"="ForwardBase"}
			Cull off
	
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			sampler2D _MainTex;
			//sampler2D _DissolveTex;
			float _Cutoff;

			struct a2v
			{
				float4 vertex:POSITION;
				float2 uv:TEXCOORD0;
			};

			struct v2f
			{
				float2 uv:TEXCOORD0;
				float4 vertex:SV_POSITION;
			};

			v2f vert(a2v v)
			{
				v2f f;
				f.uv=v.uv;
				f.vertex=UnityObjectToClipPos(v.vertex);
				return f;
			}

			float4 frag(v2f f):SV_TARGET
			{
				float4 mainTexColor=tex2D(_MainTex,f.uv);
				clip(mainTexColor.a-_Cutoff);

				//return float4(mainTexColor.rgb,mainTexColor.a * _Cutoff);
				return mainTexColor;
			}

			ENDCG
		}
	}
}

4.2 透明度混合

如果想通过更改alpha值更改模型透明度,则需要开启透明度混合。此时要关闭深度写入,如果需要深度值,可以在其他pass中开启深度写入,但不输出任何颜色值。

Shader "LL/AlphaBlend"
{
	Properties
	{
		_MainTex("Main Texture",2D)="white"{}
		//_DissolveTex("Dissolve Cutoff",2D)="white"{}
		_Cutoff("Dissolve Cutoff",Range(0,1))=1
	}

	SubShader
	{
		Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}

		// pass
		// {
		// 	Tags {"LightMode" = "ForwardBase"}
		// 	Cull Front
		// 	ZWrite On
		// 	ColorMask 0
		// }
		pass
		{			
			Tags {"LightMode"="ForwardBase"}
			//Cull Back
			//Cull Off
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			sampler2D _MainTex;
			//sampler2D _DissolveTex;
			float _Cutoff;

			struct a2v
			{
				float4 vertex:POSITION;
				float2 uv:TEXCOORD0;
			};

			struct v2f
			{
				float2 uv:TEXCOORD0;
				float4 vertex:SV_POSITION;
			};

			v2f vert(a2v v)
			{
				v2f f;
				f.uv=v.uv;
				f.vertex=UnityObjectToClipPos(v.vertex);
				return f;
			}

			float4 frag(v2f f):SV_TARGET
			{
				float4 mainTexColor=tex2D(_MainTex,f.uv);
				return float4(mainTexColor.rgb,mainTexColor.a * _Cutoff);
			}

			ENDCG
		}
	}
}

4.3 透明度混合+双面渲染

双面渲染可以在上述shader中直接Cull Off来实现,但是会看着不是很自然(如果不对比下述代码的效果时看不出来的),所以一般是先剔除正面,结束后再剔除背面。

Shader "LL/AlphaBlendTwoSides"
{
	Properties
	{
		_MainTex("Main Texture",2D)="white"{}
		//_DissolveTex("Dissolve Cutoff",2D)="white"{}
		_Cutoff("Dissolve Cutoff",Range(0,1))=1
	}

	SubShader
	{
		Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}

		// pass
		// {
		// 	Tags {"LightMode" = "ForwardBase"}
		// 	Cull Front
		// 	ZWrite On
		// 	ColorMask 0
		// }
		pass
		{			
			Tags {"LightMode"="ForwardBase"}
			Cull Front
			//Cull Off
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			sampler2D _MainTex;
			//sampler2D _DissolveTex;
			float _Cutoff;

			struct a2v
			{
				float4 vertex:POSITION;
				float2 uv:TEXCOORD0;
			};

			struct v2f
			{
				float2 uv:TEXCOORD0;
				float4 vertex:SV_POSITION;
			};

			v2f vert(a2v v)
			{
				v2f f;
				f.uv=v.uv;
				f.vertex=UnityObjectToClipPos(v.vertex);
				return f;
			}

			float4 frag(v2f f):SV_TARGET
			{
				float4 mainTexColor=tex2D(_MainTex,f.uv);
				return float4(mainTexColor.rgb,mainTexColor.a * _Cutoff);
			}

			ENDCG
		}

		pass
		{			
			Tags {"LightMode"="ForwardBase"}
			Cull Back
			//Cull Off
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			sampler2D _MainTex;
			//sampler2D _DissolveTex;
			float _Cutoff;

			struct a2v
			{
				float4 vertex:POSITION;
				float2 uv:TEXCOORD0;
			};

			struct v2f
			{
				float2 uv:TEXCOORD0;
				float4 vertex:SV_POSITION;
			};

			v2f vert(a2v v)
			{
				v2f f;
				f.uv=v.uv;
				f.vertex=UnityObjectToClipPos(v.vertex);
				return f;
			}

			float4 frag(v2f f):SV_TARGET
			{
				float4 mainTexColor=tex2D(_MainTex,f.uv);
				return float4(mainTexColor.rgb,mainTexColor.a * _Cutoff);
			}

			ENDCG
		}
	}
}

5.结语

半透明问题比较复杂,但实际操作起来却没有那么繁杂。

原文地址:https://www.cnblogs.com/llstart-new0201/p/11975807.html