mirror of
https://github.com/bevyengine/bevy
synced 2024-12-29 22:43:14 +00:00
b7bcd313ca
This commit allows the Bevy renderer to use the clustering infrastructure for light probes (reflection probes and irradiance volumes) on platforms where at least 3 storage buffers are available. On such platforms (the vast majority), we stop performing brute-force searches of light probes for each fragment and instead only search the light probes with bounding spheres that intersect the current cluster. This should dramatically improve scalability of irradiance volumes and reflection probes. The primary platform that doesn't support 3 storage buffers is WebGL 2, and we continue using a brute-force search of light probes on that platform, as the UBO that stores per-cluster indices is too small to fit the light probe counts. Note, however, that that platform also doesn't support bindless textures (indeed, it would be very odd for a platform to support bindless textures but not SSBOs), so we only support one of each type of light probe per drawcall there in the first place. Consequently, this isn't a performance problem, as the search will only have one light probe to consider. (In fact, clustering would probably end up being a performance loss.) Known potential improvements include: 1. We currently cull based on a conservative bounding sphere test and not based on the oriented bounding box (OBB) of the light probe. This is improvable, but in the interests of simplicity, I opted to keep the bounding sphere test for now. The OBB improvement can be a follow-up. 2. This patch doesn't change the fact that each fragment only takes a single light probe into account. Typical light probe implementations detect the case in which multiple light probes cover the current fragment and perform some sort of weighted blend between them. As the light probe fetch function presently returns only a single light probe, implementing that feature would require more code restructuring, so I left it out for now. It can be added as a follow-up. 3. Light probe implementations typically have a falloff range. Although this is a wanted feature in Bevy, this particular commit also doesn't implement that feature, as it's out of scope. 4. This commit doesn't raise the maximum number of light probes past its current value of 8 for each type. This should be addressed later, but would possibly require more bindings on platforms with storage buffers, which would increase this patch's complexity. Even without raising the limit, this patch should constitute a significant performance improvement for scenes that get anywhere close to this limit. In the interest of keeping this patch small, I opted to leave raising the limit to a follow-up. ## Changelog ### Changed * Light probes (reflection probes and irradiance volumes) are now clustered on most platforms, improving performance when many light probes are present. --------- Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
49 lines
2.1 KiB
WebGPU Shading Language
49 lines
2.1 KiB
WebGPU Shading Language
#import bevy_pbr::forward_io::VertexOutput
|
|
#import bevy_pbr::irradiance_volume
|
|
#import bevy_pbr::mesh_view_bindings
|
|
#import bevy_pbr::clustered_forward
|
|
|
|
struct VoxelVisualizationIrradianceVolumeInfo {
|
|
world_from_voxel: mat4x4<f32>,
|
|
voxel_from_world: mat4x4<f32>,
|
|
resolution: vec3<u32>,
|
|
// A scale factor that's applied to the diffuse and specular light from the
|
|
// light probe. This is in units of cd/m² (candela per square meter).
|
|
intensity: f32,
|
|
}
|
|
|
|
@group(2) @binding(100)
|
|
var<uniform> irradiance_volume_info: VoxelVisualizationIrradianceVolumeInfo;
|
|
|
|
@fragment
|
|
fn fragment(mesh: VertexOutput) -> @location(0) vec4<f32> {
|
|
// Snap the world position we provide to `irradiance_volume_light()` to the
|
|
// middle of the nearest texel.
|
|
var unit_pos = (irradiance_volume_info.voxel_from_world *
|
|
vec4(mesh.world_position.xyz, 1.0f)).xyz;
|
|
let resolution = vec3<f32>(irradiance_volume_info.resolution);
|
|
let stp = clamp((unit_pos + 0.5) * resolution, vec3(0.5f), resolution - vec3(0.5f));
|
|
let stp_rounded = round(stp - 0.5f) + 0.5f;
|
|
let rounded_world_pos = (irradiance_volume_info.world_from_voxel * vec4(stp_rounded, 1.0f)).xyz;
|
|
|
|
// Look up the irradiance volume range in the cluster list.
|
|
let view_z = dot(vec4<f32>(
|
|
mesh_view_bindings::view.view_from_world[0].z,
|
|
mesh_view_bindings::view.view_from_world[1].z,
|
|
mesh_view_bindings::view.view_from_world[2].z,
|
|
mesh_view_bindings::view.view_from_world[3].z
|
|
), mesh.world_position);
|
|
let cluster_index = clustered_forward::fragment_cluster_index(mesh.position.xy, view_z, false);
|
|
var clusterable_object_index_ranges =
|
|
clustered_forward::unpack_clusterable_object_index_ranges(cluster_index);
|
|
|
|
// `irradiance_volume_light()` multiplies by intensity, so cancel it out.
|
|
// If we take intensity into account, the cubes will be way too bright.
|
|
let rgb = irradiance_volume::irradiance_volume_light(
|
|
mesh.world_position.xyz,
|
|
mesh.world_normal,
|
|
&clusterable_object_index_ranges,
|
|
) / irradiance_volume_info.intensity;
|
|
|
|
return vec4<f32>(rgb, 1.0f);
|
|
}
|