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

Toon Lit - shadow stripes artifacts

Discussion in 'Shaders' started by sparksinfinite, Aug 10, 2017.

  1. sparksinfinite

    sparksinfinite

    Joined:
    Aug 10, 2017
    Posts:
    6
    When using the standart assets shader Toon Lit, chances are that you encounter dramatic shadow artifacts at specific angles (when the angle of the light is almost the same as the surface apparently):

    artifact.png

    This problem is very strong on low poly meshes and large faces (low poly terrain).
    When using a day night cycle (slowly rotating light) there is no possiblity to avoid these artifacts.

    artifact2.png

    I tried to figure out how to write a shader which basically clips away such parallel angle cases in the shadow calc, but thats a tough topic. Maybe someone around knows how to solve this problem.

    Note: This cannot be solved with bias / normal bias settings. You may alter it, but it does not disappear. Higher Shadow resolution makes the stripes more crips but does not help either.

    This really kills me, because it basically breaks my game. Who wants to look at a beautiful landscape, only to encounter disturbing shadow stripes?

    Please help. :(
     
    Last edited: Aug 10, 2017
  2. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    Just have the ground only receive shadows not cast...
     
  3. brn

    brn

    Joined:
    Feb 8, 2011
    Posts:
    320
    There are a few things you can try to increase the accuracy of your shadows.

    Easy un-costly things to try
    • Set your near and far plane on your camera to the smallest effective range you can. Do this by setting your near plane as far out as you can and your far plane as close as you can.
    • In the project quality settings set the shadow distance to be a close as you can get away with.
    • Also in the project settings try the different shadow modes. If you are willing to trade stability for resolution it might be ok for you.

    Performance reducing things to try
    • Increase the resolution of your shadow in the light settings or the project quality settings.
    • If you can wear the cost of extra shadow cascades give it a go. you can find this in the project quality settings. Try moving the ranges for your lod levels around and allow for more resolution at the further distances.

    If this doesn't produce satisfactory results there is a complicated option.
    Modify the screenspace shadow buffer results by light angle and normal angle. You will need access to the gbuffers to do this or force your forward lighting camera to render a depth normal pass.

    Effectively where the dot product of the lights forward vector to normal is 0 you will want to white out your shadow buffer.

    This is not a trivial task as you will need to tackle command buffers and a few complex transformations.

    SE's screenspace shadows package has all the underpinning structures in it. But is tackling contact shadows. It would make an excellent starting point as a template on making your own variation.
    https://www.assetstore.unity3d.com/en/#!/content/77836

    If you are feeling tricky enough you can even derive the normal from the depthsample. on your type of scene this will probably be ok even though its not as accurate. Probably not the answer you were after but I hope it helps.
     
    sparksinfinite likes this.
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,236
    Lots of good information here, and good tips for battling shadow acne and peter panning artifacts ...

    None of which will really help here since what is happening here is mainly an artifact of using a wrap around shading model. That is to say the directional light is affecting more than the surfaces facing towards the light.

    The two solutions to this are:
    • Don't use a wrap around shading model, which in this case means using a toon ramp texture that goes completely black at the 50% mark or use a different toon shader from the store. The UnityChan shader supposedly doesn't have this issue.
    • Do what @brownboot67 suggested. A lot of games actually limit the angle the main directional light can get down to to try to reduce the effects of this when it comes to straight shadow acne. For Dino Frontier I completely fade out shadows as the light gets close the horizon.
    Basically, it's an unsolved issue with shadow maps which even the latest state of the art non-linearly quantized moment shadow maps (yes, that's really a thing) only helps to soften the artifacts a little, it doesn't get rid of them entirely.

    SE's Screen-Space Shadows is an awesome asset, but it too has a hard time with lighting that's nearly parallel to the surface, sometimes causing an even stronger noise than the original shadow. I believe the solution employed to combat this problem for that asset just tries to skip doing the shadows on surfaces that are nearly parallel, so it might not help at all for you.

    The "perfect" solution is ray traced shadows, but that's not really an option as they're not supported in Unity. Even if they were they'd be too slow to actually use. Nvidia showed off their Hybrid Ray Traced Shadows some time ago, and it shipped with The Division, but it only works on the latest Nvidia GPUs, and have a significant performance hit (like you need a GTX 1080 to be able to use them with out a noticeable framerate drop).
     
    nxrighthere and sparksinfinite like this.
  5. sparksinfinite

    sparksinfinite

    Joined:
    Aug 10, 2017
    Posts:
    6
    First off, thank you all for replying! I will answer bgolus in my second post.

    This would break the day-night cycle (think about objects on high ground at sun rise/set). And I like shadows from the terrain. Also, this will not help on other objects all around you, which are also affected by stripes phenomena. They all have the Toon Lit Shader!

    terrainReceive.png

    I am aware of these settings, sorry that I didnt mentioned this. They indeed help to increase shadow quality and resolution, and also to get to get rid of some bleeding and so on.. very common.

    But I need a solution on the Toon Lit Shader shadow artifacts specifically.
    This is different from the standart shadow receiving.

    You can try this, if you want, just place a 4 sided cone in a scene, use Toon Lit Shader, and set the sun direction around the same angle as the cone surface. And then adjust all possible light settings. You cannot get rid of these artifact this way. Example, that I tried it:

    artifact3.png

    Thats the direction I was thinking about. I also think, this is the only solution here. As you say, this is tough. More on this in the next post.
     
    brn likes this.
  6. sparksinfinite

    sparksinfinite

    Joined:
    Aug 10, 2017
    Posts:
    6
    I really like real casted shadows, I do no want tinted surfaces, just to clarify this.
    You can call it Unlit Shading with shadow cast and receive. The surfaces are not highlighted or darkend, only the shadow cast lies on them. Thats what the Toon Lit does.

    The chan shaders are very similar to the Standart Asset Ton Lit Shader imo.
    And they have the exact same issue, like all other Toon (Receiving Shadows) Shaders I have tried so far.

    At specific angles you will get: (I tried all of the chan shaders, here is one )

    chanShader.png

    I also thought about this. It may exclude some big almost flat terrain triangles that way. But It will not help on all other objects. I also thought about using some fixed angles on meshes like 45, 60, 30 only - and then jump on the day-night cycle just a little bit over them. Then I also need to consider rotation of the objects on the other axis, to avoid that any surface will ever be parallel to the sun .. ! That will not work on moving objects. No way.

    In my opinion - there is a very very simple solution. If only the shadow calc would consider such close parallel angles (of dir light and surf) and just draw it like it would be a little bit over / beneath that (depending on rising or descending). However access to the shadow calc is so complicated. I think I'm screwed.
     
    Last edited: Aug 11, 2017
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,236
    Yeah, don't do this one. This will just make those surfaces not have any shadows at all, even the ones you want.

    You're describing receiver plane biasing. I used this for Planetary Annihilation, and is a common technique for a lot of games not using Unity. Unity's screen space shadows make this a little harder to implement, but Unity actually has the code for it in their screen space shadow shader already. If you get the built in shader source, look for Internal-ScreenSpaceShadows.shader. Make a copy of that file and put it in your project, then change this line:

    #define UNITY_USE_RECEIVER_PLANE_BIAS 1

    Then go to the graphics settings and change the screen space shadows shader setting from built-in to custom and assign that modified shader to it.
     
    brn likes this.
  8. brn

    brn

    Joined:
    Feb 8, 2011
    Posts:
    320
    Awesome and what looks to be a proper solution there Bgolus!

    The "Easy un-costly things to try" & "Performance reducing things to try" are a bit newbie but unfortunately on a forum you just don't know what level somebody's experience level is at. I prefer to state the obvious just in case.

    I might be wrong when visualising the problem of the streaking, but I would guess that the higher the shadow resolution, the range in which the streaking will occur will be reduced. Unfortunately at the point where the sample is partial you will still end up with streaks. Just of a higher resolution.

    Recently for a project I'm part of I've modified the standard unity gbuffer configuration and lighting to handle approximate SSS. Which created a similar set of issues to the situation described here and a few others. I used SE's asset as a starting point. However I had to modify it a lot to suite our situation, but it provides a nice starting template . Which in our case covered close contact and far shadows to cover where the last cascade finishes. I also tweaked the shadows in general to cope with light transfer. Its an open world situation.

    Its all under NDA so there are limitations to what I can show. However the Tassie Devil pic(my avatar icon) uses custom fur/sss shaders from a personal AR project that the game solution is based on.

    I'll be switching on that define this afternoon for sure :D
     
    Last edited: Aug 12, 2017
  9. sparksinfinite

    sparksinfinite

    Joined:
    Aug 10, 2017
    Posts:
    6
    I did what you suggested, but the stripes still occur. Did I miss something? Do I have to include something to the Toon Lit Shader?

    problemStill.png

    I forgot to mention that this problem is mostly visible on Hard Shadows. When using soft shadows, it is acceptable, sadly it does not fit the art style so good as the hard shadows do.

    Toon Lit, Shadow Receive and Hard Shadows bascially dont work together, there will be stripes. This really baffles me, I thought that wouldnt be a problem in the first place, but it is. Please help me, Unity Staff !!

    Following is the art style I am after, not 1:1, but it was the inital inspiration:
    https://twitter.com/NewinGames/status/884447853787140097

    When you watch the shadows closely, you will notice stripes in this unity game as well. However, he managed to hide them as good as possible. Not sure if he is heading for a day-night cycle as well.. then he will be in trouble too.

    tafoniArtifact.png
    Screenshot from a game called Tafoni.
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,236
    There shouldn't be anything needed to make it work with the toon shader. However it is only implemented for soft shadows ... and it's totally broken as far as I can tell. It worked when I tested it around Unity 5.4 but in 5.6 and 2017 it appears totally broken.

    As I said, this is an unsolved issue with shadow maps that people tend to just work around. For example, this is your basic diffusely lit cube.
    cube-lit.png
    You can see a touch of shadow acne on the top left edge there, but there's nothing on the right side, right?

    Wrong, there's a lot. Here's that cube just showing the shadows by themselves.
    cube-shadow.png

    The final lit cube is a combination of the shadows multiplied by the direct lighting, the "ndotl", or the dot product of the normal and light direction.
    cube-ndotl.png

    As you can see that right side is basically black, so it hides all of that nasty shadow acne. Then ambient lighting is added on top.
    cube-ambient.png

    So basically it's:
    cube-shadow.png * cube-ndotl.png + cube-ambient.png = cube-lit.png
    Toon shaders generally work by making the ndotl a hard edge, or map the dot product to a texture ramp, and also often push the "edge" back some. I suspect the project you linked to is doing something like that, but may be pushing the "edge" forward instead of back to help hide the acne a little better.

    The standard assets toon shader is ramp based, and if you don't use a ramp at all it'll act just like the shadow only cube above. If you notice the toon ramp that comes with the toon shaders is mostly white for half of it, but goes black before the half way mark, probably for explicitly for this reason.
     
    Last edited: Aug 12, 2017
    funkyCoty and morepixels like this.
  11. brn

    brn

    Joined:
    Feb 8, 2011
    Posts:
    320
    Bgolus pointed out earlier that the reason the shadow streak is visible is due to the wrapping in the lighting. Its a good point. Its the disparity on the shadowed value and the lit value that makes the toon shader look streaky. Not knowing the specific conditions for your game makes it difficult to suggest the exact solution.

    However some ideas do spring to mind. They all require some compromise.

    Ideally you want there to be no disparity at the shadow transition. So I would take a type of "double pass" at the lighting.
    For your typical lighting that gets attenuated by the shadows use a standard lighting model. For the wrapped contribution of the lighting put it into the emissive/ambient part of your lighting. Make sure you only add the difference between the wrapped and the standard to the emissive/ambient.

    you will get some lighting in your shadows by doing this. but it will be only the difference between the wrapped and standard lighting. Since the wrapped part wont be modulated by the shadows you shouldn't see any more streaking than you would get with standard lighting.

    next you could try and remove the wrapped lighting once its transitioned far enough into your shadowed area. This should happen at the transition point of the dot(light,normal) as it goes from 0 to a negative value. Use that transition to modulate the wrapped contribution. something like wrappedLambertDifferance * 1- saturate(-dot(light,normal)).

    play with the modulation value by adding offsets or multiplying it by itself to shift and compress the falloff into the shadows/self shadowed areas.

    perhaps this will be satisfactory.
     
  12. brn

    brn

    Joined:
    Feb 8, 2011
    Posts:
    320
    Seems Bgolus beat me to it. LOL we are pretty much saying the same thing in different ways.
     
  13. sparksinfinite

    sparksinfinite

    Joined:
    Aug 10, 2017
    Posts:
    6
    Thank you both, and nice explanation, Bgolus!

    I kinda "fixed" the stripes issue now. It is not a perfect solution, and I need to test it a lot more on different environments, but it works so far. And maybe it can be improved / corrected even more. (Thinking of some fading.)

    Code (CSharp):
    1. // surf
    2. float d = (dot(s.Normal, lightDir));
    3. if (d <0.1 && d > -0.1) atten = _ShadowStrength;
    4.  
    5. // shader code rest from the toon lit shader
    6. half4 c;
    7. c.rgb = s.Albedo * (_LightColor0.rgb * atten * 2);
    8. c.a = 0;
    9. return c;
    Basically overrides the shadow drawing on critical angle and just draws the surface in the current shadow strength. Nothing fancy.
     
    Last edited: Aug 12, 2017
    nxrighthere and brn like this.
  14. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,469
    How about doing shading as usual, then apply "toon effect" as a post process from the lighting result, basically applying a ramp to it? Or a variation of that?