介绍

项目中使用的一部分 Shader 是 Cutout 类型的,功能是透明度在一定阈值以下会隐藏,否则显示,处于显示隐藏的二值状态。

但是有需求是物体在遮挡视线时变为半透明。

最初的方案是动态切换材质,然后设置透明度,但是进行性能分析的时候发现创建新材质极其耗费性能。因此尝试为原来的 Shader 增加透明度功能。

方案

尝试一

使用纹理自带的 alpha 值作为裁剪的依据,然后使用材质指定 _Color 属性的 alpha 值作为透明值。

需要修改混合模式以使用 alpha 值显示

Blend SrcAlpha OneMinusSrcAlpha

Shader 需要修改为以下属性以保证与其他半透明 Shader 正确排序

Tags{ "RenderType" = "TransparentCutout" "Queue" = "Transparent"

需要使用编辑器脚本查找 wall 层级使用的所有 Shader 名字,也需要修改所有材质的 RenderQueue 为 Shader 默认值,因为上面修改了 Shader 的默认 RenderQueue 值。

RenderType 在相机使用 public void RenderWithShader(Shader shader, string replacementTag); 时才启用,用于按照规则替换不同 Shader 显示。

尝试二

在实际测试时发现这样修改的 Shader 会遮挡特效的显示,而原来的换材质的方案并不会这样。检查原来默认的 Transparent/Diffuse Shader 是一个 Surface Shader,需要点击 Compile and show code 按钮查看编译后的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Shader "Legacy Shaders/Transparent/Diffuse" {
Properties {
 _Color ("Main Color", Color) = (1.000000,1.000000,1.000000,1.000000)
 _MainTex ("Base (RGB) Trans (A)", 2D) = "white" { }
}
SubShader { 
 LOD 200
 Tags { "QUEUE"="Transparent" "IGNOREPROJECTOR"="true" "RenderType"="Transparent" }

 // Stats for Vertex shader:
 //         gles: 7 math, 1 texture
 Pass {
  Name "FORWARD"
  Tags { "LIGHTMODE"="FORWARDBASE" "QUEUE"="Transparent" "IGNOREPROJECTOR"="true" "RenderType"="Transparent" }
  ZWrite Off
  Blend SrcAlpha OneMinusSrcAlpha
  ColorMask RGB

可以看到其中与现有 Shader 最重要的区别就是 ZWrite Off,并未写入深度值。经过测试,在 Cutout Shader 中增加 ZWrite Off 后,与场景内的各种半透明物体(特效、人物头顶高亮指引等等)都显示正确了。

但是如果 RenderQueue 重置为默认的 AlphaTest 2450 了。那么显示又不正确了。 所以正确做法与换材质 Shader 相同,将需要修改的属性作为动态属性在脚本中设置,因此只有在变透明的时候才需要关闭 ZWrite Off,同时设置 RenderQueue = Transparent 3000 以便与特效混合正确显示。

Shader 默认关闭了混合 Blend Off Blend Off: Turn off blending (this is the default) 如果在运行时动态修改,需要动态使用 BlendMode 替代 Blend Off

1
2
Blend One Zero
Blend SrcAlpha OneMinusSrcAlpha

Unity - Manual: ShaderLab: Blending

总结

最终需要修改的属性

  • Alpha 混合模式
  • ZWrite 深度缓存开关
  • RenderQueue 渲染顺序

这些属性的默认值都要保持原样不变,只在遮挡相机时使用脚本进行动态修改。

缺陷

由于在运行时修改了 ZWrite,在开启 ZWrite 的瞬间会出现画面的跳变,这是无法修改的问题,原理上的限制决定了渲染结果会出现不同。