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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Always draw on top of particular object; is ZTest the answer?

Discussion in 'Shaders' started by rageingnonsense, Jan 29, 2016.

  1. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    In my game, I allow the player to lay roads onto the terrain. My terrain is chunked LOD style, and as such at far distances the lower resolution chunks pop up above my roads in some places (the highest LOD chunks are deformed to fit just under the roads, but the chunks of lower LOD don't always have all necessary vertices moved [and can't really for a variety of reasons]).

    I'd like to solve this problem by writing a shader(s) that basically says "always draw this road's pixels above the terrain, but not anything else like trees or buildings".

    Reading up on this, it appears ZTest might be what I am looking for, but the documentation of the options are pretty slim.

    How could I go about writing a shader(s) like this? How can I tag the terrain, and make other shaders aware of it? I'm looking for guidance; am I thinking along the correct lines, or is there something else entirely I should be considering?
     
  2. mholub

    mholub

    Joined:
    Oct 3, 2012
    Posts:
    123
  3. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    The problem with this is that if I set ZTest Always, I can see the road through mountains. It is otherwise what I am looking for, but how can I solve the issue with roads being visible behind mountains?
     
  4. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    ZTest Always means it's same as if(true){render();}. So no matter what's written in Z, it will still draw. What you need it ZTest LEqual (default one).
     
  5. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    ZTest LEqual just brings me right back to where I started...
     
  6. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    If you examine the polys of your road, perhaps you're not actually saving much or changing performance by lodding your road to begin with. Some games do not actually lod things far off, unless there's a *lot of those things*. If it's just the odd object here and there that doesn't lod well - you don't have to.

    You want to look at the big picture visually and performance. There's no point in having 4 lods for the main character if it's only ever shown close up for instance. Likewise, if a road isn't drawing correctly in the distance you probably should make a bespoke lod for it or don't lod it.

    Do you notice any change in performance?

    Finally you can try the shader offset parameter. This really only solves z fighting for co planar faces, but it can sometimes resolve issues like what you're having in the distance.
     
  7. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    Turns out the answer has to do with Offset. The problem I have now is figuring out how the hell it works so I can use it effectively. I have been playing with it, and it does what I want; but I am confused about the difference between factor and units. It seems like only Factor makes any difference. I can set units to anything and it does nothing.
     
  8. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Offset –1, –1 will usually be what you want, but this thing only makes an effect if 2 faces are almost together. It may or may not improve matters.
     
  9. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    I'm not LODing my road at all. I don't think I explained myself well enough.

    I have LODed terrain. When I place a road, the highest LOD of the terrain is deformed such that it is not above the road. However, as the camera moves away, and the LOD of the terrain chunks change, you can start to see terrain pop through the road in places.

    At the lowest terrain LOD, it is entirely possible for a road to exist in a single triangle due to the size of that chunk's mesh.

    This is why I need to solve this with a shader. Offset appears to be what I need to use, but now I am trying to figure out the difference between factor and units.
     
  10. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    I am trinyg to find a happy balance. I found offset -20, 1 on the road works pretty well, but has issues with other objects in the scene. I am considering editing the terrain shader instead to use offset 20, 1, but when the factor gets that high, shadows start to look.... incorrect to say the least.
     
  11. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I doubt offset is what you want. Why not just not lod the road? I doubt it will have any performance impact for it probably isn't a major triangle difference ie if it's a 20k poly difference, it is not worth it, since it's just one element.
     
  12. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    You're really not understanding the problem. I appreciate the help, but you're clearly not getting it.

    The road, in no way, is LODed. Not at all. The road always has the same amount of triangles. always. Period. The road has no LOD at all whatsowever.

    The issue happens because of the changes in terrain LOD. Here are a couple of pictures to help explain the issue:

    This is the road zoomed in to the terrain such that the highest LOD is shown:


    Here is the same area zoomed out such that a lower LOD chunk is being shown instead(in blue) (shown from scene view, game camera is much further away):



    Notice how, due to the resolution change, parts of the terrain are now popping through the road? This gets more pronounced as the camera gets further and further away, revealing lower and lower LOD terrain chunks.

    I can't simply move all vertices below the road; as chunks get larger and larger, the flattened area would get larger and larger. At the lowest LOD (terrain, we are only talking about terrain here), a single quad could get as large as the entire highest LOD chunk. It is simply not feasible to physically move all the required vertices.

    This is where shader magic needs to come in. Here is the same image with an offset (Offset -10, 1):


    Notice how it visually is hidden behind the road? The vertices from the second image were altered in no way. This is what I was trying to accomplish. I don't have it perfect yet though. At some angles I can still see the terrain show through.

    Now I am just trying to figure out the nuts and bolts of Offset because there are still situations where it is imperfect. Setting a very low offset (-50) helps, but then other objects in the scene look very weird when they come close to the road (partly shaded by the road, appear to be floating, etc.)

    I am also open to other suggestions, but first I think I needed to explain this in far more detail. Does this make more sense now?
     
  13. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I see yes, there isn't much you can do about that short of decal (need deferred rendering) or making the road part of the terrain textures. It's a tricky thing because you won't be able to match up a mesh with it correctly.

    Deferred decals will be able to do this as you're essentially drawing the decal mesh, replacing colour, and normals. It would depend on your approach.

    Check http://forum.unity3d.com/threads/easyroads3d-v3-the-upcoming-new-road-system.229327/ to see if he's solved it. If he has, it may just be quicker to use that.

    Ideally though if the terrain is reasonably flattened near the road, terrain lod shouldn't actually interfere... it may be the simplest solution is to compromise by adjusting the terrain with trial and error.
     
  14. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Shader magic is nice, but you need determinism in behaviour caused by non-shaders. Solutions I see:
    1. Decals (previous answer).
    2. Textures on top of Terrain... (previous answer too).
    3. Add LOD to road so it matches terrain. Might be tricky as Unity Terrain LODs are built-in and not disclosed how exactly they work.

    P.S. when you will find determinism by using shader magic where result is undetermined by default, I'll admit you used magic and will try to find what broke in this world.
     
  15. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    I actually use decals for the placement guide. These roads are 3D though (they have sidewalks with sunken geometry, can potentially be raised, etc), so a decal approach is insufficient.

    They actually work via a string of smaller segments (skinned meshes) placed along a bezier curve, and then the bones of the segments are rotated to match the ones in front of them, as well as to auto-link ends to create intersections of theoretically infinite connections (3 way, 4 way, 8 way, whatever), and angles of attack. The intersections are not "hard coded"; they are fully dynamic.

    Anyways, This rules out decals and textures on top of terrain as it fundamentally goes against how the entire system works. Not sure how I could use LOD, but it seems like a lot of trouble for little gain.

    I'm really looking for a shader approach here. I already deform the terrain under roads after placement to make them flush up close. Now I just need to road drawing takes precedence over terrain drawing (with regards to depth), but not anything else in the scene.


    Is it possible to set a z offset that only applies to a particular queue?

    For instance, Say I have a renderqueue for my terrain (geometry-1). Could I have something in my road shader that is akin to:

    Offset -10, 1 if pixels below came from renderqueue geometry-1

    ?
     
  16. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    You can remember queue by writing to stencil buffer in Terrain shader. And you can have additional pass in road shader with stencil buffer test and offset (if stencil test fails - first pass, else second pass - like that). Not sure how it'll help you.
     
  17. rageingnonsense

    rageingnonsense

    Joined:
    Dec 3, 2014
    Posts:
    99
    Ooooo this is very interesting! So I can write, say, 1 to the stencil buffer in the terrain shader; and then in the road shader check for 1 in the stencil buffer and if so, ignore depth testing altogether.

    This seems very promising. My only concern is how to differentiate between "shwo above the terrain" and "do not show behind a mountain". I am going to play with it. :D
     
  18. jolix

    jolix

    Joined:
    May 22, 2016
    Posts:
    70
    I'm interested how you solved this (with deferred shading).

    • I tried the stencil buffer: works only in forward, can't figure out how to use it in deferred rendering.
    • I tried with GrabPass and clipping pixels based on the alpha channel: works only if HDR is off, also shadows are not clipping (ugly overlapping shadows)
    • I tried multiple Camera's: shadows from the second camera don't draw on the first one
    If only there is a way to write to the z buffer in deferred mode.