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

Different Z Offset per triangle

Discussion in 'Shaders' started by Magister, Oct 9, 2017.

  1. Magister

    Magister

    Joined:
    Mar 29, 2013
    Posts:
    34
    Name says it all. I'd like to use Z offset to fix some Z flickering issues. I know you can do this with a shader using Offset; https://docs.unity3d.com/Manual/SL-CullAndDepth.html

    However, that only assigns a single Z offset for the whole mesh. I need different Z offsets depending on the triangle because I sometimes have multiple overlapping polys in the same plane. Since Z offset takes 2 values it seems like a perfect fit for using 1 of the UV texture channels to hold the 2 values as UV coordinates, allowing different values for every vertex. Is this possible?

    Alternatively I could split the mesh into several meshes, each with a separate Z offset applied, but I'd prefer not to for performance reasons because of how many more meshes that may end up being.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    The Offset parameter is set long before the vertex or fragment shaders have run. It's a single set of values for the entire draw call.

    If you're seeing z fighting from overlapping geometry there are a few causes.

    You're drawing different geometry in the same place. Offset is indeed the correct solution here and kind of the purpose of it. However you likely do not want to have Zwrite enabled for later passes to prevent them from causing additional zfighting.

    You're drawing the exact same geometry in the same place with different shaders. If this is causing a problem then the issue is your shaders. You need to make sure the way you're transforming geometry from the object to clip space is exactly the same otherwise it'll z-fight. The correct solution here is to make sure they're all using Unity's built in UnityObjectToClipPos() function. If you're doing any kind of per vertex offsets make sure they're all using the same math.

    There's one last case which is kind of Unity specific which is drawing the same geometry with the same shader but with different methods (unique renderer component vs batched renderer component vs manual draw mesh) they might z fight. Unfortunately offset is again kind of the correct solution as this seems to be an unsolvable problem, apart from changing the way you do things to render all passes with the same method. They are in effect actually different geometry in these cases.

    Now if none of these really cover what you're doing and having multiple offsets per draw call really is the solution you want to run with, this is possible. The simplest solution would be to add a small value to the clip space z in the vertex shader. Basically:

    o.pos = UnityObjectToClipPos(v.vertex);
    o.pos.z += 0.001;

    That's pretty close to the result you would get using Offset 0, 1 in your shader. You could then store an offset amount in your vertex data. If that first value in the offset is important to your use case, ie: if Offset 1, 1 is noticeably better than Offset 0, 1, then you'll need to implement some form of additional slope offset. The easiest way would be to do a dot product of the view dir and vertex normal and add additional offset the closer to 0.0 you get. Note that this isn't how Offset is actually implemented on the GPU, this is just a quick approximation. The real way too replicate it would be by writing directly to z depth from fragment shader and calculating the slope from derivatives, but that gets complicated if that sentence didn't make sense to you.
     
    ArturTEST likes this.