Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Is there a simple way in a frag or surface shader to control Z-fighting with itself?

Discussion in 'Shaders' started by awardell, Jan 16, 2022.

  1. awardell

    awardell

    Joined:
    Feb 5, 2014
    Posts:
    71
    I'm generating a bunch of flat geometry in a single mesh. X/Y varies but Z remains constant. Sometimes the geometry will overlap with itself. I know how to tell which geometry should draw in front thanks to some data I have encoded in a UV-channel. What I don't know is the best way to tell the shader how to use that info.

    Controlling the depth bias via Offset doesn't work because it needs to be variable and needs to interact with itself. Transparency is a no-go; this shader is opaque. I'd rather not use vertex-displacement because I need this mesh to be perfectly flat.

    Are there better ways of achieving this kind of effect? I'm working in the standard pipeline, but something pipeline-agnostic would be ideal. Thanks for any help!
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    What do you think Offset does? Effectively it's just pushing the surface towards (or away from) the camera.

    This really is the easiest solution. If you're using a vertex fragment shader offset the clip space z a tiny amount based on the encoded data. Something like this:
    Code (csharp):
    1. o.pos = UnityObjectToClipPos(v.vertex);
    2. float sortingOrderData = i.texcoord1.x; // or whatever you're using
    3. o.pos += _ProjectionParams.x * _SortingOffsetScale * o.pos.w * sortingOrderData;
    The
    _SortingOffsetScale
    should be very, very small. You'll have to play with it, but it may only need to be something like 0.0001 to work.

    If you're using a Surface Shader, you'll have to do this a little differently and instead calculate the world to camera vector and offset a tiny amount along that vector.
    Code (csharp):
    1. void vertex (inout appdata_full v)
    2. {
    3.   float sortingOrderData = i.texcoord1.x; // or whatever you're using
    4.   float3 worldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1.0);
    5.   float3 viewDir = worldPos - _WorldSpaceCameraPos.xyz;
    6.   viewDir /= dot(viewDir, UNITY_MATRIX_V[2].xyz).
    7.   worldPos += viewDir * _SortingOffsetScale * sortingOrderData;
    8.   v.vertex.xyz = mul(unity_WorldToObject, float4(worldPos, 1.0));
    9. }
     
    Last edited: Jan 16, 2022
    awardell likes this.
  3. awardell

    awardell

    Joined:
    Feb 5, 2014
    Posts:
    71
    Well that's good to know. I'll give the vertex-displacement a shot. Thanks!