All,
I am using DirectionalLight with setShadowCaster on and default shader (render.setShaderAuto). This causes the planar surface on which the shadows are cast appear imperfect with a jagged appearance/ripples/concentric circles (see attached pic).
There have been some previous discussion on this topic in the forum and the solutions ranged from changing depth offset, depth bits, setting two sided, anti-aliasing, film size etc. None of this fixes the issue for me. My graphics card is Radeon RX Vega 10 and I use pandadx9 (but same issue with opengl as well). Iāve heard that shadow issues donāt occur in NVidia - but I canāt test on it.
Below is relevant code for reference. If it helps, some h (e.g. 180) have lower ripples than others (e.g. 45). The problem is triggered only when shadows are on.
Or conversely, have you perhaps set your floor-model to be ātwo-sidedā (or disabled backface-culling for the floor, perhaps in your modelling program)?
I had tried setting two-sided and it makes no difference. In the below, Plane is the object that represents the floor. I presume setTwoSided is the function you are referring to, or is there another way from panda3d to disable culling?
Ah, I was perhaps unclear (for which I apologise): I was concerned that you might have made the plane two-sided, as thatās one potential cause of the problem that youāre seeing, I think.
Indeed, that is the method that I had in mind, I do believe!
There are other ways of disabling culling, I believeābut Iād expect doing so to make things worse, if anything.
In short, what appears to be happening is that the planeās shadow is being rendered at more or less the same depth as the plane itself. As a result, thereās some depth-fighting occurring, with some shadow-pixels being found to be slightly closer than their corresponding visual-pixels, and some visual-pixels being found to be slightly closer than their corresponding shadow-pixelsāhence the artefacting that you see.
Now, usually this sort of thing is handled (in part) by only rendering back-faces into the shadow-buffer (something that should, I think, be happening automatically). The should prevent a given face from being (much) rendered to both the view and the shadow-buffer, and thus should (largely) prevent this sort shadow depth-fighting.
However, it appears that this isnāt happening in your case, which is why I thought that perhaps you had made the floor-plane two-sidedā¦
Appreciate the follow up. Toggling this twoSided flag doesnāt help as you noted.
There was a discussion about this same topic a few years ago. e.g. see issue (1) in below link and I even tried setting depth offsets and depth-bits as user rdb suggested. Doesnāt improve my case.
I am using Windows 10 though. Makes me suspect I am not alone with a small percent of users facing this issue potentially based on graphics card. Perhaps writing a custom shader could help but I guess that wouldnāt be trivial for shadows.
Hmmā¦ The thing is, if itās your graphics card, then why does it not appear to affect the table in your screenshot? I see no āshadow acneā (as I believe that itās called) on the tabletop.
Would you be willing to share a file containing the floor-plane, please? Perhaps investigating the model itself will provide some clueā¦
table1.blend -> The model in blender 3.0.1
table1.glb -> The model exported in glTf from blender to use in panda3d
shadow_issue.py -> The python code for rendering with lights/views etc.
out_panda3d.jpg -> A screenshot of the generated image showing ripples.
You will note in line 104 of python file that I use pandadx9 (opengl doesnāt generate shadows in my machine).
Hmmā¦ For whatever reason, Iām not seeing shadows at all when I run the program.
However, if I rotate the view to fall beneath the floor-quadā¦ I still see the floor. Which implies that either the floor is two-sided, or there are two floor-planes, one facing up and once facing down. Either way, I suspect that this may well be the source of the problem.
Duh! You are right - if I go to blender, explicitly set the material settings Backface Culling as True (by default it seems to be False) and then export the glb, the shadow ripples/acne vanish.
I feel a bit embarrassed that this was suggested first up by you all, but I incorrectly relied on the setTwoSided function in panda3d which has let me down:-) I tried both model.find(āPlaneā).setTwoSided(False) [which should affect all its children?] and also tried model.find(āPlaneā).findAllMaterials()[0].setTwoside(False) but no change in the floor backside being drawn. A mystery for another day then, at least I can now disable it in blender.
Separately, is there a recommended solution for avoiding shadow acne IF backface culling is off then (say we need to rotate and see the floor backside)?
Based on some quick testing, it looks like an override value may be called for in order to overcome the effect of the āCullFaceAttribā present in the geometry. Something like this:
self.model.setTwoSided(False, 1)
# "1" being the override value
Note, however, that doing this breaks your table, which seems to be inside-out and only rendering properly because itās two-sidedā¦
Hmmā¦ A sufficiently large shadow-bias might do itābut it may be that it calls for an especially large bias. (Which might then introduce its own issues.)
Itās likely that the exporter is exporting double-sided planes from Blender as a pair of back-to-back planes, in which case thereās nothing for setTwoSided to do, since itās two separate polygons.
I considered that, but a call to ālsā seems to suggest otherwise.
In any case, a call to āsetTwoSidedā with an override value does actually seem to work, suggesting that it is a matter of the plane being two-sided, but simply having a priority higher than the default.
I was also very tired of it. I have found advice in several places on the internet to fix this by setting:
base.render.set_depth_offset(1)
But this solution (with a value of +1) did NOT work for me. Until finally I started experimenting myself and by trial and error I came to set a completely different value (-3):
Honestly, Iām not sure why it works, because I donāt think it should. But, as the saying goes, when something is stupid but works, that is, it is clearly not that stupid.
PS: My case is macOS on the M1 processor. You, I see, run on a Windows PC, so my solution may not necessarily work.
You should really be setting this only when rendering the scene from the shadow cameraās point of view, which you can do by manipulating the lightās initial state via light.node().initial_state = light.node().initial_state.set_attrib(DepthOffsetAttrib.make(...))