bevy/pipelined/bevy_pbr2/src/render/light.rs

731 lines
28 KiB
Rust
Raw Normal View History

use crate::{AmbientLight, DirectionalLight, ExtractedMeshes, MeshMeta, PbrShaders, PointLight};
2021-06-02 02:59:17 +00:00
use bevy_ecs::{prelude::*, system::SystemState};
2021-07-01 23:48:55 +00:00
use bevy_math::{const_vec3, Mat4, Vec3, Vec4};
2021-06-02 02:59:17 +00:00
use bevy_render2::{
camera::CameraProjection,
2021-06-02 02:59:17 +00:00
color::Color,
core_pipeline::Transparent3dPhase,
mesh::Mesh,
render_asset::RenderAssets,
2021-06-02 02:59:17 +00:00
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{Draw, DrawFunctions, RenderPhase, TrackedRenderPass},
2021-06-21 23:28:52 +00:00
render_resource::*,
renderer::{RenderContext, RenderDevice},
2021-06-02 02:59:17 +00:00
texture::*,
2021-07-01 23:54:58 +00:00
view::{ExtractedView, ViewUniformOffset},
2021-06-02 02:59:17 +00:00
};
use bevy_transform::components::GlobalTransform;
use crevice::std140::AsStd140;
use std::num::NonZeroU32;
bevy_pbr2: Add support for most of the StandardMaterial textures (#4) * bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-06-27 23:10:23 +00:00
pub struct ExtractedAmbientLight {
color: Color,
brightness: f32,
}
2021-06-02 02:59:17 +00:00
pub struct ExtractedPointLight {
color: Color,
intensity: f32,
range: f32,
radius: f32,
transform: GlobalTransform,
shadow_depth_bias: f32,
shadow_normal_bias: f32,
}
pub struct ExtractedDirectionalLight {
color: Color,
illuminance: f32,
direction: Vec3,
projection: Mat4,
shadow_depth_bias: f32,
shadow_normal_bias: f32,
2021-06-02 02:59:17 +00:00
}
#[repr(C)]
#[derive(Copy, Clone, AsStd140, Default, Debug)]
pub struct GpuPointLight {
2021-06-02 02:59:17 +00:00
color: Vec4,
2021-07-01 23:48:55 +00:00
// proj: Mat4,
2021-06-02 02:59:17 +00:00
position: Vec3,
2021-07-01 23:48:55 +00:00
inverse_square_range: f32,
radius: f32,
near: f32,
far: f32,
shadow_depth_bias: f32,
shadow_normal_bias: f32,
}
#[repr(C)]
#[derive(Copy, Clone, AsStd140, Default, Debug)]
pub struct GpuDirectionalLight {
view_projection: Mat4,
color: Vec4,
dir_to_light: Vec3,
shadow_depth_bias: f32,
shadow_normal_bias: f32,
2021-06-02 02:59:17 +00:00
}
#[repr(C)]
#[derive(Copy, Clone, Debug, AsStd140)]
pub struct GpuLights {
// TODO: this comes first to work around a WGSL alignment issue. We need to solve this issue before releasing the renderer rework
point_lights: [GpuPointLight; MAX_POINT_LIGHTS],
directional_lights: [GpuDirectionalLight; MAX_DIRECTIONAL_LIGHTS],
bevy_pbr2: Add support for most of the StandardMaterial textures (#4) * bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-06-27 23:10:23 +00:00
ambient_color: Vec4,
n_point_lights: u32,
n_directional_lights: u32,
2021-06-02 02:59:17 +00:00
}
// NOTE: this must be kept in sync with the same constants in pbr.frag
2021-07-01 23:54:58 +00:00
pub const MAX_POINT_LIGHTS: usize = 10;
pub const MAX_DIRECTIONAL_LIGHTS: usize = 1;
pub const POINT_SHADOW_SIZE: Extent3d = Extent3d {
2021-06-02 02:59:17 +00:00
width: 1024,
height: 1024,
depth_or_array_layers: (6 * MAX_POINT_LIGHTS) as u32,
};
pub const DIRECTIONAL_SHADOW_SIZE: Extent3d = Extent3d {
width: 4096,
height: 4096,
depth_or_array_layers: MAX_DIRECTIONAL_LIGHTS as u32,
2021-06-02 02:59:17 +00:00
};
pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float;
pub struct ShadowShaders {
2021-06-21 23:28:52 +00:00
pub pipeline: RenderPipeline,
pub view_layout: BindGroupLayout,
pub point_light_sampler: Sampler,
pub directional_light_sampler: Sampler,
2021-06-02 02:59:17 +00:00
}
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
impl FromWorld for ShadowShaders {
fn from_world(world: &mut World) -> Self {
2021-06-21 23:28:52 +00:00
let render_device = world.get_resource::<RenderDevice>().unwrap();
let pbr_shaders = world.get_resource::<PbrShaders>().unwrap();
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
entries: &[
// View
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStage::VERTEX | ShaderStage::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: true,
// TODO: change this to ViewUniform::std140_size_static once crevice fixes this!
// Context: https://github.com/LPGhatguy/crevice/issues/29
min_binding_size: BufferSize::new(80),
2021-06-21 23:28:52 +00:00
},
count: None,
2021-06-02 02:59:17 +00:00
},
],
2021-06-21 23:28:52 +00:00
label: None,
});
2021-06-02 02:59:17 +00:00
2021-06-21 23:28:52 +00:00
let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
push_constant_ranges: &[],
bind_group_layouts: &[&view_layout, &pbr_shaders.mesh_layout],
2021-06-21 23:28:52 +00:00
});
2021-06-02 02:59:17 +00:00
2021-06-21 23:28:52 +00:00
let pipeline = render_device.create_render_pipeline(&RenderPipelineDescriptor {
label: None,
vertex: VertexState {
buffers: &[VertexBufferLayout {
array_stride: 32,
step_mode: InputStepMode::Vertex,
attributes: &[
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 12,
shader_location: 0,
},
// Normal
VertexAttribute {
format: VertexFormat::Float32x3,
offset: 0,
shader_location: 1,
},
// Uv
VertexAttribute {
format: VertexFormat::Float32x2,
offset: 24,
shader_location: 2,
},
],
}],
module: &pbr_shaders.shader_module,
entry_point: "vertex",
2021-06-21 23:28:52 +00:00
},
fragment: None,
2021-06-02 02:59:17 +00:00
depth_stencil: Some(DepthStencilState {
format: SHADOW_FORMAT,
depth_write_enabled: true,
depth_compare: CompareFunction::LessEqual,
stencil: StencilState {
front: StencilFaceState::IGNORE,
back: StencilFaceState::IGNORE,
read_mask: 0,
write_mask: 0,
},
bias: DepthBiasState {
Scale normal bias by texel size (#26) * 3d_scene_pipelined: Use a shallower directional light angle to provoke acne * cornell_box_pipelined: Remove bias tweaks * bevy_pbr2: Simplify shadow biases by moving them to linear depth * bevy_pbr2: Do not use DepthBiasState * bevy_pbr2: Do not use bilinear filtering for sampling depth textures * pbr.wgsl: Remove unnecessary comment * bevy_pbr2: Do manual shadow map depth comparisons for more flexibility * examples: Add shadow_biases_pipelined example This is useful for stress testing biases. * bevy_pbr2: Scale the point light normal bias by the shadow map texel size This allows the normal bias to be small close to the light source where the shadow map texel to screen texel ratio is high, but is appropriately large further away from the light source where the shadow map texel can easily cover multiple screen texels. * shadow_biases_pipelined: Add support for toggling directional / point light * shadow_biases_pipelined: Cleanup * bevy_pbr2: Scale the directional light normal bias by the shadow map texel size * shadow_biases_pipelined: Fit the orthographic projection around the scene * bevy_pbr2: Directional lights should have no shadows outside their projection Before this change, sampling a fragment position from outside the ndc volume would result in the return sample being clamped to the edge in x,y or possibly always casting a shadow for fragment positions past the orthographic projection's far plane. * bevy_pbr2: Fix the default directional light normal bias * Revert "bevy_pbr2: Do manual shadow map depth comparisons for more flexibility" This reverts commit 7df1bab38a42d8a33bc50ca583d4be37bd9c9f0d. * shadow_biases_pipelined: Adjust directional light normal bias in 0.1 increments * pbr.wgsl: Add a couple of clarifying comments * Revert "bevy_pbr2: Do not use bilinear filtering for sampling depth textures" This reverts commit f53baab0232ce218866a45cad6902b470f4cf2c4. * shadow_biases_pipelined: Print usage to terminal
2021-07-19 19:20:59 +00:00
constant: 0,
slope_scale: 0.0,
2021-06-02 02:59:17 +00:00
clamp: 0.0,
},
}),
2021-06-21 23:28:52 +00:00
layout: Some(&pipeline_layout),
multisample: MultisampleState::default(),
2021-06-02 02:59:17 +00:00
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
2021-06-21 23:28:52 +00:00
strip_index_format: None,
front_face: FrontFace::Ccw,
2021-07-01 23:48:55 +00:00
cull_mode: None,
2021-06-21 23:28:52 +00:00
polygon_mode: PolygonMode::Fill,
2021-06-02 02:59:17 +00:00
clamp_depth: false,
2021-06-21 23:28:52 +00:00
conservative: false,
2021-06-02 02:59:17 +00:00
},
2021-06-21 23:28:52 +00:00
});
2021-06-02 02:59:17 +00:00
ShadowShaders {
pipeline,
2021-06-21 23:28:52 +00:00
view_layout,
point_light_sampler: render_device.create_sampler(&SamplerDescriptor {
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
address_mode_w: AddressMode::ClampToEdge,
mag_filter: FilterMode::Linear,
min_filter: FilterMode::Linear,
mipmap_filter: FilterMode::Nearest,
compare: Some(CompareFunction::LessEqual),
..Default::default()
}),
directional_light_sampler: render_device.create_sampler(&SamplerDescriptor {
2021-06-02 02:59:17 +00:00
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
address_mode_w: AddressMode::ClampToEdge,
mag_filter: FilterMode::Linear,
min_filter: FilterMode::Linear,
mipmap_filter: FilterMode::Nearest,
2021-06-21 23:28:52 +00:00
compare: Some(CompareFunction::LessEqual),
2021-06-02 02:59:17 +00:00
..Default::default()
}),
}
}
}
// TODO: ultimately these could be filtered down to lights relevant to actual views
pub fn extract_lights(
mut commands: Commands,
bevy_pbr2: Add support for most of the StandardMaterial textures (#4) * bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-06-27 23:10:23 +00:00
ambient_light: Res<AmbientLight>,
point_lights: Query<(Entity, &PointLight, &GlobalTransform)>,
directional_lights: Query<(Entity, &DirectionalLight, &GlobalTransform)>,
2021-06-02 02:59:17 +00:00
) {
bevy_pbr2: Add support for most of the StandardMaterial textures (#4) * bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-06-27 23:10:23 +00:00
commands.insert_resource(ExtractedAmbientLight {
color: ambient_light.color,
brightness: ambient_light.brightness,
});
Scale normal bias by texel size (#26) * 3d_scene_pipelined: Use a shallower directional light angle to provoke acne * cornell_box_pipelined: Remove bias tweaks * bevy_pbr2: Simplify shadow biases by moving them to linear depth * bevy_pbr2: Do not use DepthBiasState * bevy_pbr2: Do not use bilinear filtering for sampling depth textures * pbr.wgsl: Remove unnecessary comment * bevy_pbr2: Do manual shadow map depth comparisons for more flexibility * examples: Add shadow_biases_pipelined example This is useful for stress testing biases. * bevy_pbr2: Scale the point light normal bias by the shadow map texel size This allows the normal bias to be small close to the light source where the shadow map texel to screen texel ratio is high, but is appropriately large further away from the light source where the shadow map texel can easily cover multiple screen texels. * shadow_biases_pipelined: Add support for toggling directional / point light * shadow_biases_pipelined: Cleanup * bevy_pbr2: Scale the directional light normal bias by the shadow map texel size * shadow_biases_pipelined: Fit the orthographic projection around the scene * bevy_pbr2: Directional lights should have no shadows outside their projection Before this change, sampling a fragment position from outside the ndc volume would result in the return sample being clamped to the edge in x,y or possibly always casting a shadow for fragment positions past the orthographic projection's far plane. * bevy_pbr2: Fix the default directional light normal bias * Revert "bevy_pbr2: Do manual shadow map depth comparisons for more flexibility" This reverts commit 7df1bab38a42d8a33bc50ca583d4be37bd9c9f0d. * shadow_biases_pipelined: Adjust directional light normal bias in 0.1 increments * pbr.wgsl: Add a couple of clarifying comments * Revert "bevy_pbr2: Do not use bilinear filtering for sampling depth textures" This reverts commit f53baab0232ce218866a45cad6902b470f4cf2c4. * shadow_biases_pipelined: Print usage to terminal
2021-07-19 19:20:59 +00:00
// This is the point light shadow map texel size for one face of the cube as a distance of 1.0
// world unit from the light.
// point_light_texel_size = 2.0 * 1.0 * tan(PI / 4.0) / cube face width in texels
// PI / 4.0 is half the cube face fov, tan(PI / 4.0) = 1.0, so this simplifies to:
// point_light_texel_size = 2.0 / cube face width in texels
// NOTE: When using various PCF kernel sizes, this will need to be adjusted, according to:
// https://catlikecoding.com/unity/tutorials/custom-srp/point-and-spot-shadows/
let point_light_texel_size = 2.0 / POINT_SHADOW_SIZE.width as f32;
for (entity, point_light, transform) in point_lights.iter() {
2021-06-02 02:59:17 +00:00
commands.get_or_spawn(entity).insert(ExtractedPointLight {
color: point_light.color,
intensity: point_light.intensity,
range: point_light.range,
radius: point_light.radius,
2021-07-02 01:05:20 +00:00
transform: *transform,
shadow_depth_bias: point_light.shadow_depth_bias,
Scale normal bias by texel size (#26) * 3d_scene_pipelined: Use a shallower directional light angle to provoke acne * cornell_box_pipelined: Remove bias tweaks * bevy_pbr2: Simplify shadow biases by moving them to linear depth * bevy_pbr2: Do not use DepthBiasState * bevy_pbr2: Do not use bilinear filtering for sampling depth textures * pbr.wgsl: Remove unnecessary comment * bevy_pbr2: Do manual shadow map depth comparisons for more flexibility * examples: Add shadow_biases_pipelined example This is useful for stress testing biases. * bevy_pbr2: Scale the point light normal bias by the shadow map texel size This allows the normal bias to be small close to the light source where the shadow map texel to screen texel ratio is high, but is appropriately large further away from the light source where the shadow map texel can easily cover multiple screen texels. * shadow_biases_pipelined: Add support for toggling directional / point light * shadow_biases_pipelined: Cleanup * bevy_pbr2: Scale the directional light normal bias by the shadow map texel size * shadow_biases_pipelined: Fit the orthographic projection around the scene * bevy_pbr2: Directional lights should have no shadows outside their projection Before this change, sampling a fragment position from outside the ndc volume would result in the return sample being clamped to the edge in x,y or possibly always casting a shadow for fragment positions past the orthographic projection's far plane. * bevy_pbr2: Fix the default directional light normal bias * Revert "bevy_pbr2: Do manual shadow map depth comparisons for more flexibility" This reverts commit 7df1bab38a42d8a33bc50ca583d4be37bd9c9f0d. * shadow_biases_pipelined: Adjust directional light normal bias in 0.1 increments * pbr.wgsl: Add a couple of clarifying comments * Revert "bevy_pbr2: Do not use bilinear filtering for sampling depth textures" This reverts commit f53baab0232ce218866a45cad6902b470f4cf2c4. * shadow_biases_pipelined: Print usage to terminal
2021-07-19 19:20:59 +00:00
// The factor of SQRT_2 is for the worst-case diagonal offset
shadow_normal_bias: point_light.shadow_normal_bias
* point_light_texel_size
* std::f32::consts::SQRT_2,
2021-06-02 02:59:17 +00:00
});
}
for (entity, directional_light, transform) in directional_lights.iter() {
Scale normal bias by texel size (#26) * 3d_scene_pipelined: Use a shallower directional light angle to provoke acne * cornell_box_pipelined: Remove bias tweaks * bevy_pbr2: Simplify shadow biases by moving them to linear depth * bevy_pbr2: Do not use DepthBiasState * bevy_pbr2: Do not use bilinear filtering for sampling depth textures * pbr.wgsl: Remove unnecessary comment * bevy_pbr2: Do manual shadow map depth comparisons for more flexibility * examples: Add shadow_biases_pipelined example This is useful for stress testing biases. * bevy_pbr2: Scale the point light normal bias by the shadow map texel size This allows the normal bias to be small close to the light source where the shadow map texel to screen texel ratio is high, but is appropriately large further away from the light source where the shadow map texel can easily cover multiple screen texels. * shadow_biases_pipelined: Add support for toggling directional / point light * shadow_biases_pipelined: Cleanup * bevy_pbr2: Scale the directional light normal bias by the shadow map texel size * shadow_biases_pipelined: Fit the orthographic projection around the scene * bevy_pbr2: Directional lights should have no shadows outside their projection Before this change, sampling a fragment position from outside the ndc volume would result in the return sample being clamped to the edge in x,y or possibly always casting a shadow for fragment positions past the orthographic projection's far plane. * bevy_pbr2: Fix the default directional light normal bias * Revert "bevy_pbr2: Do manual shadow map depth comparisons for more flexibility" This reverts commit 7df1bab38a42d8a33bc50ca583d4be37bd9c9f0d. * shadow_biases_pipelined: Adjust directional light normal bias in 0.1 increments * pbr.wgsl: Add a couple of clarifying comments * Revert "bevy_pbr2: Do not use bilinear filtering for sampling depth textures" This reverts commit f53baab0232ce218866a45cad6902b470f4cf2c4. * shadow_biases_pipelined: Print usage to terminal
2021-07-19 19:20:59 +00:00
// Calulate the directional light shadow map texel size using the largest x,y dimension of
// the orthographic projection divided by the shadow map resolution
// NOTE: When using various PCF kernel sizes, this will need to be adjusted, according to:
// https://catlikecoding.com/unity/tutorials/custom-srp/directional-shadows/
let largest_dimension = (directional_light.shadow_projection.right
- directional_light.shadow_projection.left)
.max(
directional_light.shadow_projection.top
- directional_light.shadow_projection.bottom,
);
let directional_light_texel_size = largest_dimension / DIRECTIONAL_SHADOW_SIZE.width as f32;
commands
.get_or_spawn(entity)
.insert(ExtractedDirectionalLight {
color: directional_light.color,
illuminance: directional_light.illuminance,
direction: transform.forward(),
projection: directional_light.shadow_projection.get_projection_matrix(),
shadow_depth_bias: directional_light.shadow_depth_bias,
Scale normal bias by texel size (#26) * 3d_scene_pipelined: Use a shallower directional light angle to provoke acne * cornell_box_pipelined: Remove bias tweaks * bevy_pbr2: Simplify shadow biases by moving them to linear depth * bevy_pbr2: Do not use DepthBiasState * bevy_pbr2: Do not use bilinear filtering for sampling depth textures * pbr.wgsl: Remove unnecessary comment * bevy_pbr2: Do manual shadow map depth comparisons for more flexibility * examples: Add shadow_biases_pipelined example This is useful for stress testing biases. * bevy_pbr2: Scale the point light normal bias by the shadow map texel size This allows the normal bias to be small close to the light source where the shadow map texel to screen texel ratio is high, but is appropriately large further away from the light source where the shadow map texel can easily cover multiple screen texels. * shadow_biases_pipelined: Add support for toggling directional / point light * shadow_biases_pipelined: Cleanup * bevy_pbr2: Scale the directional light normal bias by the shadow map texel size * shadow_biases_pipelined: Fit the orthographic projection around the scene * bevy_pbr2: Directional lights should have no shadows outside their projection Before this change, sampling a fragment position from outside the ndc volume would result in the return sample being clamped to the edge in x,y or possibly always casting a shadow for fragment positions past the orthographic projection's far plane. * bevy_pbr2: Fix the default directional light normal bias * Revert "bevy_pbr2: Do manual shadow map depth comparisons for more flexibility" This reverts commit 7df1bab38a42d8a33bc50ca583d4be37bd9c9f0d. * shadow_biases_pipelined: Adjust directional light normal bias in 0.1 increments * pbr.wgsl: Add a couple of clarifying comments * Revert "bevy_pbr2: Do not use bilinear filtering for sampling depth textures" This reverts commit f53baab0232ce218866a45cad6902b470f4cf2c4. * shadow_biases_pipelined: Print usage to terminal
2021-07-19 19:20:59 +00:00
// The factor of SQRT_2 is for the worst-case diagonal offset
shadow_normal_bias: directional_light.shadow_normal_bias
* directional_light_texel_size
* std::f32::consts::SQRT_2,
});
}
2021-06-02 02:59:17 +00:00
}
2021-07-01 23:48:55 +00:00
// Can't do `Vec3::Y * -1.0` because mul isn't const
const NEGATIVE_X: Vec3 = const_vec3!([-1.0, 0.0, 0.0]);
const NEGATIVE_Y: Vec3 = const_vec3!([0.0, -1.0, 0.0]);
const NEGATIVE_Z: Vec3 = const_vec3!([0.0, 0.0, -1.0]);
struct CubeMapFace {
target: Vec3,
up: Vec3,
}
// see https://www.khronos.org/opengl/wiki/Cubemap_Texture
const CUBE_MAP_FACES: [CubeMapFace; 6] = [
// 0 GL_TEXTURE_CUBE_MAP_POSITIVE_X
CubeMapFace {
target: NEGATIVE_X,
up: NEGATIVE_Y,
},
// 1 GL_TEXTURE_CUBE_MAP_NEGATIVE_X
CubeMapFace {
target: Vec3::X,
up: NEGATIVE_Y,
},
// 2 GL_TEXTURE_CUBE_MAP_POSITIVE_Y
CubeMapFace {
target: NEGATIVE_Y,
up: Vec3::Z,
},
// 3 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
CubeMapFace {
target: Vec3::Y,
up: NEGATIVE_Z,
},
// 4 GL_TEXTURE_CUBE_MAP_POSITIVE_Z
CubeMapFace {
target: NEGATIVE_Z,
up: NEGATIVE_Y,
},
// 5 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
CubeMapFace {
target: Vec3::Z,
up: NEGATIVE_Y,
},
];
fn face_index_to_name(face_index: usize) -> &'static str {
match face_index {
0 => "+x",
1 => "-x",
2 => "+y",
3 => "-y",
4 => "+z",
5 => "-z",
_ => "invalid",
}
}
2021-06-02 02:59:17 +00:00
pub struct ViewLight {
2021-07-01 23:48:55 +00:00
pub depth_texture_view: TextureView,
pub pass_name: String,
2021-06-02 02:59:17 +00:00
}
pub struct ViewLights {
pub point_light_depth_texture: Texture,
pub point_light_depth_texture_view: TextureView,
pub directional_light_depth_texture: Texture,
pub directional_light_depth_texture_view: TextureView,
2021-06-02 02:59:17 +00:00
pub lights: Vec<Entity>,
pub gpu_light_binding_index: u32,
}
#[derive(Default)]
pub struct LightMeta {
pub view_gpu_lights: DynamicUniformVec<GpuLights>,
2021-06-21 23:28:52 +00:00
pub shadow_view_bind_group: Option<BindGroup>,
2021-06-02 02:59:17 +00:00
}
2021-07-08 03:01:41 +00:00
#[allow(clippy::too_many_arguments)]
2021-06-02 02:59:17 +00:00
pub fn prepare_lights(
mut commands: Commands,
mut texture_cache: ResMut<TextureCache>,
2021-06-21 23:28:52 +00:00
render_device: Res<RenderDevice>,
2021-06-02 02:59:17 +00:00
mut light_meta: ResMut<LightMeta>,
views: Query<Entity, With<RenderPhase<Transparent3dPhase>>>,
bevy_pbr2: Add support for most of the StandardMaterial textures (#4) * bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-06-27 23:10:23 +00:00
ambient_light: Res<ExtractedAmbientLight>,
point_lights: Query<&ExtractedPointLight>,
directional_lights: Query<&ExtractedDirectionalLight>,
2021-06-02 02:59:17 +00:00
) {
// PERF: view.iter().count() could be views.iter().len() if we implemented ExactSizeIterator for archetype-only filters
light_meta
.view_gpu_lights
2021-06-21 23:28:52 +00:00
.reserve_and_clear(views.iter().count(), &render_device);
2021-06-02 02:59:17 +00:00
bevy_pbr2: Add support for most of the StandardMaterial textures (#4) * bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-06-27 23:10:23 +00:00
let ambient_color = ambient_light.color.as_rgba_linear() * ambient_light.brightness;
2021-06-02 02:59:17 +00:00
// set up light data for each view
for entity in views.iter() {
let point_light_depth_texture = texture_cache.get(
&render_device,
TextureDescriptor {
size: POINT_SHADOW_SIZE,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: SHADOW_FORMAT,
usage: TextureUsage::RENDER_ATTACHMENT | TextureUsage::SAMPLED,
label: None,
},
);
let directional_light_depth_texture = texture_cache.get(
2021-06-21 23:28:52 +00:00
&render_device,
2021-06-02 02:59:17 +00:00
TextureDescriptor {
size: DIRECTIONAL_SHADOW_SIZE,
2021-06-02 02:59:17 +00:00
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: SHADOW_FORMAT,
usage: TextureUsage::RENDER_ATTACHMENT | TextureUsage::SAMPLED,
2021-06-21 23:28:52 +00:00
label: None,
2021-06-02 02:59:17 +00:00
},
);
let mut view_lights = Vec::new();
let mut gpu_lights = GpuLights {
bevy_pbr2: Add support for most of the StandardMaterial textures (#4) * bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-06-27 23:10:23 +00:00
ambient_color: ambient_color.into(),
n_point_lights: point_lights.iter().len() as u32,
n_directional_lights: directional_lights.iter().len() as u32,
point_lights: [GpuPointLight::default(); MAX_POINT_LIGHTS],
directional_lights: [GpuDirectionalLight::default(); MAX_DIRECTIONAL_LIGHTS],
2021-06-02 02:59:17 +00:00
};
// TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
for (light_index, light) in point_lights.iter().enumerate().take(MAX_POINT_LIGHTS) {
bevy_pbr2: Add support for most of the StandardMaterial textures (#4) * bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-06-27 23:10:23 +00:00
let projection =
Mat4::perspective_rh(std::f32::consts::FRAC_PI_2, 1.0, 0.1, light.range);
2021-06-02 02:59:17 +00:00
2021-07-01 23:48:55 +00:00
// ignore scale because we don't want to effectively scale light radius and range
// by applying those as a view transform to shadow map rendering of objects
// and ignore rotation because we want the shadow map projections to align with the axes
let view_translation = GlobalTransform::from_translation(light.transform.translation);
for (face_index, CubeMapFace { target, up }) in CUBE_MAP_FACES.iter().enumerate() {
// use the cubemap projection direction
let view_rotation = GlobalTransform::identity().looking_at(*target, *up);
let depth_texture_view =
point_light_depth_texture
2021-07-01 23:48:55 +00:00
.texture
.create_view(&TextureViewDescriptor {
label: None,
format: None,
dimension: Some(TextureViewDimension::D2),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: (light_index * 6 + face_index) as u32,
array_layer_count: NonZeroU32::new(1),
});
let view_light_entity = commands
.spawn()
.insert_bundle((
ViewLight {
depth_texture_view,
pass_name: format!(
"shadow pass point light {} {}",
light_index,
face_index_to_name(face_index)
),
},
2021-07-01 23:48:55 +00:00
ExtractedView {
width: POINT_SHADOW_SIZE.width,
height: POINT_SHADOW_SIZE.height,
2021-07-01 23:48:55 +00:00
transform: view_translation * view_rotation,
projection,
},
RenderPhase::<ShadowPhase>::default(),
))
.id();
view_lights.push(view_light_entity);
}
gpu_lights.point_lights[light_index] = GpuPointLight {
2021-06-02 02:59:17 +00:00
// premultiply color by intensity
// we don't use the alpha at all, so no reason to multiply only [0..3]
bevy_pbr2: Add support for most of the StandardMaterial textures (#4) * bevy_pbr2: Add support for most of the StandardMaterial textures Normal maps are not included here as they require tangents in a vertex attribute. * bevy_pbr2: Ensure RenderCommandQueue is ready for PbrShaders init * texture_pipelined: Add a light to the scene so we can see stuff * WIP bevy_pbr2: back to front sorting hack * bevy_pbr2: Uniform control flow for texture sampling in pbr.frag From 'fintelia' on the Bevy Render Rework Round 2 discussion: "My understanding is that GPUs these days never use the "execute both branches and select the result" strategy. Rather, what they do is evaluate the branch condition on all threads of a warp, and jump over it if all of them evaluate to false. If even a single thread needs to execute the if statement body, however, then the remaining threads are paused until that is completed." * bevy_pbr2: Simplify texture and sampler names The StandardMaterial_ prefix is no longer needed * bevy_pbr2: Match default 'AmbientColor' of current bevy_pbr for now * bevy_pbr2: Convert from non-linear to linear sRGB for the color uniform * bevy_pbr2: Add pbr_pipelined example * Fix view vector in pbr frag to work in ortho * bevy_pbr2: Use a 90 degree y fov and light range projection for lights * bevy_pbr2: Add AmbientLight resource * bevy_pbr2: Convert PointLight color to linear sRGB for use in fragment shader * bevy_pbr2: pbr.frag: Rename PointLight.projection to view_projection The uniform contains the view_projection matrix so this was incorrect. * bevy_pbr2: PointLight is an OmniLight as it has a radius * bevy_pbr2: Factoring out duplicated code * bevy_pbr2: Implement RenderAsset for StandardMaterial * Remove unnecessary texture and sampler clones * fix comment formatting * remove redundant Buffer:from * Don't extract meshes when their material textures aren't ready * make missing textures in the queue step an error Co-authored-by: Aevyrie <aevyrie@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-06-27 23:10:23 +00:00
color: (light.color.as_rgba_linear() * light.intensity).into(),
2021-07-02 01:05:20 +00:00
radius: light.radius,
position: light.transform.translation,
2021-07-01 23:48:55 +00:00
inverse_square_range: 1.0 / (light.range * light.range),
near: 0.1,
far: light.range,
// proj: projection,
shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias,
2021-06-02 02:59:17 +00:00
};
}
for (i, light) in directional_lights
.iter()
.enumerate()
.take(MAX_DIRECTIONAL_LIGHTS)
{
// direction is negated to be ready for N.L
let dir_to_light = -light.direction;
// convert from illuminance (lux) to candelas
//
// exposure is hard coded at the moment but should be replaced
// by values coming from the camera
// see: https://google.github.io/filament/Filament.html#imagingpipeline/physicallybasedcamera/exposuresettings
const APERTURE: f32 = 4.0;
const SHUTTER_SPEED: f32 = 1.0 / 250.0;
const SENSITIVITY: f32 = 100.0;
let ev100 =
f32::log2(APERTURE * APERTURE / SHUTTER_SPEED) - f32::log2(SENSITIVITY / 100.0);
let exposure = 1.0 / (f32::powf(2.0, ev100) * 1.2);
let intensity = light.illuminance * exposure;
// NOTE: A directional light seems to have to have an eye position on the line along the direction of the light
// through the world origin. I (Rob Swain) do not yet understand why it cannot be translated away from this.
let view = Mat4::look_at_rh(Vec3::ZERO, light.direction, Vec3::Y);
// NOTE: This orthographic projection defines the volume within which shadows from a directional light can be cast
let projection = light.projection;
gpu_lights.directional_lights[i] = GpuDirectionalLight {
// premultiply color by intensity
// we don't use the alpha at all, so no reason to multiply only [0..3]
color: (light.color.as_rgba_linear() * intensity).into(),
dir_to_light,
// NOTE: * view is correct, it should not be view.inverse() here
view_projection: projection * view,
shadow_depth_bias: light.shadow_depth_bias,
shadow_normal_bias: light.shadow_normal_bias,
};
let depth_texture_view =
directional_light_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: None,
format: None,
dimension: Some(TextureViewDimension::D2),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: i as u32,
array_layer_count: NonZeroU32::new(1),
});
let view_light_entity = commands
.spawn()
.insert_bundle((
ViewLight {
depth_texture_view,
pass_name: format!("shadow pass directional light {}", i),
},
ExtractedView {
width: DIRECTIONAL_SHADOW_SIZE.width,
height: DIRECTIONAL_SHADOW_SIZE.height,
transform: GlobalTransform::from_matrix(view.inverse()),
projection,
},
RenderPhase::<ShadowPhase>::default(),
))
.id();
view_lights.push(view_light_entity);
}
let point_light_depth_texture_view =
point_light_depth_texture
2021-07-01 23:48:55 +00:00
.texture
.create_view(&TextureViewDescriptor {
label: None,
format: None,
dimension: Some(TextureViewDimension::CubeArray),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
2021-07-02 01:05:20 +00:00
base_array_layer: 0,
2021-07-01 23:48:55 +00:00
array_layer_count: None,
});
let directional_light_depth_texture_view = directional_light_depth_texture
.texture
.create_view(&TextureViewDescriptor {
label: None,
format: None,
dimension: Some(TextureViewDimension::D2Array),
aspect: TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
});
2021-07-01 23:48:55 +00:00
2021-06-02 02:59:17 +00:00
commands.entity(entity).insert(ViewLights {
point_light_depth_texture: point_light_depth_texture.texture,
point_light_depth_texture_view,
directional_light_depth_texture: directional_light_depth_texture.texture,
directional_light_depth_texture_view,
2021-06-02 02:59:17 +00:00
lights: view_lights,
gpu_light_binding_index: light_meta.view_gpu_lights.push(gpu_lights),
});
}
light_meta
.view_gpu_lights
2021-06-21 23:28:52 +00:00
.write_to_staging_buffer(&render_device);
2021-06-02 02:59:17 +00:00
}
pub struct ShadowPhase;
pub struct ShadowPassNode {
main_view_query: QueryState<&'static ViewLights>,
view_light_query: QueryState<(&'static ViewLight, &'static RenderPhase<ShadowPhase>)>,
}
impl ShadowPassNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
main_view_query: QueryState::new(world),
view_light_query: QueryState::new(world),
}
}
}
impl Node for ShadowPassNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(ShadowPassNode::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.main_view_query.update_archetypes(world);
self.view_light_query.update_archetypes(world);
}
fn run(
&self,
graph: &mut RenderGraphContext,
2021-06-21 23:28:52 +00:00
render_context: &mut RenderContext,
2021-06-02 02:59:17 +00:00
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
2021-07-02 01:05:20 +00:00
if let Ok(view_lights) = self.main_view_query.get_manual(world, view_entity) {
for view_light_entity in view_lights.lights.iter().copied() {
let (view_light, shadow_phase) = self
.view_light_query
.get_manual(world, view_light_entity)
.unwrap();
2021-06-21 23:28:52 +00:00
let pass_descriptor = RenderPassDescriptor {
label: Some(&view_light.pass_name),
2021-06-21 23:28:52 +00:00
color_attachments: &[],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
2021-07-01 23:48:55 +00:00
view: &view_light.depth_texture_view,
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
2021-06-02 02:59:17 +00:00
}),
};
let draw_functions = world.get_resource::<DrawFunctions>().unwrap();
2021-06-21 23:28:52 +00:00
let render_pass = render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
let mut draw_functions = draw_functions.write();
let mut tracked_pass = TrackedRenderPass::new(render_pass);
for drawable in shadow_phase.drawn_things.iter() {
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
draw_function.draw(
world,
&mut tracked_pass,
view_light_entity,
drawable.draw_key,
drawable.sort_key,
);
}
}
2021-06-02 02:59:17 +00:00
}
Ok(())
}
}
2021-06-21 23:28:52 +00:00
type DrawShadowMeshParams<'s, 'w> = (
Res<'w, ShadowShaders>,
Res<'w, ExtractedMeshes>,
Res<'w, LightMeta>,
Res<'w, MeshMeta>,
Res<'w, RenderAssets<Mesh>>,
2021-06-21 23:28:52 +00:00
Query<'w, 's, &'w ViewUniformOffset>,
2021-06-02 02:59:17 +00:00
);
pub struct DrawShadowMesh {
2021-06-21 23:28:52 +00:00
params: SystemState<DrawShadowMeshParams<'static, 'static>>,
2021-06-02 02:59:17 +00:00
}
impl DrawShadowMesh {
pub fn new(world: &mut World) -> Self {
Self {
params: SystemState::new(world),
}
}
}
impl Draw for DrawShadowMesh {
2021-06-21 23:28:52 +00:00
fn draw<'w>(
2021-06-02 02:59:17 +00:00
&mut self,
2021-06-21 23:28:52 +00:00
world: &'w World,
pass: &mut TrackedRenderPass<'w>,
2021-06-02 02:59:17 +00:00
view: Entity,
draw_key: usize,
_sort_key: usize,
) {
let (shadow_shaders, extracted_meshes, light_meta, mesh_meta, meshes, views) =
2021-06-21 23:28:52 +00:00
self.params.get(world);
let view_uniform_offset = views.get(view).unwrap();
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
2021-07-01 23:48:55 +00:00
let shadow_shaders = shadow_shaders.into_inner();
pass.set_render_pipeline(&shadow_shaders.pipeline);
2021-06-02 02:59:17 +00:00
pass.set_bind_group(
0,
2021-06-21 23:28:52 +00:00
light_meta
.into_inner()
.shadow_view_bind_group
.as_ref()
.unwrap(),
&[view_uniform_offset.offset],
2021-06-02 02:59:17 +00:00
);
let transform_bindgroup_key = mesh_meta.mesh_transform_bind_group_key.unwrap();
2021-06-02 02:59:17 +00:00
pass.set_bind_group(
1,
2021-06-21 23:28:52 +00:00
mesh_meta
.into_inner()
.mesh_transform_bind_group
.get_value(transform_bindgroup_key)
2021-06-21 23:28:52 +00:00
.unwrap(),
&[extracted_mesh.transform_binding_offset],
2021-06-02 02:59:17 +00:00
);
let gpu_mesh = meshes.into_inner().get(&extracted_mesh.mesh).unwrap();
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
if let Some(index_info) = &gpu_mesh.index_info {
2021-06-21 23:28:52 +00:00
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
2021-06-02 02:59:17 +00:00
pass.draw_indexed(0..index_info.count, 0, 0..1);
} else {
panic!("non-indexed drawing not supported yet")
}
}
}