Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

开启深度写入的半透明效果

一、开启深度写入的半透明效果是用来处理什么需求的

对于结构比较复杂的模型 使用之前的透明混合 Shader 会由于关闭了深度写入 会产生错误的渲染效果

如下: 2024-09-13 154309.png

虽然可以通过拆分模型的方式解决部分问题 但是对于一些结构复杂的模型,拆分模型的方式会增加工作量

因此我们可以采用 开启深度写入的半透明 Shader 来优化效果

二、基本原理

使用两个 Pass 渲染通道老处理渲染逻辑

  • 第一个 Pass:开启深度写入,不输出颜色 目的是让该模型各个片元的深度值能写入深度缓冲
  • 第二个 Pass:进行正常的透明度混合(和透明度混合的步骤一样)

这样,执行第一个 Pass 时,会执行深度测试,并进行深度写入 如果此时该片元没有通过深度测试会直接丢弃,不会再执行第二个 Pass 此时对于同一个模型中处于同一位置的片元们,会进行该位置的深度测试再决定渲染哪个片元

如何不输出颜色? 主要使用 ColorMask 颜色遮罩 这种渲染状态(命令) 主要用于控制颜色分量是否写入颜色缓冲区 ColorMask RGBA 表示写入颜色的 RGBA 通道 ColorMask 0 表示不写入 ColorMask RB 表示写入红蓝通道

注意:

  1. 开启深度写入的半透明效果,模型内部之间不会有任何半透明效果(因为模型内部深度较大的片元会被丢弃)
  2. 由于有两个 Pass 渲染通道,因此它会带来一定的性能开销

三、具体实现

1.实现思路

  1. 我们复制透明度混合的 shader 代码
  2. 在 subshader 中之前的 Pass 渲染通道前面加一个 Pass 渲染通道
  3. 在新加的 Pass 渲染通道中开启深度写入,并且使用 colorMask 0 颜色遮罩的渲染命令,不输出颜色

2.实例代码

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
Shader "Unlit/ZWriteTransparent"
{
Properties
{
//光照相关属性
_MainColor("MainColor",Color) = (1,1,1,1)
_specularColor("specularColor",Color) = (1,1,1,1)
_specularNum("specularNum",Range(0,20)) = 15
//单张纹理
_MainTex("MainTex",2D) = ""{}
//法线纹理
_BumpMap("BumpMap",2D) = ""{}
_BumpMapScale("BumpMapScale",Float) = 1
//透明度
_AlphaScale("AlphaScale",Range(0,1)) = 1

}
SubShader
{
//设置透明度混合的 渲染队列 为 透明的Transparent
Tags{"Queue" = "Transparent" "IngnoreProjector" = "true" "RenderType" = "Transparent"}
//主要就是进行深度写入 但是不输出颜色
Pass
{
ZWrite On
ColorMask 0
}
Pass
{
Tags { "LightMode"="ForwardBase" }
//关闭深度写入
ZWrite off
//将混合因子 设置为半透明效果 的搭配
Blend SrcAlpha OneMinusSrcAlpha

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
#include "Lighting.cginc"

struct v2f
{
float4 pos:POSITION;
//我们可以单独申明两个float2成员 用于记录 颜色和法线的uv坐标
/* float2 uvTex:TEXCOORD0;
float2 uvBump:TEXCOORD1; */
//我们也可以直接申明一个float4成员
//xy表示颜色纹理的uv zw表示法线的纹理uv
float4 uv:TEXCOORD0;
//光的方向 相对于切线空间下
float3 lightDir:TEXCOORD1;
//视角的方向 相对于切线空间下
float3 viewDir:TEXCOORD2;
};
//---光照---//
float4 _MainColor;//漫反射颜色
float4 _specularColor;//高光反色颜色
float _specularNum;//光泽度
//---纹理---//
sampler2D _MainTex;//颜色纹理
float4 _MainTex_ST;//颜色纹理缩放和平移
sampler2D _BumpMap;//法线纹理缩放和平移
float4 _BumpMap_ST;//法线纹理缩放和平移
float _BumpMapScale;//凹凸程度
//---透明度---//
fixed _AlphaScale;
v2f vert (appdata_full v)
{
v2f data;
//将模型空间下的顶点转移到裁剪空间下
data.pos = UnityObjectToClipPos(v.vertex);
//计算纹理的缩放偏移
data.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
data.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

//在顶点着色器当中 得到 模型空间到切线空间的转换矩阵
//切线、副切线、法线
//计算副切线————注意乘以切线中的w确定方向
float3 binormal = cross(normalize(v.tangent),normalize(v.normal))*v.tangent.w;
//转换矩阵
float3x3 rotation = float3x3(v.tangent.xyz,
binormal,
v.normal);
//模型空间下的光的方向
data.lightDir = ObjSpaceLightDir(v.vertex);
//转换到切线空间下的光的方向
data.lightDir = mul(rotation,data.lightDir);
//模型空间下的视角方向
data.viewDir = ObjSpaceViewDir(v.vertex);
//转换到切线空间下的视角方向
data.viewDir = mul(rotation,data.viewDir);
return data;
}

fixed4 frag (v2f i) : SV_Target
{


//通过纹理采样函数 取出法线纹理贴图当中的数据
float4 packedNormal = tex2D(_BumpMap,i.uv.zw);
//将我们取出来的法线数据 进行逆运算并且可能会进行解压缩运算 得到切线空间下的法线信息
float3 tangentNormal = UnpackNormal(packedNormal);
//乘以凹凸程度的系数
tangentNormal *= _BumpMapScale;
//---处理带颜色纹理的 BlinnPhong光照模型---//

//取出纹理当中的RGBA
fixed4 texColor = tex2D(_MainTex,i.uv.xy);

//颜色纹理和漫反射颜色的叠加
fixed3 albedo = tex2D(_MainTex,i.uv.xy).rgb * _MainColor.rgb;
//计算兰伯特
fixed3 lambertColor = _LightColor0.rgb * albedo.rgb * max(0,dot(tangentNormal,normalize(i.lightDir)));
//计算半角向量
float3 halfA = normalize(normalize(i.viewDir) + normalize(i.lightDir));
//计算高光反射
float3 specularColor = _LightColor0.rgb * _specularColor * pow(max(0,dot(tangentNormal,halfA)) , _specularNum);
//计算BlinnPhong
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo + lambertColor + specularColor;

//修改我们的Alpha返回值 决定透明度
return fixed4(color.rgb , texColor.a *_AlphaScale );
}
ENDCG
}


}
}

3.实现效果

2024-09-13 154313.png 2024-09-13 154336.png

评论