Page MenuHome

Reduce shadow terminator artifacts
Confirmed, NormalPublicTO DO

Tokens
"Love" token, awarded by Muritaka."Love" token, awarded by sam_vh."Love" token, awarded by bnzs."Love" token, awarded by Dabi."Like" token, awarded by YAFU.
Assigned To
None
Authored By

Event Timeline

Dalai Felinto (dfelinto) lowered the priority of this task from 90 to Normal.Aug 20 2019, 9:30 PM
Dalai Felinto (dfelinto) created this task.

To address this issue, what do you think about adding a "shadow bias" parameter to control an offset from the surface along the geometric normal ? I know its not a perfect solution but at least it would allow users to avoid a lot of rendering artifacts that are due to shadow termination.

If you're ok with that I can try an implementation.

I'd prefer us to first try a better solution, like ignoring backfaces, which may not be all that complicated.

Also as described in T37814#773025, this needs changes in BSDF evaluation too.

I'd prefer us to first try a better solution, like ignoring backfaces, which may not be all that complicated.
Also as described in T37814#773025, this needs changes in BSDF evaluation too.

From what I understand of this "problem", this visual artifact is mainly due to low poly meshes having smooth shading enabled: you cannot fix the fact that a triangle is completely hidden from a light (from a point/directional light the artifact is obvious, it becomes less visible for large area lights). So for me it is basically just an incompatibility between the resolution of the mesh and its shading that try to simulate a smooth surface. Therefore I don't understand how ignoring backfaces could help, could you explain your idea ?

I never tried to dig deeper into that problem so I may miss something. Maybe some mathematical solution based on a reconstruction of the approximated continuous surface exists, but it seems complicated. In our previous renderer we "fixed" it with the simple shadow bias strategy that I mentioned, so that it can handle production. When the problem occured, our artists just increased the value and re-render the specific shot. However the problem was mitigated by the fact that artists used a subdivision level high enough on important assets, and almost only area lights.

That's why I am proposing the shadow "bias solution" here, but I can understand that it is not enough and cannot be considered robust. However with a default value of 0 I think it cannot hurt to offer that kind of control to the user, what do you think ?

I've just spoken to Jules Urbach from Octane, he's going to ask Lino from his Dev team to help you out.

@Laurent Noel (c2ba), I think someone should try an automatic solution before we add manual settings.

The issue happens when a reflection ray goes into the mesh, and then wrong shadow comes from when it exits. So if we ignore that exit, it may solve the most common type of problem. It's even possible to experiment with this using just a shader setup with a transparent BSDF on the backside, to see how well it works. Either way, any solution will need changes to the BSDF evaluation first.

same problem with volumes too? identical model left and right

@Laurent Noel (c2ba), I think someone should try an automatic solution before we add manual settings.
The issue happens when a reflection ray goes into the mesh, and then wrong shadow comes from when it exits. So if we ignore that exit, it may solve the most common type of problem. It's even possible to experiment with this using just a shader setup with a transparent BSDF on the backside, to see how well it works. Either way, any solution will need changes to the BSDF evaluation first.

Is this what you meant Brecht? Doesn't seem to make any difference if so. Also makes no difference if shadows are disabled on the light, and if glossy rays are turned off for the light.

This seems to be the most used solution I can find:

https://computergraphics.stackexchange.com/questions/4986/ray-tracing-shadows-the-shadow-line-artifact

and this one from siggraph this year, which I thought had already been implemented? Doesn't seem to be working if so.

https://dl.acm.org/citation.cfm?id=3328172

Read the SIGGRAPH paper more closely - it applies to problems introduced by bump/normal mapped surfaces only. It is implemented and working.

Yeah the problem of shadow terminator for normal maps seems to be fixed thanks to https://developer.blender.org/D5399

Cleary the shadows are smoothed enough where the artifact occurs.

However @Lukas Stockner (lukasstockner97) I was wondering why you limited it to diffuse surfaces ? the paper does not mention anything about any kind of diffuse restriction, so in the cloth scene provided by Stefan if you replace the diffuse BSDF with a glossy one the artefact pops up again. Two screenshots:

So I think the fix should be generalized to any kind of scattering behavior (except maybe toon ones ?)


Now the original and "simpler" shadow terminator issue (this one https://computergraphics.stackexchange.com/questions/4986/ray-tracing-shadows-the-shadow-line-artifact) is a different problem. I don't think it can be solved without adding lighting from the backface of the triangle (in bsdf_eval function and sample probably, doing something in the else case of "if (dot(sd->Ng, omega_in) >= 0.0f) {"). That's a bit cheating but, after all, interpolated normals are a cheat. Also in my experience it is hard to do without suffering from side effects such as light leaking occuring in some cases. Maybe it should be a boolean parameter that control this behavior ?
Right now it is true that low resolution assets such as video game assets are just not acceptable when rendered with Cycles, or you have yo use large area lights to mitigate the problem :/

On the video above you can see that the shading issue is apparent even on the far right polygons where the light ray isn't passing through other faces and then hitting the back side of neighbouring faces.

Laurent Noel (c2ba) added a comment.EditedOct 17 2019, 7:19 PM

I'm not really sure but I think it might be related to the linear interpolation of the vertex normals on the face, that does not match the true normals a circle would have (correct me if I'm wrong). So each face get a set of interpolated normals that is slightly wrong, and since the interpolated normal is used to build the shading normal with the tangent normal map, correlation artifacts appear.
Try the following: build a cylinder in blender, remove the top and bottom faces, then in edit mode select all faces and subdivide them multiple time. Put your cylinder in smooth shading and you will see the the individual original low res faces appear, even with opengl rendering.
I guess this is because the new vertices created with subdivision have a normal computed from linear interpolation, but I'm not familiar with subdivision algorithms in general and the one used by blender in edit mode so I can only guess. Try to display the normals on edit mode with the overlay, you will see that the normals of the newly created vertices do not create a perfect circular pattern.

You have to imagine that Cycles is using theses kind of wrong normals on each point of a face. It is ok for low frequency shading, but when you add a high frequency normal map on top of that, the error becomes visible.

In brief I think your issue cannot be solved as long as interpolated normals are obtained from linear interpolation (which is the case for all renderers I can think of), you need a mesh with higher subdivisions, where normals are correctly computed (in the case of the cylinder, a catmull clark subdiv modifier gives me perfectly circular normals).

I've tried the same mesh in redshift and also in mantra. The shading issue is not there in either. See this bug report where I posted the comparison.

https://developer.blender.org/T70716

The test you suggested of adding extra subdivisions in edit mode should result in the shading artefacts because there's more geometry between the original edges. The same way you would intentionally add a hard edge by putting two edges close together.

The issue is still there even if using a subdivision modifier, it's just less noticeable because the angle between the faces is lower, so the difference in shading per face is lesser. There is still no smoothing of the shading between neighbouring face. Here's the same mesh with a subidivion modifier added:

Here's a perfectly cylindrical mesh with perfectly cylindrical normals:

with bump map:

without bump map:

Here's the .blend if anyone wants to see if they can improve the coding to get this shading correctly. It's almost as if the bump/normal maps are using the face normal instead of the merged vertex normals for overall shading?

The test you suggested of adding extra subdivisions in edit mode should result in the shading artefacts because there's more geometry between the original edges. The same way you would intentionally add a hard edge by putting two edges close together.

Ok after another test I'm convinced: while subdividing in edit mode Blender does not seem to interpolate the normal at all (all new vertex has its normal being the same as the face it was created on), so the test does not show anything sorry ^^'

I'm gonna try with Arnold tomorrow at work to see how it behaves for that scene.

