Search Unity

How to approach baked edge detection?

Discussion in 'Shaders' started by adslitw, Feb 4, 2020.

  1. adslitw

    adslitw

    Joined:
    Aug 23, 2012
    Posts:
    275
    I'm toying with an idea at the moment that needs solid edge detection on some simple objects. I'm vaguely thinking I can just bake the edges out to a texture, e.g: box.jpg
    However some edges are obviously thicker than others, depending the object rotation, camera position etc. I'd really like to make all of the edges a uniform thickness. I *feel* like this might be doable using the edge information baked in the texture, however a couple of hours tinkering with Amplify Shader Editor and I'm at a bit of a loss.

    Is this a dead end? Maybe I need to bake out different information, or maybe it has to be screen space?

    Any pointers / suggestions gratefully received!
     

    Attached Files:

  2. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,573
    A simple way to have edges is to make them an image effect.
    https://github.com/Unity-Technologies/PostProcessing/wiki/Writing-Custom-Effects
    Old tutorial regardign it:
    https://www.ronja-tutorials.com/2018/07/15/postprocessing-outlines.html

    This method requires least amount of hassle.
    --------
    Another option is to generate outlines with geometry
    --------

    Without image effect, you'll need to perform calculations in screen space, and within pixel shader, color pixel based on screen space distance to the nearest edge.

    The thing is, the only part of the shader pipeline that has access to the entirety of primitive is geometry shader, so you'd need to write a geometry shader, calculate position of face edges there, and pass this information into a pixel shader.

    It should be doable, but I'm unsure if it is doable within amplify shader editor.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    For Planetary Annihilation I did something similar. It's not a Unity game, but the technique will work. First I baked out a distance to edge texture for every unit. I did this very grossly by rendering space uniform auto UVs out to a high resolution texture, using a photoshop hard edged glow, and then using that as the texture for the distance to edge. I didn't have any good tools at the time that could have done it more nicely ...

    In the shader it's then a simple trick of dividing the greyscale texture by the
    fwidth()
    of the sampled value, and offset it by the number of pixels you want the line to be wide. This is roughly the same technique used to do wireframe rendering by a lot of shaders, though they're using per-triangle "UVs" (barycentrics) to calculate the distance to the edge rather than a texture.
     
    neoshaman, adslitw and neginfinity like this.
  4. adslitw

    adslitw

    Joined:
    Aug 23, 2012
    Posts:
    275
    Haha, that's exactly what I've just been doing! What would you suggest as a more elegant solution?

    OK awesome, fwidth() sounds like the key - thanks. I just googled it, and came across another thread where you discuss it (https://forum.unity.com/threads/how-to-use-fwidth.395634/), so double thanks!
     
  5. adslitw

    adslitw

    Joined:
    Aug 23, 2012
    Posts:
    275
    So, I had a little play, and I think it's mostly working. It definitely works as you move the object closer / further away from the camera, but to my eye it's still not getting the 'non-face on' edges thick enough for my liking? That might just be an issue with the distance-to-edge texture, not sure. Anyway, here's my ASE graph if anyone else wants something similar at any point (and of course any suggestions gratefully received!). Distance to edge is baked into the alpha channel, fyi. edgeShader.jpg edge_result.jpg
    edge_alpha.jpg
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    For PA I wasn't super worried about the line thickness or precision. It just needed to be "something" for the build effects to key off of.

    For your case you might need a little more precision in how the edge is defined than just "it's black". In that case going with a SDF (signed distance field) will produce a slightly better approximation of where the edge is. By that I mean use both an inner and outer glow and center the edge on 127. Then in the shader subtract 127/255 from the texture before dividing it by fwidth. Getting a SDF gradient right in Photoshop was a pain though.

    At the time I did find some plugins for 3ds Max that could produce a distance field texture, but they were minor features of multi-thousand dollar plugins that I couldn't really convince anyone to buy for the whole studio, so the Photoshop hack stayed. Today I'd probably end up writing something myself to bake these out from Unity. Render out the UVs & local space position to a high resolution render texture, then pass that through a compute shader to calculate the edge distance for each pixel. Split that out to a texture file.
     
  7. adslitw

    adslitw

    Joined:
    Aug 23, 2012
    Posts:
    275
    Awesome. I just gave that a go, using this Photoshop SDF generation technique: https://halisavakis.com/my-take-on-shaders-ui-sdf-icon-shader/

    It's definitely a cleaner line! However the islands between the UVs aren't currently big enough to make a nice wide SDF, so I'll adjust them, have another go and report back. Thanks very much for your help!
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    That's a much better way of doing it. Make sure you adjust the gradient to use a smoothness of 0. That makes the gradient linear rather than applying a subtle curve to make it perceptually smooth. Use a line width of 128.

    That's not really an issue. As long as there's some amount of gutter between UV islands (like few pixels worth) it's fine. The main issue is avoiding solid black and immediately outside the surface's edge.
     
  9. adslitw

    adslitw

    Joined:
    Aug 23, 2012
    Posts:
    275
    Ace, great shout re: setting the smoothness to 0.

    Looking not too shabby. I'll keep playing to see if I can get the thinner lines looking thicker, I think my implementation might still be missing something. Thanks again @bgolus!

    SDF_outline.jpg
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The fact your lines are getting so thin in the above image is a little interesting. Not really sure what to make of that. Could be mip-mapping causing problems, could be something not quite right in your shader (what you posted above was correct, apart from needing to subtract 0.5 before the divide).