Search Unity

Question Create recess/inset with shader graph on flat surface?

Discussion in 'Shader Graph' started by colin_young, Dec 27, 2020.

  1. colin_young

    colin_young

    Joined:
    Jun 1, 2017
    Posts:
    243
    It is possible to create a recessed/inset effect on a flat surface, with just a shader? I'm trying to place windows on a building facade without having to add additional geometry to create holes for the windows. Typically, the frame, glazing, etc. of a window is set back some distance from the plane of the wall, sometimes significantly. So far I've been able to fake it by making my window trim unrealistically thick, but aside from the thickness, if I want windows that don't have protruding trim that technique isn't going to work. I'm trying to optimize the modeling effort here, not necessarily the rendering, although the hope is that would also be nearly as fast or faster than if I were to model the full geometry. The hope is that the shader could be applied to a plane object that was placed on the wall surface (i.e. a decal).

    Similar in concept to fake interiors, but I want it to appear as though there are 4 sides to the hole aligned to the edges of the "window". Ideally I'd like to be able to use a normal map on the recessed back surface to make it appear as though there are mullions, etc. and if it was possible to have parts of it transparent or otherwise allow combination with fake interiors that would be awesome.

    Something like: layer 1 - fake recess with 4 sides framing the window "cut out", layer 2 - height/normal mapped frame and glazing, layer 3 - fake interiors.

    Is such a thing possible? Where would I even start looking for details on implementing it?

    Conceptually it's pretty simple, but explaining it to the computer is a whole other thing :)
     
  2. colin_young

    colin_young

    Joined:
    Jun 1, 2017
    Posts:
    243
    UPDATE: I think I've solved it. Will do some further testing, then add a new post with my solution, assuming it continues to work and the visual artifacts I'm seeing are solvable or unrelated.

    Thinking a bit more, this is sort of pseudo-code of what I think would achieve the effect I'm after:

    Code (CSharp):
    1. foreach pixel in affected mat_A
    2. {
    3.     if next thing matches "B"
    4.     {
    5.         show the thing after the next thing (even if it turns out to match "B")
    6.     }
    7.     else
    8.     {
    9.         show next visible thing
    10.     }
    11. }
    12.  
    Where
    type_B
    could be a material, layer, etc., anything that would allow me to tag the object as being a thing to be masked out. In non-pseudo code terms, for a given pixel in my hypothetical knock-out material, show whatever the shader for the next visible thing would have shown if I wasn't in the way, as long as that thing doesn't match "B", but only ignore the first "B". Obviously this has some limitations, e.g. if I dragged a window off the corner of a building it could potentially mask out the surface of a different building, but since that's a situation I wouldn't want for many other reasons, I can live with that. I have no idea if this is something that can (or should) be done with shader graph.

    I've tried playing around with stencils, but the problem I'm finding is stencils on the back/sides of the building are bleeding through to the front. There would also be issues with things like notches even if I were able to figure out a way to get non-front facing stencil materials culled out.

    In case it isn't painfully obvious, I have next to zero experience with shaders, beyond simple sampling in 2d space. Once we're talking about scene depth, screen position, etc. I am, so to speak, out of my depth.
     
    Last edited: Dec 29, 2020
  3. colin_young

    colin_young

    Joined:
    Jun 1, 2017
    Posts:
    243
    Here are the 2 shaders I came up with. The DepthMask shader (A) is applied to a mesh that sits 0.001 in front of the mesh (avoid z-fighting, but minimize parallax problems) to have the hole cut into it. The window_mask shader (B) is applied to the mesh to have holes cut into it. This shader uses the depth buffer to set the alpha.

    A major issue I have run into is that if I have 2 layers of window_mask materials being viewed through the DepthMask, both have holes created in them. I've been trying to figure out if there is a way to filter on a range of distances, rather than just the "less than" I am currently using. So far I can't get that to work. I'm guessing it's something to do with the 0-1 range from scene depth/eye, but no combination of modes and logic I've tried gets what I'm looking for. What I'm trying to do is if B is behind A (distance < 0) and less than distance 'n' behind A, set alpha to 100%. I can't figure out how to get 'n', where the "true" distance between is e.g. 0.5 units or more (if B is more than 0.5 units behind A, don't mask it out).
     

    Attached Files: