Specify light() only runs for pixels affected by light - #12174
Conversation
- Added a small section to specify that the `light()` function is only ran when the pixel is actually affected by at least one light.
|
The introduction documentation here makes a similar statement as I have added to the Spatial Shader documentation.
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) |
clayjohn
left a comment
There was a problem hiding this comment.
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.
|
Thanks for explaining! I suspected it was more complicated than I had originally expected the more I tested it. When you say
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? |
| 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. |
There was a problem hiding this comment.
| 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.
light()function is only ran when the pixel is actually affected by at least one light.Notes
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.0in thefragment(), followed by aALPHA = 1.0in the light() would make the pixels only be shown if they are lit. I presumed thelight()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 asALPHA = 1.0would 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:
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 withoutATTENUATIONit doesn't show as per-pixel, but creates chunks which are either affected or not.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.