Skip to content

Commit

Permalink
bevy_pbr: Document how the iterative sphere refinement algorithm works
Browse files Browse the repository at this point in the history
In case the PDF is taken down.
  • Loading branch information
superdump committed Mar 27, 2022
1 parent 35bda54 commit 97ea755
Showing 1 changed file with 15 additions and 0 deletions.
15 changes: 15 additions & 0 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,10 @@ pub(crate) fn assign_lights_to_clusters(
// What follows is the Iterative Sphere Refinement algorithm from Just Cause 3
// Persson et al, Practical Clustered Shading
// http://newq.net/dl/pub/s2015_practical.pdf
// NOTE: A sphere under perspective projection is no longer a sphere. It gets
// stretched and warped, which prevents simpler algorithms from being correct
// as they often assume that the widest part of the sphere under projection is the
// center point on the axis of interest plus the radius, and that is not true!
let view_light_sphere = Sphere {
center: Vec3A::from(inverse_view_transform * light_sphere.center.extend(1.0)),
radius: light_sphere.radius,
Expand Down Expand Up @@ -995,11 +999,15 @@ pub(crate) fn assign_lights_to_clusters(
for z in min_cluster.z..=max_cluster.z {
let mut z_light = view_light_sphere.clone();
if z_center.is_none() || z != z_center.unwrap() {
// The z plane closer to the light has the larger radius circle where the
// light sphere intersects the z plane.
let z_plane = if z_center.is_some() && z < z_center.unwrap() {
z_planes[(z + 1) as usize]
} else {
z_planes[z as usize]
};
// Project the sphere to this z plane and use its radius as the radius of a
// new, refined sphere.
if let Some(projected) = project_to_plane_z(z_light, z_plane) {
z_light = projected;
} else {
Expand All @@ -1009,11 +1017,15 @@ pub(crate) fn assign_lights_to_clusters(
for y in min_cluster.y..=max_cluster.y {
let mut y_light = z_light.clone();
if y_center.is_none() || y != y_center.unwrap() {
// The y plane closer to the light has the larger radius circle where the
// light sphere intersects the y plane.
let y_plane = if y_center.is_some() && y < y_center.unwrap() {
y_planes[(y + 1) as usize]
} else {
y_planes[y as usize]
};
// Project the refined sphere to this y plane and use its radius as the
// radius of a new, even more refined sphere.
if let Some(projected) =
project_to_plane_y(y_light, y_plane, is_orthographic)
{
Expand All @@ -1022,6 +1034,7 @@ pub(crate) fn assign_lights_to_clusters(
continue;
}
}
// Loop from the left to find the first affected cluster
let mut min_x = min_cluster.x;
loop {
if min_x >= max_cluster.x
Expand All @@ -1036,6 +1049,7 @@ pub(crate) fn assign_lights_to_clusters(
}
min_x += 1;
}
// Loop from the right to find the last affected cluster
let mut max_x = max_cluster.x;
loop {
if max_x <= min_x
Expand All @@ -1053,6 +1067,7 @@ pub(crate) fn assign_lights_to_clusters(
let mut cluster_index = ((y * clusters.dimensions.x + min_x)
* clusters.dimensions.z
+ z) as usize;
// Mark the clusters in the range as affected
for _ in min_x..=max_x {
clusters.lights[cluster_index].entities.push(light.entity);
cluster_index += clusters.dimensions.z as usize;
Expand Down

0 comments on commit 97ea755

Please sign in to comment.