Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Point/Spot light docs #6813

Closed
wants to merge 2 commits into from
Closed

Point/Spot light docs #6813

wants to merge 2 commits into from

Conversation

JMS55
Copy link
Contributor

@JMS55 JMS55 commented Dec 1, 2022

Documents some fields of PointLight and SpotLight.

I'm unsure whether range is measured from the surface of the point/spot light, or always from the center regardless of its radius.

I'm also unsure what units range and radius are in (meters?), and how to document the rest of the fields.

@JMS55 JMS55 changed the title Point light docs Point/Spot light docs Dec 1, 2022
@james7132 james7132 added A-Rendering Drawing game state to the screen C-Docs An addition or correction to our documentation labels Dec 1, 2022
pub range: f32,
/// The size (radius) of the point light itself.
///
/// A radius of 0.0 (the default) is a point with no measureable size, while a radius of 1.0 is a unit sphere, etc.
Copy link
Contributor

@doup doup Dec 1, 2022

Choose a reason for hiding this comment

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

I don't know if it's the case in Bevy, but usually the size of the light source affects the sharpness of the shadows. The bigger the light source (keeping the light source at the same distance from the object), the softer the shadows get as they are further from the object. I mention the distance, because the sun is a huge sphere, but since it's so far that it works as a directional light giving mostly sharp shadows (?).

Might be interesting to mention?

Rendered in Blender:

Light radius: 0.1m
image

Radius: 1m
image

Radius: 2m
image

Copy link
Contributor

@superdump superdump Jun 23, 2023

Choose a reason for hiding this comment

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

There are no soft shadows yet. This would be addressed by something like #3631 . When I was hacking on soft shadows using the PCSS method, I was using the light's radius for this kind of effect.

@Selene-Amanita
Copy link
Member

Selene-Amanita commented Jun 23, 2023

Bumping this because I think it's important. I tried to test things with bevy_inspector_egui, a custom pivot camera, a PointLightCamera, a plane and a cube.

I can confirm for PointLightBundle:

  1. range works as follow: anything outside of the range will not be lit at all by the light, and the intensity perceived will be an exponential of getting close to the center from the limit of the range (or logarithm of getting close to the limit of the range from the center), meaning if you're really close to the limit of the range you'll be barely lit even if you have a very very high intensity

  2. range extends from the center, not the surface, and like if you have a range of 20., and a cube that has a size of 2. and "orthogonal" to the light, the center of the cube has to be at 21. for the face facing the light to not be lit anymore

  3. range works on tilted surfaces (I tried to rotate my cube).

  4. I can't put a negative range in bevy_inspector_egui, maybe there's a system preventing that, who knows

5.radius doesn't seem to change anything about how the shadow are handles, it's more about the "reflection" (? probably) "glow" (? probably not), example:
image
image

  1. radius seem to be intended to be between 0. and 1.
    image

  2. I can't put negative radius either. Weird, I feel like I did before but maybe I was dreaming (it's very late and I'm kinda high because of meds).

  3. Here's what happen when shadow_depth_bias is > 0. (high value to exagerate, the cube is resting on the plane ground):
    image
    This is weird because the best value seem to be 0., but the default is 0.2, so maybe it's to avoid an edge case or someting?

  4. shadow_depth_bias doesn't seem to be intended to be < 0.
    image
    image

  5. Not sure what shadow_normal_bias does (it is documented but the documentation is not clear to me and needs to be rewriten imo) but it doesn't seem to be intended to be close to 0. or negative if shadow_depth_bias is close to 0.
    image
    image
    image

  6. The whole "Real-world values for intensity" part/table should be put as a documentation of intensity imo, and clarify that it makes sense only if world units are equivalent to real-world meters (?).

  7. DirectionalLight have an illuminance field instead of intensity like Spotlight and PointLight, is there a good reason for that?

I can test more on demand, and may test SpotLight and DirectionalLight too (this last one should also be documented), but input from people who know rendering might be valuable here.

