SpotLight only reflects off of wall when the target wall is *completely* within the light's frustum?

This thread is a continuation of this thread:

I have moved it to a new thread because a subtle interaction between many parts of my code led to the update() method of the SpotLightHelper object I use with my SpotLight not being called. That has been fixed and now I can see the proper dimensions of the light’s frustum.

It now seems that the real problem I am having is that I don’t see any light reflected of the SpotLight’s target object, which is a MeshLambertMaterial material, if the frustum of the SpotLight does not encompass the entire surface of the rectangular MeshLamberMateria that makes up a wall in my scene. Can someone give me an idea as to why this is happening and how to fix it, assuming I have the right hypothesis for the problem?

Look at these screenshots. The first one shows frustum of the SpotLight definitely smaller than the the wall surface.

I widen the angle of the frustum until it just fits within wall surface and still no light:

The instant I again widen the light’s angle and make the frustum exceed the boundaries of the entire MeshLambertMaterial of the wall, the light appears:

And even wider and the wall and it’s neighbors are really lit up:

What could cause a situation like mine when the target of the object only shows any light at all once the SpotLIght’s frustum completely encompasses it, not just part of it? Note, the same behavior occurs if instead of widening the angle or the SpotLight beam, I move the SpotLight further away from the wall so that again, the frustum encompasses the entire wall.

What version of three.js is this, and do you see the same behavior with another material type, like MeshStandardMaterial?

In past versions, MeshLambertMaterial used per-vertex lighting and not per-pixel (unlike other materials) which might explain what you’re seeing.

3 Likes

Thank you! That was it. (See screenshot)

Any harm in just using MeshStandardMaterial instead of MeshLambertMaterial I noticed that the light looks much smoother, at least to my eyes, with MeshStandardMaterial`.

As far as performance concerns, I have exactly 60 of these surface in my world, 30 double-sided panels. Could that present a problem? I don’t see any performance issues on my PC but I’m asking just in case.

For the curious that don’t know how per-vertex light rendering works and why it led to my problem, I got this helpful text from ChatGPT:

“Per-vertex” lighting in Three.js is a shading technique that calculates the lighting of a 3D mesh based on the lighting information provided at each vertex of the mesh. This means that the lighting is interpolated across the surface of the mesh between vertices, which can lead to a less realistic appearance compared to more advanced shading techniques like “per-pixel” or “physically-based” shading.

When using a “per-vertex” lighting technique, the lighting calculations are performed on the vertices of the mesh rather than on every pixel of the surface. This means that the lighting values are only calculated for the vertices, and then interpolated across the surface of the mesh between them. Therefore, if a part of the mesh is outside the frustum of a SpotLight, the vertices that are outside the frustum will not have their lighting calculated, and therefore, the interpolated lighting values across the surface of the mesh will also be affected, leading to a darker appearance of the mesh outside the frustum.

In contrast, more advanced shading techniques like “per-pixel” or “physically-based” shading calculate the lighting for every pixel on the surface of the mesh, regardless of whether the pixel is inside or outside the frustum of a light source. This can result in a more realistic appearance of the lighting on the surface of the mesh.

So since none of the vertices were inside the SpotLight frustum, no light was rendered. This also explains why neighboring did light up once the frustum was large enough to encompass one wall, the vertices belonging to the edges of the neightboring walls were also in the SpotLight’s frustum.

I don’t think I can add anything to ChatGPT’s explanation, nice! :sweat_smile:

As far as performance concerns, I have exactly 60 of these surface in my world, 30 double-sided panels. Could that present a problem? I don’t see any performance issues on my PC but I’m asking just in case.

The cost is generally based on how many pixels you’re drawing per frame, and so closely tied to the resolution of your screen. A more expensive shader like MeshStandardMaterial doesn’t really cost that much if it only occupies a small part of your viewport.

Some mobile devices will have a hard time filling the screen with an expensive shader like MeshStandardMaterial, you may want something cheaper for them. In the newest versions of three.js (r144+) MeshLambertMaterial was changed to per-fragment shading, but it’s still much cheaper than MeshStandardMaterial. If you still want per-vertex shading for anything, MeshGouradMaterial is available (in the addons directory) for that.

1 Like

Would the rendering code for MeshLambertMaterial from the latest ThreeJS version be faster? If so, do you think I could just “copy-paste” drop in that code intor124, or is it likely to end up in one of those situations where I end up dragging in so much other dependent rendering code that the whole effort implodes?

It would be difficult to backport a newer material to an older three.js version, the shaders connect with other changes throughout the library. I would expect the older MeshLambertMaterial is equivalent to the newer MeshGouradMaterial, and that the newer MeshLambertMaterial is better-looking but slower.

1 Like