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
    5
    Properties
    {
    _OffsetFactor("Offset Factor", Float) = 0
    _OffsetUnits("Offset Units", Float) = 0
    }
  • SubShader中添加

    1
    2
    3
    4
    SubShader
    {
    Offset[_OffsetFactor],[_OffsetUnits]
    }
  • 将shader拖拽到材质球中,点击材质球,在inspector中可以看到Offset FactorOffset Units属性

  • Offset FactorOffset Units官方介绍中取值为[-1, 1],笔者实际操作发现-2、-3等数值,也可以使用

  • 为不同材质球设置不同的Offset FactorOffset Units,即可解决Z-fighting问题

四、相同材质物体的Z-fighting问题

  • 如果发生Z-fighting的物体材质相同,则还需进一步的优化:
  • 思路:不同的物体设置不同的Offset FactorOffset Units
  • 核心:获取对象InstanceID,通过C#代码改变物体材质
  • 代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    GameObject 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);
  • 优化前:

  • 优化后:

五、reference