1060 words
5 minutes
UnityShader学习笔记-透明-透明度混合
透明度混合(Alpha Blending)是一种常见的透明效果,它可以实现物体真正的半透明效果,即部分透明、部分不透明。 与透明度测试不同,透明度混合不会直接舍弃片元,而是使用混合函数(Blend)将当前片元的颜色与颜色缓冲中的已有颜色按比例合成。其原理是:**根据当前片元的透明度(Alpha 值),与已经存储在颜色缓冲中的颜色值进行混合,并在混合前先关闭深度写入(ZWrite Off)。**混合的计算方式可以用下式表示:
其中:
- 当前片元颜色:
- 当前片元透明度:
- 颜色缓冲中的已有颜色:
透明度混合着色器实现
在Unity中,通常使用 Blend命令实现来进行透明度混合。在Unity提供的ShaderLab中提供了四种混合命令的语义,如下表所示:
| 语义 | 定义 |
|---|---|
| Blend Off | 关闭混合 |
| Blend SrcFactor DstFactor | 开启混合,统一设置指定颜色的源/目标混合因子 |
| Blend SrcFactor DstFactor, SrcFactorA DstFactorA | 开启混合,分别设置并指定颜色的源/目标的混合因子 |
| BlendOp BlendOperation | 开启混合,指定混合运算方式(加法、减法等) |
属性
_Color ("Main Tint", Color) = (1, 1, 1, 1)_MainTex ("主纹理", 2D) = "white" {}_AlphaScale ("Alpha Scale", Range(0, 1)) = 1在着色器中定义了纹理属性:颜色、主贴图、透明度缩放
- 颜色:用于描述物体自身颜色
- 主纹理:用于描述物体的漫反射颜色的纹理图片,默认是一个纯白的纹理
- 透明度缩放:整体控制纹理像素的透明度
SubShader语义中的标签(Tags)设置
Tags{ "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}与透明度测试设置的标签不同,在透明度混合中,我们定义了3个标签:
"Queue" = "Transparent":放到透明队列中绘制(即在所有不透明物体之后渲染)"IgnoreProjector" = "True":不受 Projector 投影影响"RenderType" = "Transparent":标记当前Shader渲染出来的物体属于透明物体类别
Pass中透明度混合设置
//关闭深度写入ZWrite Off//开启混合模式Blend SrcAlpha OneMinusSrcAlpha因为透明度混合需要关闭深度写入,因此,透明度混合的Pass中需要添加 ZWrite Off,并同时开启Unity的混合模式 Blend SrcAlpha OneMinusSrcAlpha,在这里选用了 OneMinusSrcAlpha,即最经典的半透明混合公式,也就是取作为目标混合因子来参与透明度混合。
着色器变量
fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaScale;- 使用
sampler2D表示纹理资源 - 使用
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);}效果
从视频中可以看到,随着 AlphaScale 数值的变化,物体的透明度会平滑变化:
- Alpha = 1(完全不透明)时,新片元完全覆盖背景
- Alpha = 0(完全透明)时,保留背景颜色,不显示新片元
- 0 < Alpha < 1 时,新片元与背景颜色按比例叠加,呈现出半透明效果
这种平滑混合方式,与透明度测试的效果不同,能更好地表现玻璃、水面等材质的真实质感。
Reference
UnityShader学习笔记-透明-透明度混合
https://jerryym.github.io/posts/unityshader/unityshader学习笔记-透明-透明度混合/