How to make shadows darker on transparent objects?

In this example:

The shadow on the pink surface from the sphere is much lighter than the shadows on the teal background.

How can we make the shadow on the pink surface darker like it is on the teal background surface?

Original post, for more detail

The center plane is composed of a DOM element beneath the canvas (rendered with CSS3DRenderer), and shadow is casted onto a transparent plane that is aligned with the DOM element (with WebGLRenderer). The transparent plane is what slices the Sphere as well as the background Plane in order to give the illusion that the DOM element is intersecting with the WebGL meshes.

I’d like to make the lighting consistent on all the objects so the shadows are the same “darkness”.

How can I achieve this?

One thing I tried to do is change the opacity of the transparent Plane (line 139) to something higher than 0.08, like 0.5, and the shadow will be more realistic:

But the negative side effect of this is that the DOM element will seem to get darker overall. For example, if I change the opacity to 0.7, then the element is too dark:

And opacity of 1 completely hides the element:

How can we achieve better lighting on fully transparent surfaces to give a better effect of lighting on the DOM element (darker shadows without darkening the DOM element)?


Maybe if we had selective lighting in Three.js, I could adjust the effect using multiple lights (some for shadows, others for lighting). There would be two point lights: one point light casts shadow on the background, and the other one casts shadow onto the transparent plane representing the DOM element. I could then adjust the shadow qualities for each surface independently for each light, thus being able to finally make the whole scene look like it has more realistic lighting.

Bump. Any ideas?

hey @trusktr

So, this is not an answer to your question, but rather an explanation as to “why” it happens. The object has certain level of opacity, let’s say 0.5 (half-transparent), this means that when it is shaded, the shader only applies half of the color values and uses the rest from the background. Shader includes shadow also.

If you want it darker - I would suggest writing a custom shader.

in terms of realism, the behaviour you see is correct, a shadow on a translucent surface will be lighter than that on an opaque surface. What damages the aesthetic is the dark shadow behind the translucent occluder, but there is no easy way around that as shadow map is generated using a single layer of depth, so things are either in shadow or they are not, there is no notion of transparency there.

2 Likes

Hello Alex, thanks for the insight!

(by the way, adjusted the shadow bias so the original example doesn’t have so many artefacts)

That’s what I’m thinking.

That makes sense.

The following is another version of it where the shadow looks more consistent across all the surfaces, but as you can see the opacity of the transparent object has to be higher for it to look right, but then it blocks the underlying DOM content too much:

No tricks that you can think of? Or is a custom shader the only way to do it?

I think eventually I’ll get around to a custom shader, so I can have more control, but I’ve got other things in the pipeline.

@trusktr

looks much better :slight_smile:

what you want requires multiple shadow maps, a shadow map does not have a notion of transparency, since it’s calculated on a depth buffer. If you want shadows for transparent parts - you basically have to build several shadow maps or do some ray-tracing from a depth buffer to detect any transparent surfaces.

  1. hide all transparent surfaces
  2. build depth buffer for the scene from perspective of the light
  3. render shadow map from depth buffer
  4. render transparent surfaces onto the shadow map

it will still produce wrong result, as there will be 1 level of shadow, but you can get much better looking shadows for transparent occluders

@Usnul Sounds interesting! Any live examples of this that you know of?