I still think it might have to do with the linear interpolation of normals but again, not sure at all, some experiments in the code are required (found this old paper https://webhome.cs.uvic.ca/~blob/publications/p397-van_overveld.pdf addressing the fact that linear interpolation of normals is not a good approximation, but no mention about the interaction with normal maps).

Also the artifact is far less visible with Eevee:

Cycles:

Eevee:

For subdivision modifier, putting it to 3 seems to be enough to completely get rid of it:

subdiv 1:

subdiv 2:

subdiv 3:

subdiv 4:

Tried with Luxrender, it does not have the problem:

  • Lux:

  • Cycles:

It seems strongly influenced by the normal adjustment that was introduced in: https://developer.blender.org/D2574
For example by removing the line https://developer.blender.org/differential/changeset/?ref=147520 I get the following image:


For comparison, I have this without remove the line:

Some triangles are still slightly visible on the right of the mesh, but nothing like the original.

Also it strikes me how different is the overhaul look of the mesh when the normal is not corrected according to D2574. It's like the illumination is completely different Oo

This comment was removed by michael campbell (3di).

Is there any way to vote to get this escalated to high priority? I literally can't use cycles at all in scenes with curved surfaces that have bump/normal maps......which is most scenes.

No, we judge the priority, it is not determined by voting.

This seems to fix it. Left is a bump map, middle is a normal map, right is a normal texture run through an alternative node setup instead of the normal map node. The bump is controlled by map range nodes instead of the normal map's strength parameter.

the normal pass:

The file:

Slightly better version:

Same again, left is the bump map node, middle is the normal map node, right is my custom node setup, but this time mixing the object smooth normals back in.

Normal pass:

file:

Also works with a bump texture:

It can also be used to get rid of shading artefacts on smooth surfaces:

For what it's worth, a little insight in what other renderers do (in the case without bump maps)
LuxRender: Nothing.
"the black triangles on the edge are unavoidable, their geometric normal points away from the light source so there is no way to fix them aside increasing tessellation and/or using softer shadows (due to a larger light source, etc.)"
https://forums.luxcorerender.org/viewtopic.php?f=5&t=1286

RedShift: Shadow ray bias derived from triangle size and the deviation between geometric and shading normal
https://docs.redshift3d.com/display/RSDOCS/Shadow+Ray+Biasing

RedShift: Shadow ray bias derived from triangle size and the deviation between geometric and shading normal
https://docs.redshift3d.com/display/RSDOCS/Shadow+Ray+Biasing

For this one to work, some change is required in bsdf evaluation because right now the contribution is completely black when dot(Ng, omega_in) < 0.

I don't think there's any way around this without ignoring backfacing geometric normals.

@Stefan Werner (swerner) Here's another piece of test geometry. This time the parts of the model with the artefacts aren't that low poly, also there's no normal map applied. Not sure if it's actually a bit worse than in previous builds? Currently I'm using 2.82 (sub 1), branch: master, commit date: 2019-11-22 18:14, hash: rB373e936e3e64

I was made aware that Appleseed has introduced a workaround for the shadow terminator. Their method, just like the fix for the bump map issue, is pushing the shadow terminator towards the light source, that is darkening the surface overall.

A quick and dirty attempt at implementing it in Cycles:
https://github.com/skwerner/blender/commit/5de4c6371d8f772e04e6c0383dfd8f1be6a29bd1

@Stefan Werner (swerner) , awesome! To get this, do I have to download blender from github and build myself, or is this in todays daily build from the main website? Never build it before.

You'd have to build your own, and I consider this patch nothing more than a proof of concept. In my limited testing, it appeared to break CUDA rendering and I haven't tested it BRDFs other than diffuse and principled surface.

I quickly tried your fix on a sphere. The parameter is animated from 0 to 1 over 100 frames. It seems to work well but need to be fine tuned to get rid of artifacts while not loosing too much lighting. I also get funny results starting from shadow_terminator_offset = 0.67 approximately :p

Hi, Franz from appleseed here.

A quick and dirty attempt at implementing it in Cycles:

Awesome :)

I also get funny results starting from shadow_terminator_offset = 0.67 approximately :p

Indeed, the theorethical maximum limit before "ringing" appears is exactly 2/3 (0.666...).

I noticed that as the offset is increased, the edge of the shadow becomes harder, would this need to be compensated for in the code?