pub intensity: f32,
/// How far light extends from the surface (TODO: or center?) of the point light.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// How far light extends from the surface (TODO: or center?) of the point light.
/// How far light extends from the center of the point light.

pub intensity: f32,
/// How far light extends from the surface (TODO: or center?) of the spot light.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// How far light extends from the surface (TODO: or center?) of the spot light.
/// How far light extends from the center of the spot light.

pub range: f32,
/// The size (radius) of the spot light itself.
///
/// A radius of 0.0 (the default) is a point with no measureable size, while a radius of 1.0 is a unit sphere, etc.
Copy link
Contributor

Choose a reason for hiding this comment

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

Radius 1.0 would give the cone cut from a unit sphere according to the outer angle.

@superdump
Copy link
Contributor

The lighting mostly follows what Google's Filament renderer defines, but with a few tweaks from other places. Point and spot lights have their light falling off over distance in a physical way (proportional to 1 / distance^2).

Point lights with 0 radius can technically not exist physically, so some approximations need to be applied there, also if I recall correctly for when something is inside the radius of the light. I think we followed some Brian Karis / Unreal Engine ideas for that part. It's documented in the shader code. And then we apply some additional attenuation to the light so that it smoothly fades out to 0 at its range limit rather than abruptly stopping.

Bumping this because I think it's important. I tried to test things with bevy_inspector_egui, a custom pivot camera, a PointLightCamera, a plane and a cube.

I can confirm for PointLightBundle:

1. `range` works as follow: anything outside of the range will not be lit at all by the light, and the intensity perceived will be an exponential of getting close to the center from the limit of the range (or logarithm of getting close to the limit of the range from the center), meaning if you're really close to the limit of the range you'll be barely lit even if you have a very very high intensity

As noted above, the falloff is physically-modelled and is proportional to 1 / distance^2. Distances are measured from the centre. And additional non-physical attenuation is applied close to the range in order to smoothly fade out rather than abruptly cutting off.

2. `range` extends from the center, not the surface, and like if you have a range of `20.`, and a cube that has a size of 2. and "orthogonal" to the light, the center of the cube has to be at `21.` for the face facing the light to not be lit anymore

Correct.

3. `range` works on tilted surfaces (I tried to rotate my cube).

The range is a property of the light, so it depends how far the point on the surface of the object is from the centre of the light.

4. I can't put a negative `range` in bevy_inspector_egui, maybe there's a system preventing that, who knows

The light range calculations use 1 / range^2 so whether range is positive or negative it will behave based on its absolute value I think. It may be that the culling of objects or clustering gets messed up with negative range. Range should not be negative and I'm not sure if we check or limit that anywhere. A quick search suggests that we don't. But range should be > 0.0. A range of 0.0 may cause some divide by 0s...

5.radius doesn't seem to change anything about how the shadow are handles, it's more about the "reflection" (? probably) "glow" (? probably not), example: image image

As mentioned, the radius could be used for soft shadows in the future but that is not yet supported. Radius is used for lighting however and is a feature: https://bevyengine.org/news/bevy-0-6/#spherical-area-lights

6. `radius` seem to be intended to be between `0.` and `1.`
   ![image](https://user-images.githubusercontent.com/134181069/248137372-5f52455c-b3ec-4374-ad6d-1d30260b962e.png)

This is incorrect. >= 0.0 is the constraint I think.

7. I can't put negative `radius` either. Weird, I feel like I did before but maybe I was dreaming (it's very late and I'm kinda high because of meds).

I think negative radius is invalid.

8. Here's what happen when `shadow_depth_bias` is > `0.` (high value to exagerate, the cube is resting on the plane ground):
   ![image](https://user-images.githubusercontent.com/134181069/248137925-18a71735-71e0-451f-8c17-1c4eee525455.png)
   This is weird because the best value seem to be `0.`, but the default is `0.2`, so maybe it's to avoid an edge case or someting?

Too large values will detach the shadow from where the object contacts the surface on which it is resting and make it look like the object is floating.

shadow_depth_bias and shadow_normal_bias are to avoid 'shadow acne' or 'self-shadowing'. They are workarounds for a problem with shadow map resolution and depth precision with the way that shadow mapping using depth buffers works. From experience, avoiding self-shadowing with shadow mapping techniques is a major pain.

The shadow_biases example intends to allow users to familiarise themselves with this, but also good default values have been chosen.

The self-shadowing problem is that the shadow map textures are depth textures of geometry in the scene from the perspective of the light. So they contain the closest point to the light for each pixel in the texture. When rendering from a camera's perspective, in order to understand if the point on the surface of some geometry in the scene that is visible to the camera is in shadow or not, that point is transformed into the light's perspective, and then its depth or distance from the light is compared with the depth in the shadow map texture to see if there is something closer to the light. If so, the point is in shadow, if not then it is the closest point along that light ray from the light to the surface and it is lit. Because of limited shadow map texture resolution and limited depth precision, there can be small differences in the two depths when the point on the surface that is visible to the camera is actually meant to be the closest point to the light and it should be lit, but where the small differences make the comparison of the two depths indicate that there is something slightly closer to the light and so that point should not be lit. The depth bias moves the point in the scene that is visible to the camera, slightly closer to the light to try to compensate for these precision problems. The normal bias moves the point slightly away from the surface along the surface's normal, for the same reason.

The depth and normal biases are one of the standard approaches to dealing with these problems. Godot's documentation has a good writeup with images and I thought that we would do something similar at some point. https://docs.godotengine.org/en/stable/tutorials/3d/lights_and_shadows.html#shadow-mapping Their 'bias' is the depth bias, and their 'normal bias' is the same as our normal bias.

9. `shadow_depth_bias` doesn't seem to be intended to be < `0.`
   ![image](https://user-images.githubusercontent.com/134181069/248138196-97bee56e-6eef-4e25-a94b-b93a5f5081cb.png)
   ![image](https://user-images.githubusercontent.com/134181069/248138260-853d3a6f-3071-4b33-9446-76914de42821.png)

Right, this is self-shadowing. Too small values will exhibit self-shadowing. The defaults are meant to tradeoff avoiding self-shadowing while being as small as possible to avoid detaching the shadows from the contact points. While it could be negative, in practical terms that will just cause self-shadowing and so is never going to be what a user wants.

10. Not sure what `shadow_normal_bias` does (it is documented but the documentation is not clear to me and needs to be rewriten imo) but it doesn't seem to be intended to be close to `0.` or negative if `shadow_depth_bias` is close to `0.`
    ![image](https://user-images.githubusercontent.com/134181069/248138499-d4ba3a09-e9d2-4199-953f-54633ea02bbb.png)
    ![image](https://user-images.githubusercontent.com/134181069/248138550-c4846854-869e-44cb-a747-e81ac071eab9.png)
    ![image](https://user-images.githubusercontent.com/134181069/248138658-fed48c15-8008-49a6-8041-ff2d6f0bd531.png)

Again it should not be negative for practical purposes as that would cause self-shadowing which is undesirable. It should be as small as possible, >= 0.0, and enough to along with the depth bias avoid self-shadowing.

11. The whole "Real-world values for intensity" part/table should be put as a documentation of `intensity` imo, and clarify that it makes sense only if world units are equivalent to real-world meters (?).

Good point, the lighting is definitely based on SI units of measure and so world units should be in metres for correct lighting.

12. `DirectionalLight` have an `illuminance` field instead of `intensity` like `Spotlight` and `PointLight`, is there a good reason for that?

Yes. DirectionalLight is meant to model a light that is very far away and so has practically parallel light rays in the direction of the light. Google's Filament renderer has a lot of documentation on the choices: https://google.github.io/filament/Filament.html#lighting

@JMS55
Copy link
Contributor Author

JMS55 commented Jun 23, 2023

Closing this PR so that we don't have to go through me to work on this, another PR will get opened instead :)

@JMS55 JMS55 closed this Jun 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Docs An addition or correction to our documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants