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"
}

透明度测试可以用于消解动画效果的实现

2021711-134249

2021711-134837

透明度混合(Alpha Blending)

原理:当前片元的透明度作为混合参数与颜色缓冲中的颜色值混合

关闭深度写入,但不关闭深度测试


关闭深度写入将导致渲染顺序变得非常重要

一般做法:

  1. 先渲染所有不透明物体,同时开启深度写入和深度测试
  2. 半透明物体按距离远近排序,然后从后往前渲染,开启深度测试,关闭深度写入

由于排序是对于物体来说的,当物体间循环重叠时就需要用分割物体、分割网格的方法来解决

  • 尽可能让模型是凸面体
  • 让透明通道更柔和,难以分辨
  • 使用开启深度写入的半透明效果近似物体半透明

对于双面渲染的透明度混合,由于关闭了深度写入,在渲染时需要控制渲染顺序

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 //提出背面,渲染正面
...
}
}

2021711-135624

半透明物体的阴影没了,暂时不知道原因

深度写入的半透明效果

使用两个 Pass

  • 第一个开启深度写入但不输出颜色
  • 第二个进行透明度混合

由于深度信息是对像素来说的,所以如果单个模型本身存在自我遮挡,那么被遮挡部分就直接舍弃了

ColorMask RGB | A | 0 | ...表示当前 Pass 写入的颜色通道

Unity预定义渲染队列

SubShaderQueue标签定义: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