透明度混合:这种方法可以得到真正的半透明效果。它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入,这使得我们非常小心物体的渲染顺序。
为了进行混合,我们需要使用Unity提供的混合命令——Blend。Blend是Unity提供的设置混合模式的命令。想要实现半透明的效果就需要把当前自身的颜色和已经存在于颜色缓冲中的颜色值进行混合,混合时使用的函数就是由该指令决定的。
语义 | 描述 |
Blend Off | 关闭混合 |
Blend SrcFactor DstFactor | 开启混合,并设置混合因子。源颜色(该片元产生的颜色)会乘以SrcFactor,而目标颜色(已经存在于颜色缓存的颜色)会乘以DstFactor, 然后把两者相加后再存入颜色缓冲中 |
Blend SrcFactor DstFactor, SrcFactorA DstFactorA | 和上面几乎一样,只是使用不同的因子来混合透明通道 |
BlendOp BlendOperation | 并非是把源颜色和目标颜色简单相加后混合,而是使用BlendOperation对它们进行其他操作 |
Unity在我们使用Blend命令的时候就自动帮我们打开了混合模式。
Blend SrcAlpha(源颜色的混合因子) OneMinusSrcAlpha(目标颜色的混合因子)
经过混合后新的颜色是:DstColornew=SrcAlpha×SrcColor+(1-SrcAlpha)×DstColorold
Alpha混合新颜色计算公式:
(new_r,new_g,new_b,new_a) = (src_r*SrcFactorR + dst_r*DstFactorR, src_g*SrcFactorG + dst_g*DstFactorG, src_B*SrcFactorB + dst_b*DstFactorB, src_a*SrcFactorA + dst_a*DstFactorA)
Shader代码
Shader "Custom/Chapter8-AlphaBlend" { Properties { _Color ("Main Tint", Color) = (1, 1, 1, 1) _MainTex ("Base (RGB)", 2D) = "white" {} _AlphaScale ("Alpha Scale", Range(0, 1)) = 1 } SubShader { //RenderType标签通常被用于着色器替换功能。 Tags { "Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent" } LOD 200 Pass { Tags {"LightMode"="ForwardBase"} ZWrite Off Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; //用于在透明纹理的基础上控制整体的透明度。 fixed _AlphaScale; struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; //Unity会将模型的第一组纹理坐标存储到该变量中 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 = mul(UNITY_MATRIX_MVP, v.vertex); o.worldNormal = mul(v.normal, (float3x3)_World2Object); //Unity5中可用UnityObjectToWorldNormal()函数得到o.worldNormal //o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(_Object2World, v.vertex).xyz; o.uv = v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw; //或者调用Unity内建的函数计算o.uv //o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } fixed4 frag(v2f i) : COLOR { fixed3 worldNormal = normalize(i.worldNormal); //fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);//只适合场景中仅有一个平行光 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)); //只有使用Blend命令打开混合后,我们在这里设置透明通道才有意义, //否则,这些透明度并不会对片元的透明效果有任何影响。 //透明通道: 纹理像素的透明通道和材质参数_AlphaScale的乘积。 return fixed4(ambient + diffuse, texColor.a * _AlphaScale); } ENDCG } } //确保我们编写的SubShader无法在当前显卡上工作时可以有合适的代替Shader, //还可以保证使用透明度测试的物体可以正确地向其他物体投射阴影 FallBack "Transparent/Cutout/VertexLit" }
效果
由于关闭深度写入带来的各种问题。当模型本身有复杂的遮挡关系或是包含了复杂的非凸网格的时候,就会有各种各样因为排序错误而产生的错误的透明果。这都是由于我们关闭了深度写入造成的,因为这样我们就无法对模型进行像素级别的深度排序。一种解决方法是分割网格,从而可以得到一个“质量优等”的网格。但是很多情况下往往是不切实际的。这时,我们可以想办法重新利用深度写入,让模型可以像半透明物体一样进行淡入淡出。
Shader "Custom/Transparent" {
Properties{
_MainTex("Base (RGB)", 2D) = "white" {}
_Color("Tint", Color) = (1,1,1,1)
}
SubShader{
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
LOD 100
Lighting Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
v2f vert(appdata_t v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.texcoord) * _Color;
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}