US4L3——遮挡半透明效果
US4L3——遮挡半透明效果
遮挡半透明效果
在游戏开发中,遮挡半透明效果就是物体被挡住的部分,也能呈现出一种半透明效果而被看到(具体效果可以自定义)
比如当角色在建筑物之间穿行时,被遮挡部分能够呈现出半透明效果而被我们看到。
遮挡半透明效果包含了两种显示效果,即挡住和没被挡住的部分,没被遮挡的部分正常显示,被遮挡的部分自定义显示(半透明、X光等等)
遮挡半透明效果的基本原理
一句话描述它的基本原理:
两个 Pass
渲染对象,一个 Pass
用于渲染被遮挡部分,一个 Pass
用于渲染未遮挡模型。
被遮挡部分通过修改深度测试规则和关闭深度写入达到目的。
关键点:
- 如何渲染遮挡部分
- 如何渲染未遮挡部分
首先进行一个关于 深度测试 和 深度写入 的知识回顾,
我们可以设置 Pass
的深度测试规则,只有通过了深度测试,该 Pass
才会执行进行渲染
1 | ZTest Less //小于当前深度缓冲中的值,就通过测试,写入到深度缓冲中 |
不设置的话,默认为 ZTest LEqual
,即小于等于当前深度缓冲中的值,就通过测试,
我们可以使用 ZWrite On
或 ZWrite Off
来决定是否将通过深度测试的值写入到缓冲区
因此,借助对深度测试与深度写入的修改,上文的两个关键点的实现方式如下:
-
如何渲染遮挡部分
在第一个
Pass
中,我们按照想要的遮挡效果去实现 Shader 即可(比如半透明或X射线效果)
最关键的点,是需要修改该 Pass
的深度测试规则为 ZTest Greater
,并且关闭深度写入!-
修改深度测试的目的:只有自己的深度值大于深度缓冲中的值才渲染,相当于只有前方有遮挡时才渲染
-
关闭深度写入的目的:为例防止被遮挡的部分后续的
Pass
通过测试如果前方有遮挡,又写入较大的深度值,执行第二个
Pass
时会通过深度写入,
导致第二个Pass
的内容通过深度测试,呈现出未被遮挡的错误效果!错误的效果如下:
1
2
3
4
5Pass
{
ZTest Greater
ZWrite Off
}此
Pass
应当实现的效果:
-
-
如何渲染未遮挡部分
在第二个
Pass
中,我们只需要正常按需求(是否受光照影响等)渲染模型即可,不需要做什么特别处理
自定义半透明效果
普通的半透明效果非常容易实现,只需要设置混合因子即可。而要实现类似 X 射线的效果,也就是边缘不透明,中间透明的效果
我们可以利用之前学习过的菲尼尔反射的公式来得到,详见:US3S8L6——菲涅尔反射
Schlick 菲涅耳近似等式为:
其中:
- 表示入射角为 时的反射率
- 是垂直入射某介质时的反射率
- 是视角方向单位向量(入射角)
- 是顶点法线单位向量
将得到的入射角为 的反射率作为自定义颜色的 A 值,便可以得到类似的结果。
遮挡半透明效果的具体实现 —— 实现基础半透明效果
-
新建 Shader 取名
BaseOcclusionTransparent
删除无用代码 -
声明属性 映射属性
- 主纹理
- 透明度
1
2
3
4
5
6
7
8Properties
{
_Color("MainColor", Color) = (1, 1, 1, 1)
_MainTex("MainTex", 2D) = ""{}
_BumpMap("BumpMap", 2D) = ""{}
_BumpScale("BumpScale", Range(0, 1)) = 1
_CoveredAlpha("CoveredAlpha", Range(0, 1)) = 0.5
} -
复制一个
Pass
-
第一个
Pass
-
关键点 深度测试改为大于 关闭深度写入 传统透明混合因子
-
ZTest Greater
-
ZWrite Off
-
Blend SrcAlpha OneMinusSrcAlpha
混合后的颜色 = (源颜色源Alpha) + (目标颜色 (1 − 源Alpha))
-
-
传统纹理采样实现,在片元着色器返回颜色中透明通道用透明度变量
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
37Pass
{
ZTest Greater
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _CoveredAlpha;
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 color = tex2D(_MainTex, i.uv);
return fixed4(color.rgb, _CoveredAlpha);
}
ENDCG
} -
-
第二个
Pass
,使用传统纹理采样实现这里直接复用标准漫反射的两个Pass,代码详见:US3S7L1——标准漫反射Shader
1
2
3
4
5// 基础渲染通道 Base Pass
UsePass "TeachShader/BumpedDiffuse/BasePass"
// 附加渲染通道 Additional Pass
UsePass "TeachShader/BumpedDiffuse/AdditionalPass"
完整 Shader 代码如下:
1 | Shader "TeachShader/BaseOcclusionTransparent" |
显示效果(透明度0.3):
可见,被遮挡部分呈现出了半透明效果,使得我们可以看到被遮挡部分的颜色
遮挡半透明效果的具体实现 —— 实现X射线半透明效果
-
新建 Shader 取名
XRayOcclusionTransparent
,删除无用代码 -
声明属性 映射属性
- 主纹理
- 自定义颜色
- 菲涅尔反射率
- 菲涅尔 n 次方
1
2
3
4
5
6
7
8
9
10Properties
{
_Color("MainColor", Color) = (1, 1, 1, 1)
_MainTex("MainTex", 2D) = ""{}
_BumpMap("BumpMap", 2D) = ""{}
_BumpScale("BumpScale", Range(0, 1)) = 1
_FresnelScale("FresnelScale", Range(0, 1)) = 1 // 菲涅尔反射垂直角度的反射率
_FresnelPow("FresnelPow", Range(0, 5)) = 5 // 菲涅尔反射近似公式中的n次方
_CoveredColor("CoveredColor", Color) = (1, 1, 1, 1) // 被遮挡部分显示的颜色
} -
复制一个
Pass
-
第一个
Pass
-
关键点 深度测试改为大于 关闭深度写入 传统透明混合因子
-
ZTest Greater
-
ZWrite Off
-
Blend SrcAlpha OneMinusSrcAlpha
混合后的颜色 = (源颜色源Alpha) + (目标颜色 (1 − 源Alpha))
-
-
结构体中
顶点、世界空间法线、世界空间视角方向
-
顶点着色器
顶点变换、得到世界空间法线、世界空间视角方向
-
片元着色器
利用菲涅尔公式带入自定义参数计算,将得到的值作为自定义颜色的透明通道值
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// 被遮挡部分渲染通道 Covered Pass
Pass
{
ZTest Greater
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
struct v2f
{
float4 pos : SV_POSITION; // 裁剪空间下顶点坐标
float3 worldNormal: NORMAL; // 世界空间下的法线
float3 worldViewDir: TEXCOORD0; // 世界空间下的视角方向
};
fixed _FresnelScale;
fixed _FresnelPow;
fixed4 _CoveredColor;
v2f vert (appdata_base v)
{
v2f data;
data.pos = UnityObjectToClipPos(v.vertex); // 顶点坐标转裁剪坐标
data.worldNormal = UnityObjectToWorldNormal(v.normal); // 顶点法线转世界坐标
fixed3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 顶点坐标装世界坐标
data.worldViewDir = UnityWorldSpaceViewDir(worldPos); // 计算世界空间下视角所在方向
return data;
}
fixed4 frag (v2f i) : SV_Target
{
// 根据schlick菲涅尔近似公式,计算菲涅尔反射率
fixed fresnal =
_FresnelScale + (1 - _FresnelScale) * pow((1 - dot(normalize(i.worldViewDir), normalize(i.worldNormal))), _FresnelPow);
return fixed4(_CoveredColor.rgb, fresnal);
}
ENDCG
} -
-
第二个Pass,传统纹理采样实现
这里直接复用标准漫反射的两个Pass,代码详见:US3S7L1——标准漫反射Shader
1
2
3
4
5// 基础渲染通道 Base Pass
UsePass "TeachShader/BumpedDiffuse/BasePass"
// 附加渲染通道 Additional Pass
UsePass "TeachShader/BumpedDiffuse/AdditionalPass"
显示效果(菲涅尔反射率为0.1,菲涅尔n次方为5):
可见,我们可以自定义被遮挡部分的渲染效果