透明是一个大坑,个人看法,游戏开发中能避免就避免,即便是真要做,但也应该尽量使用简单模型,个人理解。
一、单Pass的透明度效果
1.1 透明度测试:若大于阈值,保留片元,若小于直接剔除。
Shader "Custom/Test1"
{
Properties
{
//用于控制纹理的整体颜色表现
_Color("Color",Color)=(1,1,1,1)
_MainTex("基础纹理",2D)="white"{}
_Specular("Specular",Color)=(1,1,1,1)
_Gloss("Gloss",Range(0,256))=20
_AlphaTest("透明度测试阈值",float)=0.5
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
fixed _Gloss;
fixed _AlphaTest;
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:TEXCOORD3;
};
v2f vert(a2v v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos=mul(unity_ObjectToWorld,v.vertex);
o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal=normalize(i.worldNormal);
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//纹理采样
fixed4 texResult=tex2D(_MainTex,i.uv)*_Color;
//透明度测试,以纹理采样作为依据
clip(texResult.a-_AlphaTest);
fixed3 diffuse=_LightColor0*texResult
*(0.5*dot(worldLight,worldNormal)+0.5);
fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos);
fixed3 halfDir=normalize(viewDir+worldLight);
fixed3 specular=_LightColor0.rgb*_Specular
*pow(saturate(dot(worldNormal,halfDir)),_Gloss);
return fixed4(ambient+diffuse+specular,1);
}
ENDCG
}
}
}
1.2 透明度混合,关闭深度写入
Shader "Custom/Test1"
{
Properties
{
//用于控制纹理的整体颜色表现
_Color("Color",Color)=(1,1,1,1)
_MainTex("基础纹理",2D)="white"{}
_Specular("Specular",Color)=(1,1,1,1)
_Gloss("Gloss",Range(0,256))=20
_AlphaBlend("透明度混合阈值",float)=0.5
}
SubShader
{
Tags{"Queue"="Transparent" "IgnoreProjector"="True"}
Pass
{
Tags{"LightMode"="ForwardBase" }
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
fixed _Gloss;
fixed _AlphaBlend;
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:TEXCOORD3;
};
v2f vert(a2v v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos=mul(unity_ObjectToWorld,v.vertex);
o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal=normalize(i.worldNormal);
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//纹理采样
fixed4 texResult=tex2D(_MainTex,i.uv)*_Color;
fixed3 diffuse=_LightColor0*texResult
*(0.5*dot(worldLight,worldNormal)+0.5);
fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos);
fixed3 halfDir=normalize(viewDir+worldLight);
fixed3 specular=_LightColor0.rgb*_Specular
*pow(saturate(dot(worldNormal,halfDir)),_Gloss);
fixed4 color=fixed4(ambient+diffuse+specular,1);
return fixed4(color.rgb,texResult.a*_AlphaBlend);
//结果一样在某些情况下
return fixed4(color.rgb,_AlphaBlend);
}
ENDCG
}
}
}
Unity内部默认会剔除背面,所以我们看到的透明效果非常怪。
1.3 关闭深度写入,关闭剔除
Shader "Custom/Test1"
{
Properties
{
//用于控制纹理的整体颜色表现
_Color("Color",Color)=(1,1,1,1)
_MainTex("基础纹理",2D)="white"{}
_Specular("Specular",Color)=(1,1,1,1)
_Gloss("Gloss",Range(0,256))=20
_AlphaBlend("透明度混合阈值",float)=0.5
}
SubShader
{
Tags
{
"Queue"="Transparent" "IgnoreProjector"="True"
}
Pass
{
Tags
{
"LightMode"="ForwardBase"
}
ZWrite off
Cull Front
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
fixed _Gloss;
fixed _AlphaBlend;
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:TEXCOORD3;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
//纹理采样
fixed4 texResult = tex2D(_MainTex, i.uv) * _Color;
fixed3 diffuse = _LightColor0 * texResult
* (0.5 * dot(worldLight, worldNormal) + 0.5);
fixed3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
fixed3 halfDir = normalize(viewDir + worldLight);
fixed3 specular = _LightColor0.rgb * _Specular
* pow(saturate(dot(worldNormal, halfDir)), _Gloss);
fixed4 color = fixed4(ambient + diffuse , 1);
return fixed4(color.rgb, texResult.a * _AlphaBlend);
//结果一样在某些情况下
return fixed4(color.rgb, _AlphaBlend);
}
ENDCG
}
Pass
{
Tags
{
"LightMode"="ForwardBase"
}
ZWrite off
Cull back
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
fixed _Gloss;
fixed _AlphaBlend;
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:TEXCOORD3;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
//纹理采样
fixed4 texResult = tex2D(_MainTex, i.uv) * _Color;
fixed3 diffuse = _LightColor0 * texResult
* (0.5 * dot(worldLight, worldNormal) + 0.5);
fixed3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
fixed3 halfDir = normalize(viewDir + worldLight);
fixed3 specular = _LightColor0.rgb * _Specular
* pow(saturate(dot(worldNormal, halfDir)), _Gloss);
fixed4 color = fixed4(ambient + diffuse , 1);
return fixed4(color.rgb, texResult.a * _AlphaBlend);
//结果一样在某些情况下
return fixed4(color.rgb, _AlphaBlend);
}
ENDCG
}
}
}
1.4 透明度混合,开启深度写入,开启剔除
Shader "Custom/Test2"
{
Properties
{
//用于控制纹理的整体颜色表现
_Color("Color",Color)=(1,1,1,1)
_MainTex("基础纹理",2D)="white"{}
_Specular("Specular",Color)=(1,1,1,1)
_Gloss("Gloss",Range(0,256))=20
_AlphaBlend("透明度混合阈值",float)=0.5
}
SubShader
{
Tags{"Queue"="Transparent" "IgnoreProjector"="True"}
Pass
{
ZWrite On
ColorMask 0
}
Pass
{
Tags{"LightMode"="ForwardBase" }
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
fixed _Gloss;
fixed _AlphaBlend;
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:TEXCOORD3;
};
v2f vert(a2v v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos=mul(unity_ObjectToWorld,v.vertex);
o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 worldNormal=normalize(i.worldNormal);
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//纹理采样
fixed4 texResult=tex2D(_MainTex,i.uv)*_Color;
fixed3 diffuse=_LightColor0*texResult
*(0.5*dot(worldLight,worldNormal)+0.5);
fixed3 viewDir=normalize(_WorldSpaceCameraPos-i.worldPos);
fixed3 halfDir=normalize(viewDir+worldLight);
//fixed3 specular=_LightColor0.rgb*_Specular
// *pow(saturate(dot(worldNormal,halfDir)),_Gloss);
fixed4 color=fixed4(ambient+diffuse,1);
return fixed4(color.rgb,texResult.a*_AlphaBlend);
//结果一样在某些情况下
return fixed4(color.rgb,_AlphaBlend);
}
ENDCG
}
}
}
关于为什么要用ColorMask 0 ,它的作用是对通过写入遮罩阻止RGBA通道的写入。
当我们在Pass中什么CG代码都不写时,Unity会自动填充最低级的Shader ,即在屏幕上输出一坨内置默认的白色,我们在第二个Pass中做混合操作,自然不希望第一个Pass的颜色会产生干扰,为了防止内置Shader的干扰,使用ColorMask 0;
甚至连ZWrite On也可以不写,Unity默认开启了深度写入,除非你亲自关闭。
当我们写了CGPROGRAM和ENDCG,哪怕里面什么都没写,Unity也不会自动填充。那有些人可能说了,那我直接这样做不写ColorMask 0,然后CGPROGRAM里面什么也不写也行吧。答案是不行,当Unity检测到流水线什么也没有输入和输出时,会自动打上紫色表示Shader丢失。而且,你什么也不写,就意味着深度值也无法写入,因为流水线拿不到输入,缓冲区自然什么也没有。有些人可能又会说了,那我ColorMask 0和CGPROGRAM同时写呢,只能说你在质疑开发Unity的程序员的基本功,结果当然是Shader丢失输出紫色。
二、双Pass渲染的透明效果
2.1 透明度测试
没什么好说的,一句命令把剔除关了就行
Pass
{
...
Cull Off
...
}
2.2 透明度混合
透明度混合讲的就是一个先后顺序,对于这种只需要两个Pass,一个先渲染背面,一个正面。也没什么好说的,第一个Pass加一句Cull Front,第二个Pass加一句Cull Back,其他不变。
三、一些注意事项和问题
当你不开启透明度混合时,只是单纯调节片元着色器输出的Alpha值,这样不会有任何结果上的变化。
fixed4 frag(v2f i):SV_Target
{
fixed3 color=tex2D(_MainTex,i.uv.xy)*_Color;
return fixed4(color,0.5);
}
fixed4 frag(v2f i):SV_Target
{
fixed3 color=tex2D(_MainTex,i.uv.xy)*_Color;
return fixed4(color,1);
}
这两个代码没有任何区别,因为你没有开启透明度混合。
还有模型本身的问题:
这个模型就和立方体不一样,结果非常怪异。而左边是单Pass的结果,右边是双Pass的结果。至于左边的一片白,一片黑,这个目前猜测的结果混合不充分造成的影响。但我让深度测试一直通过,结果也没有变化。不知道为什么。
四、总结透明物体的渲染
为了实现真正意义上的透明,必须关闭模型剔除和深度写入,否则模型本身就会出现遮挡信息以及背面没有透明效果,至于是单Pass还是双Pass,看实际情况。
至于实现一些简单的透明效果,是否开启深度写入和剔除,看模型,看需求。尽量简单模型做透明,混合的效果非常吃模型本身。