Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Feature Request Is there a ZTest with a custom threshold?

Discussion in 'Shaders' started by JSmithIR, Sep 10, 2023.

  1. JSmithIR

    JSmithIR

    Joined:
    Apr 13, 2023
    Posts:
    84
    Hi,

    I am wracking my brains over what I hoped would be an easy task. I have two objects that are similar in size, shape, and position. I need to visualize the intersecting area of the two objects as a solid color. Obviously, the Z fighting is an issue that prevents clean visualization.

    I can’t use a stencil buffer because similar comparisons are happening with pairs of objects behind the previously mentioned pair. Thus, those background objects using the same materials would also be writing to the stencil buffer and interfering with the cutout of objects in the foreground.

    I cant use “ZWrite always” for the pass visualizing the intersection for the same reasons. Pairs in the background would write to the foreground, and would obviously be a mess.

    I just need a way to tell a shader pass to pass the ZTest as long as occluding geometry is WITHIN A CERTAIN THRESHOLD of depth. Thus, my closely positioned object pairs can have the slightly-behind object appear entirely in front of its pair object, but not in front of EVERYTHING.

    I realize that stencil operations include a ZFail but this is not enough to make my situation work. I have to use ZFail incrSat to create the shape of the entire intersecting area, but doing this for all object pairs creates the same problem I just described. I think I have tried everything. Whether using the ZTest or Stencil test, my situation requires that I overwrite normal rendering order but only within a certain depth threshold, because I need to maintain normal rendering order between different PAIRS of objects, but I also need to override normal rendering order WITHIN THOSE PAIRS.

    Thus, a ztest threshold would solve my problem. I have never heard of such a functionality. Any advice?
     
    Last edited: Sep 10, 2023
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,238
    Doesn’t exist.

    Not exactly at least.
    Offset
    exists to accomplish this, kind of. It doesn’t work exactly like what you’re describing. But it’s intended to offset the depth within the accuracy of the depth buffer, explicitly to avoid things like Z fighting. Though it gets complicated with any surface that’s not parallel to the view plane, so it also has an additional offset based on the “slope”, ie the angle away from the camera a surface is facing.
     
    JSmithIR likes this.
  3. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,081
    This should work :)

    // Normal object rendering

    Stencil
    {
    Ref 0
    Comp Always
    Pass IncrSat
    ZFail IncrSat
    }

    // Second pass for object that need to show overlap

    Stencil
    {
    Ref 1
    Comp GEqual
    }

    replacement shader that will output your solid color in case of overlap
    Vertex shader( input )
    {
    compute view position and move slightly ( 1cm or other ) to the camera so overlapped geometry will pass ztest in that 1cm threshold
    convert this displaced view position to clip space and output from vertex shader
    }
     
  4. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,081
    To have more precision in depth buffer make distance between near and far plane of your camera as small as possible.
    I advice to change near plane from 0.01 to at least 0.1 and better 0.5 this will increase depth buffer accuracy significantly :)
     
  5. JSmithIR

    JSmithIR

    Joined:
    Apr 13, 2023
    Posts:
    84
    I appreciate your reply. Help me out, how will this stencil method not create unwanted rendering effects in my foreground object pairs? If a background object is writing one to the stencil buffer, that is going to cause foreground objects to render "overlap" at the wrong areas (ie, not just at the overlap with its paired object in the foreground, but also where it overlaps background objects).
     
  6. JSmithIR

    JSmithIR

    Joined:
    Apr 13, 2023
    Posts:
    84
    Always great to hear from you. Perhaps you can give some insight into my plan for an alternate approach? I have a shader that has two passes - each pass renders the same model but at two different orientations. I could effectively get the same effect as a stencil if I can get the second pass to only render inside the same clip space area as the first pass. My hope is that, since I can hold two sets of vertex positions in the vertex shader, maybe I can pass two sets into the fragment shader. The second pass can draw to the exact same pixels as the first pass (using first set of vertex information) but can map the texture as if it were being mapped onto the object at the second orientation (second set of vertices). One set of positions to tell it which pixels to draw to, and another set of positions for texture mapping. Is this possible? Not sure how it would match fragment to the correctly interpolated color though...
     
  7. JesOb

    JesOb

    Joined:
    Sep 3, 2012
    Posts:
    1,081
    Looks like make it work is not as simple as I guess :)
    There are few conditions must be meet to make it work :)

    it is must be opaque object that sorted near to far on rendering by URP so you first draw near objects then far.
    In that case

    - each object need to have a copy with intersection material slightly offseted forward (may be not physical but by bounding box modification) to render after original object
    - original object increment stencil by 1
    - second original object of intersection pair increment stencil by 1 and intersected part become 2
    - first copy rendered with stencil condition >= 2 and small depth offset to screen https://docs.unity3d.com/Manual/SL-Offset.html and render overlap solid color. stencil pass and fail operations must be set to Zero
    - second copy rendered with stencil condition >= 2 with the same rules and will output nothing but will clear stencil buffer to Zero

    - down the frame more far object will try to draw and will do the same logic starting with Zeroed Stencil buffer

    It is complicated and will break batches but it must work :)