Skip to content

Conversation

@mrdoob
Copy link
Owner

@mrdoob mrdoob commented Oct 21, 2025

Related issue: #14161

Description

This weekend I was curious to see what Claude could do if I asked to research the latest advancements on RectAreaLight shadows and implement it on Three.js. After a few tries it ended up implementing PCSS shadows.

Screen.Recording.2025-10-21.at.09.15.27.mov

https://raw.githack.com/mrdoob/three.js/rectarealight-shadows/examples/webgl_lights_rectarealight.html

It's cute but I wouldn't want WebGLRenderer to get more bloated.

In the process I think I found a solution for #9373 (I'll give it a proper go later this week) and also impelmented CircleAreaLight (no roughness/metalness).

Screen.Recording.2025-10-21.at.09.17.21.mov

https://raw.githack.com/mrdoob/three.js/rectarealight-shadows/examples/webgl_lights_circlearealight.html

I'll do another session with Claude later this week too to implement CircleAreaLight in WebGPURenderer to see how the resulting code looks like.

#define PCSS_NUM_SAMPLES 32
// Poisson disk samples for blocker search and PCF (32 samples for high quality)
const vec2 poissonDisk[32] = vec2[](
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a different set of poisson disk values (or at least sampling a different one per pixel) when performing the PCF step can help remove the blockiness / repetitiveness of the "blur" - it's always been a bit noticeable with the soft shadow option but is particularly obvious when the radius is widened, making things look like a series of LED lights shining rather than a single light surface:

Image

Compared to the three.js PCSS example and the PCSS Area Light Shadows demo I made years ago:

Image Image

Comment on lines +300 to +312
for ( int i = 0; i < BLOCKER_SEARCH_NUM_SAMPLES; i++ ) {
vec2 offset = poissonDisk[i] * searchWidth;
float shadowMapDepth = unpackRGBAToDepth( texture2D( shadowMap, uv + offset ) );
if ( shadowMapDepth < zReceiver ) {
blockerDepthSum += shadowMapDepth;
numBlockers += 1.0;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One area I thought that could be improved would be to scale the number of samples based on the light shape / size - a small or thin light, for example, would not need as many samples as a large planar light. Or perhaps the samples could be distributed more intelligently based on the light shape to help improve quality or performance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants