Skip to content

Conversation

@pcwalton
Copy link
Contributor

@pcwalton pcwalton commented Dec 27, 2025

Bevy doesn't currently ever apply parallax correction to cubemaps, so reflections are rendered as though the environment were infinitely far away. This is often acceptable for outdoor scenes in which the environment is very distant, but for indoor scenes and dense environments this is undesirable. The standard solution for this problem is parallax correction, in which each reflection probe is augmented with a bounding box, and a raytrace is performed against the bounding box in order to determine the proper direction for sampling the cubemap.

This commit implements parallax correction in Bevy for light probes in an opt-out manner. (You may add the NoParallaxCorrection component to a LightProbe with an EnvironmentMapLight in order to opt out of it.) The bounding box used for parallax correction is assumed to be identical to the bounding box of the influence of the reflection probe itself. This is a reasonable default and matches what Blender does; it's what you want when you have, for example, a cubemap that captures the interior of a rectangular room. However, a future follow-up PR may wish to extend this so that the bounding box used for parallax correction might not coincide with the bounding box used for the influence of the reflection probe. This would require increasing the GPU size of the light probe data, so in order to keep this patch small and self-contained, I opted to defer this potential future enhancement to a follow-up.

The patch is generally straightforward, adding only the minimal enhancements to the LightProbe trait and the GPU representation of light probes needed to propagate the parallax correction flag through the rendering pipeline.

Additionally, this commit fixes a bug whereby the transform of each cubemap reflection probe wasn't being taken into account in the shader. I believe that this was being masked because most cubemaps are rendered in world space and therefore most cubemap reflection probes have an identity rotation.

A new example, pccm, has been added, demonstrating the effect of parallax correction. It shows a scene consisting of an outer textured cube with an inner rotating reflective cube. The outer textured cube contains a reflection probe containing a snapshot of the scene (prerendered in Blender). Parallax correction can be toggled on and off in the example in order to demonstrate its effect.

Bevy doesn't currently ever apply parallax correction to cubemaps, so
reflections are rendered as though the environment were infinitely far
away. This is often acceptable for outdoor scenes in which the
environment is very distant, but for indoor scenes and dense
environments this is undesirable. The standard solution for this problem
is *parallax correction*, in which each reflection probe is augmented
with a bounding box, and a raytrace is performed against the bounding
box in order to determine the proper direction for sampling the cubemap.

This commit implements parallax correction in Bevy in an opt-in manner.
Add the `ParallaxCorrect` component to a `LightProbe` with an
`EnvironmentMapLight` in order to opt into it. The bounding box used for
parallax correction is assumed to be identical to the bounding box of
the influence of the reflection probe itself. This is a reasonable
default and matches what Blender does; it's what you want when you have,
for example, a cubemap that captures the interior of a rectangular room.
However, a future follow-up PR may wish to extend this so that the
bounding box used for parallax correction might not coincide with the
bounding box used for the influence of the reflection probe. This would
require increasing the GPU size of the light probe data, so in order to
keep this patch small and self-contained, I opted to defer this
potential future enhancement to a follow-up.

The patch is generally straightforward, adding only the minimal
enhancements to the `LightProbe` trait and the GPU representation of
light probes needed to propagate the parallax correction flag through
the rendering pipeline.

Additionally, this commit fixes a bug whereby the transform of each
cubemap reflection probe wasn't being taken into account in the shader.
I believe that this was being masked because most cubemaps are rendered
in world space and therefore most cubemap reflection probes have an
identity rotation.

A new example, `pccm`, has been added, demonstrating the effect of
parallax correction. It shows a scene consisting of an outer textured
cube with an inner rotating reflective cube. The outer textured cube
contains a reflection probe containing a snapshot of the scene
(prerendered in Blender). Parallax correction can be toggled on and off
in the example in order to demonstrate its effect.
@pcwalton
Copy link
Contributor Author

After this gets initial review, I'll add the new baked cubemap reflection assets to https://github.com/bevyengine/bevy_asset_files so that we don't have to add them to the main repo.

Screenshot of the new pccm example with parallax correction on:
Screenshot 2025-12-27 142659

And with parallax correction off:
Screenshot 2025-12-27 142710

@JMS55
Copy link
Contributor

JMS55 commented Dec 27, 2025

Can we make ParallaxCorrect not-opt-in? I think it's generally helpful to have less knobs to tune, and I don't see anyone wanting to turn this off.

@pcwalton
Copy link
Contributor Author

@JMS55 OK, I made parallax correction opt-out instead of opt-in.

@pcwalton pcwalton added the A-Rendering Drawing game state to the screen label Dec 27, 2025
@pcwalton pcwalton added S-Needs-Review Needs reviewer attention (from anyone!) to move forward C-Feature A new feature, making something new possible labels Dec 27, 2025
@JMS55
Copy link
Contributor

JMS55 commented Dec 27, 2025

Closes #16709

Copy link
Contributor

@atlv24 atlv24 left a comment

Choose a reason for hiding this comment

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

I played around with this - seems like something isnt quite right. Digging into the math now.

{DDEE8D8A-7DA5-4137-BB35-996CD1BE9309}

intensity: ENVIRONMENT_MAP_INTENSITY,
..default()
},
Transform::from_scale(Vec3::splat(5.0)),
Copy link
Contributor

Choose a reason for hiding this comment

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

The glb cube spans -5.0..5.0 and as such has size 10x10x10, but the environment map light is a unit cube at the origin (-0.5..0.5) so this needs to be 10.0 actually

Suggested change
Transform::from_scale(Vec3::splat(5.0)),
Transform::from_scale(Vec3::splat(10.0)),

Copy link
Contributor

Choose a reason for hiding this comment

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

Doing this seems to cause the glb to have a form of z-fighting, where it can't decide if its inside or outside the environment light so some pixels get lit by it and some dont. Might need to exclude it from env map lights.

@atlv24
Copy link
Contributor

atlv24 commented Dec 29, 2025

I think the glb is just wrong.

Copy link
Contributor

Choose a reason for hiding this comment

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

+23 MB +8 MB for this example is not justifiable, for reference all the other bevy assets combined are 54 MB. Can we put this on the web assets repo or use something smaller?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, my plan was to put this on bevy asset files once it’s reviewed.

Copy link
Contributor

@atlv24 atlv24 left a comment

Choose a reason for hiding this comment

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

The implementation is correct and high quality. The example is comprehensive, has a few flaws though. The only blocker is the asset sizes imo.

Copy link
Member

@tychedelia tychedelia left a comment

Choose a reason for hiding this comment

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

Looks great thanks. Approve being move of assets to web.

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-Feature A new feature, making something new possible S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants