Unity 中实现透明效果一般有两种方法
透明度测试(Alpha Test)
原理:只要片元的透明度不符合要求就直接舍弃
不关闭深度写入(ZWrite)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| Shader "AlphaTest"{ Properties{ _MainTex ("Main Tex", 2D) = "white" {} _Color ("Color", Color) = (1, 1, 1, 1) _Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5 //控制剔除的阈值 } SubShader { Tags { "Queue"="AlphaTest" "IgnoreProjection"="True" "RanderType"="TransparentCutout"}
Pass{ Tags { "LightMode"="ForwardBase" } Cull Off //用于双面渲染,不剔除背对摄像机的图元 CGPROGRAM
#pragma vertex vert #pragma fragment frag
#include "Lighting.cginc"
struct appdata{ float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; };
struct v2f{ float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 worldPos : TEXCOORD1; float3 worldNormal : TEXCOORD2; };
sampler2D _MainTex; float4 _MainTex_ST; fixed _Cutoff; fixed4 _Color;
v2f vert (appdata v){ v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal); return o; }
fixed4 frag (v2f i) : SV_Target{ float3 worldNormal = normalize(i.worldNormal); float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
float4 texColor = tex2D(_MainTex, i.uv); clip(texColor.a - _Cutoff); //Equal to //if( (texColor.a - _Cutoff) < 0.0 ){ // discard; //} float3 albedo = texColor.rgb * _Color.rgb;
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
float3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, 1.0); } ENDCG } } FallBack "Transparent/Cutout/VertexLit" }
|
透明度测试可以用于消解动画效果的实现
透明度混合(Alpha Blending)
原理:当前片元的透明度作为混合参数与颜色缓冲中的颜色值混合
关闭深度写入,但不关闭深度测试
关闭深度写入将导致渲染顺序变得非常重要
一般做法:
- 先渲染所有不透明物体,同时开启深度写入和深度测试
- 半透明物体按距离远近排序,然后从后往前渲染,开启深度测试,关闭深度写入
由于排序是对于物体来说的,当物体间循环重叠时就需要用分割物体、分割网格的方法来解决
- 尽可能让模型是凸面体
- 让透明通道更柔和,难以分辨
- 使用开启深度写入的半透明效果近似物体半透明
对于双面渲染的透明度混合,由于关闭了深度写入,在渲染时需要控制渲染顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| SubShader { Tags {"Queue"="Transparent" "IgnoreProjection"="True" "RanderType"="Transparent"} ZWrite Off Blend SrcAlpha OneMinusSrcAlpha //开启混合模式,设定混合因子 Pass { ... Cull Front //剔除正面,渲染背面 ... } Pass { ... Cull Back //提出背面,渲染正面 ... } }
|
半透明物体的阴影没了,暂时不知道原因
深度写入的半透明效果
使用两个 Pass
- 第一个开启深度写入但不输出颜色
- 第二个进行透明度混合
由于深度信息是对像素来说的,所以如果单个模型本身存在自我遮挡,那么被遮挡部分就直接舍弃了
ColorMask RGB | A | 0 | ...
表示当前 Pass 写入的颜色通道
Unity预定义渲染队列
由SubShader
的Queue
标签定义:Tags { "Queue"="AlphaTest" }
名称 |
索引号 |
描述 |
Background |
1000 |
最先开始渲染的队列,一般用于绘制于背景上的物体 |
Geometry |
2000 |
默认队列,不透明物体 |
AlphaTest |
2450 |
透明度测试,在不透明物体之后渲染可以更高效 |
Transparent |
3000 |
透明度混合,从后往前的顺序进行渲染 |
Overlay |
4000 |
用于最后渲染的物体,实现一些叠加效果 |
索引号越小越早被渲染
混合
混合不可编程但高度可配置
混合源颜色(当前片元)和目标颜色(颜色缓冲)
Blend SrcFactor DstFactor
: 只混合rgb通道
Blend SrcFactor DstFactor,SrcFactorA DstFactorA
: 上面的基础上,再混合 a 通道
例子:
Blend SrcAlpha OneMinusSrcAlpha
:开启混合,并且混合结果是 $Src.a * Src.rgb + (1-Src.a)*Des.rgb$
还可以配置混合操作
BlendOp BlendOperation
即改变源颜色和目标颜色最后的混合方式,默认为 Add
操作 |
描述 |
Add |
加法 |
Sub |
减法 |
RevSub |
交换减法 |
Min |
取各分量的最小值 |
Max |
取各分量的最大值 |
同时使用上面两种方法可设定混合类型
类型 |
描述 |
正常 |
Blend SrcAlpha OneMinusSrcAlpha |
柔和相加 |
Blend OneMinusDstAlpha One |
正片叠底 |
Blend DstColor Zero |
两倍相乘 |
Blend DstColor SrcColor |
变暗 |
BlendOp Min Blend One One |
变亮 |
BlendOp Max Blend One One |
滤色 |
Blend OneMinusDstColor One |
线性减淡 |
Blend One One |