Skip to content

Specify light() only runs for pixels affected by light - #12174

Open
Arecher wants to merge 1 commit into
godotengine:masterfrom
Arecher:Specify-`light()`-only-runs-for-pixels-affected-by-light-1
Open

Specify light() only runs for pixels affected by light#12174
Arecher wants to merge 1 commit into
godotengine:masterfrom
Arecher:Specify-`light()`-only-runs-for-pixels-affected-by-light-1

Conversation

@Arecher

@Arecher Arecher commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
  • Added a small section to specify that the light() function is only ran when the pixel is actually affected by at least one light.

Notes

The light() function is called for every light in every pixel. It is called within a loop for each light type.

I think these original two lines could be merged with my new additions, but since I am not fully confident about the potential implications of changing those lines or how to better explain them, I left them as is.

Reason for change

I added this because, even after reading the documentation, I did not realise that simply calling ALPHA = 0.0 in the fragment(), followed by a ALPHA = 1.0 in the light() would make the pixels only be shown if they are lit. I presumed the light() function would always be called if it was overwritten, even when the pixel was 'unlit'. In my mind this would simply add a light value of 0,0, which means any hardcoded overwrite such as ALPHA = 1.0 would apply to everything. In hindsight it makes sense, but since it was not specified, I thought I'd avoid future readers the trouble.

I personally used this for a shader for particles, making the particles only visible in light.

Open questions

I originally added the following example, but I have removed it:

for example setting ALPHA = 0.0 in the light() function will cause lit
pixels to become transparent, while unlit pixels remain opaque.

But lit/unlit are ambiguous terms, and user will likely read in-light/in-shadow instead, which is incorrect. It also requires the fragment() to have emission on, to properly show this, and without ATTENUATION it doesn't show as per-pixel, but creates chunks which are either affected or not.

image

So while I think an example of how this could be used could be helpful for making sure readers understand the implications, it would become too specific and too involved.

One thing I'm not fully sure on how to properly convey, and frankly I was very surprised by, is that light() also runs on pixels that are outside the light's range, but use the same Viewport/Screen pixel. Presumably this is because the model is going through the transparency pass, but I don't see why that would require it to compute light() onto the model itself, if the light source is far behind the model.

image

- Added a small section to specify that the `light()` function is only ran when the pixel is actually affected by at least one light.
@Arecher
Arecher requested review from a team as code owners July 24, 2026 20:36
@Arecher

Arecher commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

The introduction documentation here makes a similar statement as I have added to the Spatial Shader documentation.

The light() processor runs per pixel too, and it runs once for every light that affects the object. It does not run if no lights affect the object.

Although clearly "It does not run if no lights affect the object" is not the entire story, since the last screenshot shows it does run even when the light is too far away to affect the object. (The spotlight being ~100m away, and having a range of ~10m)

@skyace65 skyace65 added enhancement area:manual Issues and PRs related to the Manual/Tutorials section of the documentation topic:shaders labels Jul 26, 2026

@clayjohn clayjohn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This isn't really true. So I don't think it should be added to the docs.

The actual behaviour is very nuanced and differs depending on the renderer. Even in the Forward+ renderer, which is what it seems you tested with, it isn't true. In the forward+ renderer the light shader can run for the whole cluster that contains the light.

The exact number of pixels that run the light shader is an implementation detail and depends on a variety of factors. The only safe thing to say is that it can run for any pixel touched by the object, but is only guaranteed to run for all pixels in the object touched by the light.

@Arecher

Arecher commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for explaining! I suspected it was more complicated than I had originally expected the more I tested it.

When you say

The only safe thing to say is that it can run for any pixel touched by the object, but is only guaranteed to run for all pixels in the object touched by the light.

You specifically mean that it is guaranteed to run for all pixels that are touched by light on the object, rather than guaranteed to run for all pixels on the object, if the object is hit by any light, correct?

Comment on lines +550 to +552
Keep in mind the ``light()`` function only runs for pixels that are affected by at least one light source, and is
completely skipped for pixels that are not hit by any light and shadow. You can use this to create different behavior for
lit and unlit pixels in your shader.

@Arecher Arecher Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
Keep in mind the ``light()`` function only runs for pixels that are affected by at least one light source, and is
completely skipped for pixels that are not hit by any light and shadow. You can use this to create different behavior for
lit and unlit pixels in your shader.
The ``light()`` function is only guaranteed to run for pixels that are affected by at least one light source, and
*might* be completely skipped by the renderer for pixels that are not hit by any light or shadow at all.

Not sure if this addition (even with changes to make it more accurate) is worthwhile of being added to the docs anymore though, since it's a little vague and not directly applicable.

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

Labels

area:manual Issues and PRs related to the Manual/Tutorials section of the documentation enhancement topic:shaders

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants