unity shader学习---透明度测试,透明度混合

效果图

Alpha Test
在这里插入图片描述
Alpha Test With Both Side:Cull Off关闭剔除
在这里插入图片描述
Alpha Blend Scale = 0.5
在这里插入图片描述
AlphaBlendZWrite:新加一个Pass,Zwrite On, 按照像素级别的深度排序结果进行透明渲染
在这里插入图片描述

Alpha Blend With Both Side:两个Pass,Cull Front Cull Back
在这里插入图片描述

理论

  • 渲染不透明和半透明物体

景中既有不透明物体,又有半透明物体时,渲染引擎一般都会先对物体进行排序,再渲染
(1) 先渲染所有不透明物体,开启它们的深度测试和深度写入,渲染顺序为从前往后(Front-to-Back)
(2) 再渲染半透明物体,开启它们的深度测试,但关闭深度写入,按它们距离摄像机的远近进行排序,渲染顺序为从后往前(Back-to-Front)

【渲染不透明物体】
不考虑渲染顺序也能得到正确的排序效果,利用深度缓冲(z-buffer)解决可见性问题。
当渲染每个片元时, 需要把它的深度值和已经存在于深度缓冲中的值进行比较(如果开启了深度测试),如果它的值距离摄像机更远,那么说明这个片元不应该被渲染到屏幕上(有物体挡住了它);否则,这个片元应该覆盖掉此时颜色缓冲中的像素值,并把它的深度值更新到深度缓冲中(如果开启了深度写入)

为什么从前往后渲染?
减少深度颜色缓冲器的写入操作,提升性能,避免overdraw

【渲染透明物体】
通过控制透明通道 (Alpha Channel) 实现透明效果

1、透明度测试:
不需要关闭深度测试和深度写入
产生的效果很极端,要么完全透明,即看不到,要么完全不透明,就像不透明物体那样。只要片元的透明度不满足条件(通常是小千某个阙值),那么就会被舍弃。否则,就会按照普通的不透明物体的处理方式来处理它,即进行深度测试、深度写入等。

2、透明度混合:
开启深度测试,关闭深度写入(深度缓冲是只读的)
得到真正的半透明效果,使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。

为什么要关闭深度写入?
如果不关闭深度写入,半透物体后面的表面将会被剔除,就无法透过半透明表面看到后面的物体了

为什么要非常小心物体的渲染顺序?
为了渲染的正确性
在这里插入图片描述

如何排序?
尽可能让模型是凸面体,并且考虑将复杂的模型拆分成可以独立排序的多个子模型等

  • unity Shader的渲染顺序

在这里插入图片描述
透明度测试
在这里插入图片描述
透明度混合
在这里插入图片描述

Alpha Test

【效果】
在这里插入图片描述
在这里插入图片描述
默认剔除背面,在Pass里设置Cull Off关闭剔除
在这里插入图片描述
得到的透明效果在边缘处往往参差不齐有锯齿,这是因为在边界处纹理的透明度的变化精度问题
在这里插入图片描述
【代码解析】
跟渲染普通的不透明物体几乎是一样的
FS:增加了对透明度判断并裁剪片元

Properties{
		_Color("Color Tint", Color) = (1, 1, 1, 1) // Diffuse
		_MainTex("Main Tex", 2D) = "white" {}
		_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5 // cutoff
}
SubShader{
	Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"} 
	Pass {
			Tags { "LightMode" = "ForwardBase" } // 定义了正确的 LigbtMode,才能正确得到一些 Unity的内置光照变量,例如_LigbtColorO

透明度测试的subshader的固定三个标签
“Queue” = “AlphaTest” : 透明度测试使用的渲染队列
“IgnoreProjector” = “True”:这个 Shader 不会受到投影器(Projectors)的影响
“RenderType” = “TransparentCutout”: 该 Shader是个使用了透明度测试的Shader

fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff; //  范围在[O,1], 使用 fixed 来存储

和Properties 语义块中声明的属性建立联系

struct a2v {
	float4 vertex : POSITION;
	float3 normal : NORMAL;
	float4 texcoord : TEXCOORD0;
};

struct v2f {
	float4 pos : SV_POSITION;
	float3 worldNormal : TEXCOORD0;
	float3 worldPos : TEXCOORD1;
	float2 uv : TEXCOORD2;
};

VS略,同纹理映射
FS

fixed4 texColor = tex2D(_MainTex, i.uv);
clip(texColor.a - _Cutoff);
fixed3 albedo = texColor.rgb * _Color.rgb;

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));

clip CG 中的一个函数

if ((texColor.a - _Cutoff) < 0.0) {
  discard;
}

FallBack "Transparent/Cutout/VertexLit"

和之前使用的 Diffuse Specular 不同,能够保证SubShader 无法在当前显卡上工作时有合适的代替 Shader, 还可以保证使用透明度测试的物体可以正确地向其他物体投射阴影

Alpha Blend

alpha scale = 0.3
在这里插入图片描述
alpha scale = 0.8
在这里插入图片描述
【代码解析】
FS:把当前自身的颜色和已经存在于颜色缓冲中的颜色值进行混合

Properties{
		_Color("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex("Main Tex", 2D) = "white" {}
		_AlphaScale("Alpha Scale", Range(0, 1)) = 1 // 控制透明度
}
SubShader{
	Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}

使用了透明度混合的 Shader都应该在 SubShader 中设置这3个标签,透明度测试用的是TransparentCutout,透明度混合用的是Transparent

Pass {
	Tags { "LightMode" = "ForwardBase" } // 正确提供各个光照变量
	ZWrite Off // 深度只读
	Blend SrcAlpha OneMinusSrcAlpha // 

开启并设置了该Pass的混合模式(重要!!)
将源颜色(该片元着色器产生的颜色)的混合因子设为SrcAlpha, 把目标颜色(已经存在于颜色缓冲中的颜色)的混合因子设为OneMinusSrcAlpha
在这里插入图片描述
当设置混合状态时,我们实际上设置的就是混合等式中的操作和因子在这里插入图片描述
在这里插入图片描述
混合操作:
在这里插入图片描述
在这里插入图片描述

struct a2v {
	float4 vertex : POSITION;
	float3 normal : NORMAL;
	float4 texcoord : TEXCOORD0;
};

struct v2f {
	float4 pos : SV_POSITION;
	float3 worldNormal : TEXCOORD0;
	float3 worldPos : TEXCOORD1;
	float2 uv : TEXCOORD2;
};

VS略
FS:

fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb; // albedo
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale); 

先采样纹理贴图,然后设置该FS返回值中的透明通道 (= 纹理像素的透明通道* AlphaScale )

改进

开启深度写入的半透明效果

由于关闭深度写入,无法对模型进行像素级别的深度,当模型本身有复杂的遮挡关系或是包含了复杂的非凸网格的时候,就会有各种各样因为排序错误而产生的错误的透明效果
一种解决方法是分割网格
一种解决方法是使用两个Pass来渲染模型,第一个 Pass 开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中;第二个Pass 进行正常的透明度混合,但是对性能有一定影响

// 新加一个Pass
Pass{
	Zwrite On
	ColorMask 0 // 
}

ColorMask 用于设置颜色通道的写掩码 write mask,0代表 Pass 不写入任何颜色通道,即不会输出任何颜色

效果:
在这里插入图片描述
在这里插入图片描述

双面渲染的透明效果

默认情况下渲染引擎剔除了物体背面(相对于摄像机的方向)的渲染图元,而只渲染了物体的正面
Cull Back I Front I Off
如果设置为 Off,就会关闭剔除功能,那么所有的渲染图元都会被渲染,计算量成倍增加,因此除非是用于特殊效果,例如这里的双面渲染的透明效果,通常是不会关闭剔除功能的

  • 透明度测试的双面渲染
    Cull Off
  • 透明度混合的双面渲染
    分成两个 Pass——第一个 Pass 渲染背面(Cull Front),第二个 Pass只渲染正面(Cull Back),由于 Unity 会顺序执行 SubShader 中的各个 Pass, 从而可以保证先渲染背面,后渲染正面,渲染关系正确
    对比:
    在这里插入图片描述
    在这里插入图片描述

代码部分

【alpha test】

Shader "Custom/AlpbaTest"
{
	Properties{
			_Color("Color Tint", Color) = (1, 1, 1, 1) // Diffuse
			_MainTex("Main Tex", 2D) = "white" {}
			_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5 // cutoff
	}
	SubShader{
		// 透明度测试的subshader的固定三个标签
		// AlphaTest: 透明度测试使用的渲染队列
		// TransparentCutout:该 Shader是个使用了透明度测试的Shader
		// "IgnoreProjector" = "True":这个 Shader 不会受到投影器(Projectors)的影响
		Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"} 
		
		Pass {
			Tags { "LightMode" = "ForwardBase" } // 定义了正确的 LigbtMode,才能正确得到一些 Unity的内置光照变量,例如_LigbtColorO

			Cull Off // 关闭剔除,看到背面

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"

			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _Cutoff; //  范围在[O I], 使用 fixed 来存储

			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};

			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
			};

			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);

				o.worldNormal = UnityObjectToWorldNormal(v.normal);

				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

				fixed4 texColor = tex2D(_MainTex, i.uv);

				// Alpha test
				clip(texColor.a - _Cutoff);
				// Equal to 
//				if ((texColor.a - _Cutoff) < 0.0) {
//					discard;
//				}

				fixed3 albedo = texColor.rgb * _Color.rgb;

				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));

				return fixed4(ambient + diffuse, 1.0);
			}

			ENDCG
		}
	}
			FallBack "Transparent/Cutout/VertexLit"
}

【Alpha Blend】

// Q1
// 当模型本身有复杂的遮挡关系或是包含了复杂的非凸网格的时候,就会有各种各样因为排序错误而产生的错误的透明效果。
// 新加一个 Pass,开启深度写入,但不输出颜色; 第二个Pass 进行正常的透明度混合
// 就可以按照像素级别的深度排序结果进行透明渲染,但是对性能有一定的影响

// Q2 双面渲染
// 第一个 Pass 渲染背面,第二个 Pass只渲染正面
// 保证正确的深度渲染关系


Shader "Custom/AlphaBlend"
{
	Properties{
			_Color("Color Tint", Color) = (1, 1, 1, 1)
			_MainTex("Main Tex", 2D) = "white" {}
			_AlphaScale("Alpha Scale", Range(0, 1)) = 1 // 控制透明度
	}
	SubShader{
		// 通常,使用了透明度混合的 Shader都应该在 SubShader 中设置这3个标签,透明度测试用的是TransparentCutout
		Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
		

		// 新加一个Pass,按照像素级别的深度排序结果进行透明渲染,但是对性能有一定的影响
		Pass{
			Zwrite On
			ColorMask 0 // ColorMask 用于设置颜色通道的写掩码 write mask,0代表 Pass 不写入任何颜色通道,即不会输出任何颜色
		}
		
		Pass {
			Tags { "LightMode" = "ForwardBase" } // 正确提供各个光照变量
			// Cull Off
			ZWrite Off // 深度只读
			Blend SrcAlpha OneMinusSrcAlpha // 开启并设置了该Pass的混合模式[重要],将源颜色(该片元着色器产生的颜色)的混合因子设为SrcAlpha, 把目标颜色(已经存在于颜色缓冲中的颜色)的混合因子设为OneMinusSrcAlpha
			// BlendOp Min

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"

			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _AlphaScale;

			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};

			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
			};

			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);

				o.worldNormal = UnityObjectToWorldNormal(v.normal);

				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

				fixed4 texColor = tex2D(_MainTex, i.uv);

				fixed3 albedo = texColor.rgb * _Color.rgb; // albedo

				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));

				return fixed4(ambient + diffuse, texColor.a * _AlphaScale); // 纹理像素的透明通道 * _AlphaScale
			}

			ENDCG
		}
	}
	FallBack "Transparent/VertexLit"
}

【Alpha Blend With Both Side】

Shader "Custom/Alpha Blend With Both Side" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
	}
	SubShader {
		Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
		
		Pass {
			Tags { "LightMode"="ForwardBase" }
			
			// First pass renders only back faces 
			Cull Front
			
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha
			
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _AlphaScale;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				
				fixed4 texColor = tex2D(_MainTex, i.uv);
				
				fixed3 albedo = texColor.rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
				
				return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
			}
			
			ENDCG
		}
		
		Pass {
			Tags { "LightMode"="ForwardBase" }
			
			// Second pass renders only front faces 
			Cull Back
			
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha
			
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _AlphaScale;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				
				fixed4 texColor = tex2D(_MainTex, i.uv);
				
				fixed3 albedo = texColor.rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
				
				return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
			}
			
			ENDCG
		}
	} 
	FallBack "Transparent/VertexLit"
}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值