Unity3D中的Z-fithgting问题
一、什么是Z-fighting
- 多个对象争夺最接近相机的渲染(争夺顶部)。当对象由于在渲染场景时具有相似的z-buffer值而重叠时,可能会发生这种情况。
简单的讲,就是多个平面共面,导致的他们之间相互“打架”,都想让相机先看到自己而产生闪烁,如下图:
正常的渲染应如下图:
二、如何解决Z-fighting
1. 在物理上将对象移得更远
- 不推荐,视觉效果差,而且实际工程中也不允许对象的位置发生偏移。
2. 增加相机的近剪裁平面并减少相机的远剪裁平面
- 调整主相机的Clipping Planes中的Near与Far属性
3. 修改Shader深度偏移设置不同对象的渲染顺序
- 在shader中添加
Offset <factor>, <units>
三、示例——基于方法3
新建shader脚本:Create->Shader->Stander Surface Shader
打开脚本
Properties中添加
1
2
3
4
5Properties
{
_OffsetFactor("Offset Factor", Float) = 0
_OffsetUnits("Offset Units", Float) = 0
}SubShader中添加
1
2
3
4SubShader
{
Offset[_OffsetFactor],[_OffsetUnits]
}将shader拖拽到材质球中,点击材质球,在inspector中可以看到
Offset Factor
与Offset Units
属性Offset Factor
与Offset Units
官方介绍中取值为[-1, 1],笔者实际操作发现-2、-3等数值,也可以使用为不同材质球设置不同的
Offset Factor
与Offset Units
,即可解决Z-fighting问题
四、相同材质物体的Z-fighting问题
- 如果发生Z-fighting的物体材质相同,则还需进一步的优化:
- 思路:不同的物体设置不同的
Offset Factor
与Offset Units
- 核心:获取对象
InstanceID
,通过C#代码改变物体材质 代码如下:
1
2
3
4
5
6
7
8GameObject obj = new GameObject();
// 获取独一无二的InstanceID
int instanceID = obj.GetInstanceID();
// 获取材质
Material _material = obj.GetComponent<MeshRenderer>().material;
// 设置材质属性_OffsetFactor与_OffsetUnits
_material.SetFloat("_OffsetFactor", instanceID * 0.00002f);
_material.SetFloat("_OffsetUnits", instanceID * 0.00002f);优化前:
优化后: