unity-shader-透明与深度
透明渲染 与 深度测试 的爱恨情仇.
前篇
- 透明物体的深度写入问题 - https://www.jianshu.com/p/e953e937e210
透明渲染 ZWrite 的 On, Off 对比
左右两边都是交叉的面片. 红色面片 距离摄像机的距离比 蓝色面片 的 远,
后面白色 cube 是用来做参照物.
因为在 透明类型 渲染时, 渲染顺序是 距离远的先渲染, 距离近的后渲染. 所以 红色面片 会先渲染, 蓝色的后渲染.
深度测试默认是 ZTest LEqual
左边 关闭 深度写入.
先渲染的 红色面片 的深度没有写入到 深度缓冲区, 所以之后渲染 蓝色面片 时, 深度测试时 全部像素 LEqual 都会成立, 最后直接和颜色缓冲去混合.
右边 开启 深度写入.
先渲染的 红色面片 的深度写入到了 深度缓冲区, 所以之后渲染 蓝色面片 时, 深度测试时 左边的一半像素 LEqual 成立, 右边一半的像素大部分 LEqual 不成立, 所以右边不成立的像素都会被丢弃, 所以看到的结果就是 红色与 白色cube 的混合.
增加 pass 专门写入深度
左边两个和上面一样的 shader
第三个 ZWrite Pass 是在 正常渲染 之前 专门增加了一个 pass 来只写入 深度值 到 深度缓冲区, 看起来已经稍微正常了, 因为可以与后面的物体正常混合, 且自身不会出现透视的情况. 代价是话多一个 pass.
因为 增加了写入深度的pass, 所以正常渲染时, 对比深度时, 鸟 的全部像素 LEqual 成立, 楼 的部分被 鸟 挡住, 所以这部分会被丢弃. 所以最终看到的效果就是 鸟直接后 红色背景 混合.
shader
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
58Shader "ITS/Unity/Unlit/Unlit-Transparent_ZWritePass" {
Properties {
_TintColor("Tint Color", Color) = (1, 1, 1, 1)
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader {
Tags {"Queue"="Transparent" "RenderType"="Transparent"}
LOD 100
Cull Back
Pass
{
ZWrite On
ColorMask 0 // 不写入颜色缓冲区
}
Pass {
ZWrite Off // 第一个 pass 已经写入了深度, 这里就没必要再写了
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _TintColor;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.texcoord) * _TintColor;
return col;
}
ENDCG
}
}
}
透明渲染写深度总结
不写深度
a. 当一个物体自身没有穿插,两个物体间没有穿插时,不写深度没有问题。b. 当两个物体间有穿插时,会导致一个物体完全挡住另外一个物体,与现实不符。
写深度
a. 当一个物体自身有穿插时,使用两个pass,才可以保证透明正常,且不会透过物体的正面看到后面的面。b. 但是写了深度后,这两个物体穿插时,会导致不能透过一个角色看到另外一个角色。
最后
- 正常情况下,透明物体渲染不需要写深度
- 特殊情况下,根据需求才写深度,虽然会带来小问题,但也还可以接受。
透明物体实现阴影投射
如果直接用内置的 shader 的话, 是没有阴影投射的
因为没有走 shadowcast 的 pass
需要自定义 透明的 shader, 直接利用 回滚到 Diffuse 利用里面的 shadowcast pass
1 | // 渲染阴影 |