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

Honor mesh-light interaction mediation for “low” render layers #15042

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,17 @@ description = "Demonstrates use of a normal map and depth map for parallax mappi
category = "3D Rendering"
wasm = true

[[example]]
name = "render_layers"
path = "examples/3d/render_layers.rs"
doc-scrape-examples = true

[package.metadata.example.render_layers]
name = "Render Layers"
description = "A 3D scene showcasing the use of render layers"
category = "3D Rendering"
wasm = true

[[example]]
name = "render_to_texture"
path = "examples/3d/render_to_texture.rs"
Expand Down
41 changes: 35 additions & 6 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct ExtractedPointLight {
pub shadow_depth_bias: f32,
pub shadow_normal_bias: f32,
pub spot_light_angles: Option<(f32, f32)>,
pub render_layers: RenderLayers,
}

#[derive(Component, Debug)]
Expand Down Expand Up @@ -66,6 +67,10 @@ bitflags::bitflags! {
}
}

impl PointLightFlags {
pub const RENDER_LAYERS_SHIFT_BITS: usize = 16;
}

#[derive(Copy, Clone, ShaderType, Default, Debug)]
pub struct GpuDirectionalCascade {
clip_from_world: Mat4,
Expand Down Expand Up @@ -98,6 +103,10 @@ bitflags::bitflags! {
}
}

impl DirectionalLightFlags {
pub const RENDER_LAYERS_SHIFT_BITS: usize = 16;
}

#[derive(Copy, Clone, Debug, ShaderType)]
pub struct GpuLights {
directional_lights: [GpuDirectionalLight; MAX_DIRECTIONAL_LIGHTS],
Expand Down Expand Up @@ -181,6 +190,7 @@ pub fn extract_lights(
&GlobalTransform,
&ViewVisibility,
&CubemapFrusta,
Option<&RenderLayers>,
)>,
>,
spot_lights: Extract<
Expand All @@ -190,6 +200,7 @@ pub fn extract_lights(
&GlobalTransform,
&ViewVisibility,
&Frustum,
Option<&RenderLayers>,
)>,
>,
directional_lights: Extract<
Expand Down Expand Up @@ -231,8 +242,14 @@ pub fn extract_lights(

let mut point_lights_values = Vec::with_capacity(*previous_point_lights_len);
for entity in global_point_lights.iter().copied() {
let Ok((point_light, cubemap_visible_entities, transform, view_visibility, frusta)) =
point_lights.get(entity)
let Ok((
point_light,
cubemap_visible_entities,
transform,
view_visibility,
frusta,
render_layers,
)) = point_lights.get(entity)
else {
continue;
};
Expand All @@ -258,6 +275,7 @@ pub fn extract_lights(
* point_light_texel_size
* std::f32::consts::SQRT_2,
spot_light_angles: None,
render_layers: render_layers.unwrap_or_default().clone(),
};
point_lights_values.push((
entity,
Expand All @@ -273,8 +291,14 @@ pub fn extract_lights(

let mut spot_lights_values = Vec::with_capacity(*previous_spot_lights_len);
for entity in global_point_lights.iter().copied() {
if let Ok((spot_light, visible_entities, transform, view_visibility, frustum)) =
spot_lights.get(entity)
if let Ok((
spot_light,
visible_entities,
transform,
view_visibility,
frustum,
render_layers,
)) = spot_lights.get(entity)
{
if !view_visibility.get() {
continue;
Expand Down Expand Up @@ -307,6 +331,7 @@ pub fn extract_lights(
* texel_size
* std::f32::consts::SQRT_2,
spot_light_angles: Some((spot_light.inner_angle, spot_light.outer_angle)),
render_layers: render_layers.unwrap_or_default().clone(),
},
render_visible_entities,
*frustum,
Expand Down Expand Up @@ -726,7 +751,9 @@ pub fn prepare_lights(
.xyz()
.extend(1.0 / (light.range * light.range)),
position_radius: light.transform.translation().extend(light.radius),
flags: flags.bits(),
flags: flags.bits()
| (light.render_layers.bits_compact_lossy() as u32)
<< PointLightFlags::RENDER_LAYERS_SHIFT_BITS,
shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias,
spot_light_tan_angle,
Expand Down Expand Up @@ -770,7 +797,9 @@ pub fn prepare_lights(
color: Vec4::from_slice(&light.color.to_f32_array()) * light.illuminance,
// direction is negated to be ready for N.L
dir_to_light: light.transform.back().into(),
flags: flags.bits(),
flags: flags.bits()
| (light.render_layers.bits_compact_lossy() as u32)
Copy link
Contributor

Choose a reason for hiding this comment

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

I know the code is pretty self evidence but it might be nice to leave a breadcrumb here for future readers (e.g., something like "these aren't flags, but are using the extra space").

<< DirectionalLightFlags::RENDER_LAYERS_SHIFT_BITS,
shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias,
num_cascades: num_cascades as u32,
Expand Down
37 changes: 28 additions & 9 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ use bevy_render::{
renderer::{RenderDevice, RenderQueue},
texture::{BevyDefault, DefaultImageSampler, ImageSampler, TextureFormatPixelInfo},
view::{
prepare_view_targets, GpuCulling, RenderVisibilityRanges, ViewTarget, ViewUniformOffset,
ViewVisibility, VisibilityRange,
prepare_view_targets, GpuCulling, RenderLayers, RenderVisibilityRanges, ViewTarget,
ViewUniformOffset, ViewVisibility, VisibilityRange,
},
Extract,
};
Expand Down Expand Up @@ -284,12 +284,13 @@ pub struct MeshUniform {
/// [`MeshAllocator`]). This value stores the offset of the first vertex in
/// this mesh in that buffer.
pub first_vertex_index: u32,
// Only the first 16 bits are actually used, since we want to match
// the light render layers bits, and those are packed along with flags
pub render_layers_bits: u32,
/// Padding.
pub pad_a: u32,
/// Padding.
pub pad_b: u32,
/// Padding.
pub pad_c: u32,
}

/// Information that has to be transferred from CPU to GPU in order to produce
Expand Down Expand Up @@ -326,12 +327,13 @@ pub struct MeshInputUniform {
/// [`MeshAllocator`]). This value stores the offset of the first vertex in
/// this mesh in that buffer.
pub first_vertex_index: u32,
// Only the first 16 bits are actually used, since we want to match
// the light render layers bits, and those are packed along with flags
pub render_layers_bits: u32,
/// Padding.
pub pad_a: u32,
/// Padding.
pub pad_b: u32,
/// Padding.
pub pad_c: u32,
}

/// Information about each mesh instance needed to cull it on GPU.
Expand Down Expand Up @@ -362,6 +364,7 @@ impl MeshUniform {
mesh_transforms: &MeshTransforms,
first_vertex_index: u32,
maybe_lightmap_uv_rect: Option<Rect>,
render_layers: Option<RenderLayers>,
) -> Self {
let (local_from_world_transpose_a, local_from_world_transpose_b) =
mesh_transforms.world_from_local.inverse_transpose_3x3();
Expand All @@ -373,9 +376,9 @@ impl MeshUniform {
local_from_world_transpose_b,
flags: mesh_transforms.flags,
first_vertex_index,
render_layers_bits: render_layers.unwrap_or_default().bits_compact_lossy() as u32,
pad_a: 0,
pad_b: 0,
pad_c: 0,
}
}
}
Expand Down Expand Up @@ -497,6 +500,8 @@ pub struct RenderMeshInstanceShared {
pub material_bind_group_id: AtomicMaterialBindGroupId,
/// Various flags.
pub flags: RenderMeshInstanceFlags,
/// Render layers
pub render_layers: Option<RenderLayers>,
}

/// Information that is gathered during the parallel portion of mesh extraction
Expand Down Expand Up @@ -564,6 +569,7 @@ impl RenderMeshInstanceShared {
handle: &Handle<Mesh>,
not_shadow_caster: bool,
no_automatic_batching: bool,
render_layers: Option<RenderLayers>,
) -> Self {
let mut mesh_instance_flags = RenderMeshInstanceFlags::empty();
mesh_instance_flags.set(RenderMeshInstanceFlags::SHADOW_CASTER, !not_shadow_caster);
Expand All @@ -578,7 +584,7 @@ impl RenderMeshInstanceShared {

RenderMeshInstanceShared {
mesh_asset_id: handle.id(),

render_layers,
flags: mesh_instance_flags,
material_bind_group_id: AtomicMaterialBindGroupId::default(),
}
Expand Down Expand Up @@ -780,9 +786,14 @@ impl RenderMeshInstanceGpuBuilder {
None => u32::MAX,
},
first_vertex_index,
render_layers_bits: self
.shared
.render_layers
.as_ref()
.unwrap_or_default()
.bits_compact_lossy() as u32,
pad_a: 0,
pad_b: 0,
pad_c: 0,
});

// Record the [`RenderMeshInstance`].
Expand Down Expand Up @@ -864,6 +875,7 @@ pub fn extract_meshes_for_cpu_building(
&ViewVisibility,
&GlobalTransform,
Option<&PreviousGlobalTransform>,
Option<&RenderLayers>,
&Handle<Mesh>,
Has<NotShadowReceiver>,
Has<TransmittedShadowReceiver>,
Expand All @@ -881,6 +893,7 @@ pub fn extract_meshes_for_cpu_building(
view_visibility,
transform,
previous_transform,
render_layers,
handle,
not_shadow_receiver,
transmitted_receiver,
Expand Down Expand Up @@ -909,6 +922,7 @@ pub fn extract_meshes_for_cpu_building(
handle,
not_shadow_caster,
no_automatic_batching,
render_layers.map(|render_layers| render_layers.clone()),
);

let world_from_local = transform.affine();
Expand Down Expand Up @@ -963,6 +977,7 @@ pub fn extract_meshes_for_gpu_building(
Option<&PreviousGlobalTransform>,
Option<&Lightmap>,
Option<&Aabb>,
Option<&RenderLayers>,
&Handle<Mesh>,
Has<NotShadowReceiver>,
Has<TransmittedShadowReceiver>,
Expand Down Expand Up @@ -997,6 +1012,7 @@ pub fn extract_meshes_for_gpu_building(
previous_transform,
lightmap,
aabb,
render_layers,
handle,
not_shadow_receiver,
transmitted_receiver,
Expand Down Expand Up @@ -1025,6 +1041,7 @@ pub fn extract_meshes_for_gpu_building(
handle,
not_shadow_caster,
no_automatic_batching,
render_layers.map(|render_layers| render_layers.clone()),
);

let lightmap_uv_rect = pack_lightmap_uv_rect(lightmap.map(|lightmap| lightmap.uv_rect));
Expand Down Expand Up @@ -1294,6 +1311,7 @@ impl GetBatchData for MeshPipeline {
&mesh_instance.transforms,
first_vertex_index,
maybe_lightmap.map(|lightmap| lightmap.uv_rect),
mesh_instance.shared.render_layers.clone(),
),
mesh_instance.should_batch().then_some((
mesh_instance.material_bind_group_id.get(),
Expand Down Expand Up @@ -1355,6 +1373,7 @@ impl GetFullBatchData for MeshPipeline {
&mesh_instance.transforms,
first_vertex_index,
maybe_lightmap.map(|lightmap| lightmap.uv_rect),
mesh_instance.shared.render_layers.clone(),
))
}

Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_pbr/src/render/mesh_preprocess.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ struct MeshInput {
// applicable. If not present, this is `u32::MAX`.
previous_input_index: u32,
first_vertex_index: u32,
// Only the first 16 bits are actually used, since we want to match
// the light render layers bits, and those are packed along with flags
render_layers_bits: u32,
pad_a: u32,
pad_b: u32,
pad_c: u32,
}

// Information about each mesh instance needed to cull it on GPU.
Expand Down Expand Up @@ -191,4 +193,5 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
output[mesh_output_index].flags = current_input[input_index].flags;
output[mesh_output_index].lightmap_uv_rect = current_input[input_index].lightmap_uv_rect;
output[mesh_output_index].first_vertex_index = current_input[input_index].first_vertex_index;
output[mesh_output_index].render_layers_bits = current_input[input_index].render_layers_bits;
}
4 changes: 3 additions & 1 deletion crates/bevy_pbr/src/render/mesh_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ struct Mesh {
lightmap_uv_rect: vec2<u32>,
// The index of the mesh's first vertex in the vertex buffer.
first_vertex_index: u32,
// Only the first 16 bits are actually used, since we want to match
// the light render layers bits, and those are packed along with flags
render_layers_bits: u32,
pad_a: u32,
pad_b: u32,
pad_c: u32,
};

#ifdef SKINNED
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_pbr/src/render/mesh_view_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct ClusterableObject {

const POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u;
const POINT_LIGHT_FLAGS_SPOT_LIGHT_Y_NEGATIVE: u32 = 2u;
const POINT_LIGHT_FLAGS_RENDER_LAYERS_SHIFT_BITS: u32 = 16u;

struct DirectionalCascade {
clip_from_world: mat4x4<f32>,
Expand All @@ -38,6 +39,7 @@ struct DirectionalLight {

const DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u;
const DIRECTIONAL_LIGHT_FLAGS_VOLUMETRIC_BIT: u32 = 2u;
const DIRECTIONAL_LIGHT_FLAGS_RENDER_LAYERS_SHIFT_BITS: u32 = 16u;

struct Lights {
// NOTE: this array size must be kept in sync with the constants defined in bevy_pbr/src/render/light.rs
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/render/pbr_fragment.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ fn pbr_input_from_vertex_output(
pbr_input.flags = in.mesh_flags;
#else
pbr_input.flags = mesh[in.instance_index].flags;
pbr_input.render_layers_bits = mesh[in.instance_index].render_layers_bits;
#endif

pbr_input.is_orthographic = view.clip_from_view[3].w == 1.0;
Expand Down
13 changes: 13 additions & 0 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,11 @@ fn apply_pbr_lighting(
// Point lights (direct)
for (var i: u32 = offset_and_counts[0]; i < offset_and_counts[0] + offset_and_counts[1]; i = i + 1u) {
let light_id = clustering::get_clusterable_object_id(i);

if (view_bindings::clusterable_objects.data[light_id].flags >> mesh_view_types::POINT_LIGHT_FLAGS_RENDER_LAYERS_SHIFT_BITS & in.render_layers_bits) == 0u {
continue;
}

var shadow: f32 = 1.0;
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
&& (view_bindings::clusterable_objects.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
Expand Down Expand Up @@ -445,6 +450,10 @@ fn apply_pbr_lighting(
for (var i: u32 = offset_and_counts[0] + offset_and_counts[1]; i < offset_and_counts[0] + offset_and_counts[1] + offset_and_counts[2]; i = i + 1u) {
let light_id = clustering::get_clusterable_object_id(i);

if (view_bindings::clusterable_objects.data[light_id].flags >> mesh_view_types::POINT_LIGHT_FLAGS_RENDER_LAYERS_SHIFT_BITS & in.render_layers_bits) == 0u {
continue;
}

var shadow: f32 = 1.0;
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
&& (view_bindings::clusterable_objects.data[light_id].flags & mesh_view_types::POINT_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
Expand Down Expand Up @@ -486,6 +495,10 @@ fn apply_pbr_lighting(
continue;
}

if ((*light).flags >> mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_RENDER_LAYERS_SHIFT_BITS & in.render_layers_bits) == 0u {
continue;
}

var shadow: f32 = 1.0;
if ((in.flags & MESH_FLAGS_SHADOW_RECEIVER_BIT) != 0u
&& (view_bindings::lights.directional_lights[i].flags & mesh_view_types::DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT) != 0u) {
Expand Down
Loading
Loading