-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Implement irradiance volumes. #10268
Conversation
This commit implements *irradiance volumes* in Bevy, also known as *voxel global illumination* or simply as light probes (though this term is not preferred, as multiple techniques can be called light probes). Irradiance volumes are a form of baked global illumination; they work by sampling the light at the centers of each voxel within a cuboid. At runtime, the voxels surrounding the fragment center are sampled and interpolated to produce indirect diffuse illumination. The particular technique is known as *ambient cubes*, popularized by [Valve SIGGRAPH 2006]. Ambient cubes work by recording a 1×1 cubemap at each voxel position. This was chosen over the perhaps-better-known method of spherical harmonics because Blender's Eevee renderer can be easily used to generate them via the tool in [`bevy-baked-gi`]. Also, ambient cubes require fewer table lookups than second-order spherical harmonics do and don't suffer from ringing artifacts. Irradiance volumes are treated as assets, and [`bincode`] is used to deserialize them from disk. You can generate the `bincode` files by passing the `--upstream` flag to the `export-blender-gi` tool in [`bevy-baked-gi`]. **Important**: This commit doesn't provide the generic infrastructure used to look up the nearest light probe. That's part of the reflection probes pull request, bevyengine#10057. Therefore, this pull request requires hardwires the shader to support only one irradiance volume per scene. **This is a temporary limitation** and will be lifted once bevyengine#10057 lands. [Valve SIGGRAPH 2006]: https://advances.realtimerendering.com/s2006/Mitchell-ShadingInValvesSourceEngine.pdf [`bevy-baked-gi`]: https://github.com/pcwalton/bevy-baked-gi [`bincode`]: https://github.com/bincode-org/bincode
This needs a bit of cleaning up and documentation, but it works and is pretty close. |
The generated |
The generated |
The generated |
1 similar comment
The generated |
The generated |
The generated |
The generated |
Leaving this in the milestone for now, but we might have to end up cutting it depending on time constraints. @pcwalton if this is ready to go, make sure to take it out of draft. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
var<uniform> irradiance_volume_info: VoxelVisualizationIrradianceVolumeInfo; | ||
|
||
@fragment | ||
fn fragment(mesh: VertexOutput) -> @location(0) vec4<f32> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do the cubes appear to be gray+shaded? I was thinking we would want to show essentially an "unlit" solid-color cube the color of the probe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are doing what you suggested, I'm fairly sure. I don't see any shading.
I believe the gray comes from the grayish background color, which is roughly the same in Blender so the indirect light from it is captured in the probe. The sides of the cubes will be a mix of the gray from the background with the colors of the floor visible from that cube side. The relative mix of gray vs. floor color is based on how much that side of the cube can "see".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the irradiance volumes are sampling the clear color/background? Seems strange. ClearColor shouldn't be used for lighting. Only a Skybox if specified, but not the clear color itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In reality it's sampling the "world" in Blender which happens to be dark gray by default. Would you prefer that I change it to black? Or would you rather the world reflect the colors of the floor? (It can't be transparent because we don't have alpha here.)
From a physical perspective it's kind of arbitrary what we pick since the concept of "empty space with no color" doesn't exist in the real world.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think that would be better. Or drop in a skybox and use that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I added a skybox and re-baked the irradiance volume. It looks right to my eyes.
Don't merge yet. The CI failure is legit. |
in the scene. Fixes the crash.
This should be ready for review again. |
Merging tomorrow morning unless I hear otherwise from the rendering crew. |
Objective
Bevy could benefit from irradiance volumes, also known as voxel global illumination or simply as light probes (though this term is not preferred, as multiple techniques can be called light probes).
Irradiance volumes are a form of baked global illumination; they work by sampling the light at the centers of each voxel within a cuboid. At runtime, the voxels surrounding the fragment center are sampled and interpolated to produce indirect diffuse illumination.
Solution
This is divided into two sections. The first is copied and pasted from the irradiance volume module documentation and describes the technique. The second part consists of notes on the implementation.
Overview
An irradiance volume is a cuboid voxel region consisting of
regularly-spaced precomputed samples of diffuse indirect light. They're
ideal if you have a dynamic object such as a character that can move about
static non-moving geometry such as a level in a game, and you want that
dynamic object to be affected by the light bouncing off that static
geometry.
To use irradiance volumes, you need to precompute, or bake, the indirect
light in your scene. Bevy doesn't currently come with a way to do this.
Fortunately, Blender provides a baking tool as part of the Eevee
renderer, and its irradiance volumes are compatible with those used by Bevy.
The
bevy-baked-gi
project provides a tool,export-blender-gi
, that canextract the baked irradiance volumes from the Blender
.blend
file andpackage them up into a
.ktx2
texture for use by the engine. See thedocumentation in the
bevy-baked-gi
project for more details as to thisworkflow.
Like all light probes in Bevy, irradiance volumes are 1×1×1 cubes that can
be arbitrarily scaled, rotated, and positioned in a scene with the
[
bevy_transform::components::Transform
] component. The 3D voxel grid willbe stretched to fill the interior of the cube, and the illumination from the
irradiance volume will apply to all fragments within that bounding region.
Bevy's irradiance volumes are based on Valve's ambient cubes as used in
Half-Life 2 (Mitchell 2006, slide 27). These encode a single color of
light from the six 3D cardinal directions and blend the sides together
according to the surface normal.
The primary reason for choosing ambient cubes is to match Blender, so that
its Eevee renderer can be used for baking. However, they also have some
advantages over the common second-order spherical harmonics approach:
ambient cubes don't suffer from ringing artifacts, they are smaller (6
colors for ambient cubes as opposed to 9 for spherical harmonics), and
evaluation is faster. A smaller basis allows for a denser grid of voxels
with the same storage requirements.
If you wish to use a tool other than
export-blender-gi
to produce theirradiance volumes, you'll need to pack the irradiance volumes in the
following format. The irradiance volume of resolution (Rx, Ry, Rz) is
expected to be a 3D texture of dimensions (Rx, 2Ry, 3Rz). The unnormalized
texture coordinate (s, t, p) of the voxel at coordinate (x, y, z) with
side S ∈ {-X, +X, -Y, +Y, -Z, +Z} is as follows:
Visually, in a left-handed coordinate system with Y up, viewed from the
right, the 3D texture looks like a stacked series of voxel grids, one for
each cube side, in this order:
A terminology note: Other engines may refer to irradiance volumes as voxel
global illumination, VXGI, or simply as light probes. Sometimes light
probe refers to what Bevy calls a reflection probe. In Bevy, light probe
is a generic term that encompasses all cuboid bounding regions that capture
indirect illumination, whether based on voxels or not.
Note that, if binding arrays aren't supported (e.g. on WebGPU or WebGL 2),
then only the closest irradiance volume to the view will be taken into
account during rendering.
Implementation notes
This patch generalizes light probes so as to reuse as much code as possible between irradiance volumes and the existing reflection probes. This approach was chosen because both techniques share numerous similarities:
To do this, we generalize most of the methods in the reflection probes patch #11366 to be generic over a trait,
LightProbeComponent
. This trait is implemented by bothEnvironmentMapLight
(for reflection probes) andIrradianceVolume
(for irradiance volumes). Using a trait will allow us to add more types of light probes in the future. In particular, I highly suspect we will want real-time reflection planes for mirrors in the future, which can be easily slotted into this framework.Changelog
Added
IrradianceVolume
asset type is available for baked voxelized light probes. You can bake the global illumination using Blender or another tool of your choice and use it in Bevy to apply indirect illumination to dynamic objects.