Skip to content

Commit

Permalink
bevy_render2/bevy_pbr2: Use the infinite reverse rh perspective proje…
Browse files Browse the repository at this point in the history
…ction

This projection is industry standard and offers better precision distribution
over the depth range than forward projections.
  • Loading branch information
superdump committed Jul 25, 2021
1 parent c5f4223 commit 077489b
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 22 deletions.
2 changes: 1 addition & 1 deletion pipelined/bevy_pbr2/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl Default for PointLight {

impl PointLight {
pub const DEFAULT_SHADOW_DEPTH_BIAS: f32 = 0.02;
pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.5;
pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6;
}

/// A Directional light.
Expand Down
14 changes: 7 additions & 7 deletions pipelined/bevy_pbr2/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ pub struct ExtractedDirectionalLight {
#[repr(C)]
#[derive(Copy, Clone, AsStd140, Default, Debug)]
pub struct GpuPointLight {
projection: Mat4,
color: Vec4,
// proj: Mat4,
position: Vec3,
inverse_square_range: f32,
radius: f32,
Expand Down Expand Up @@ -164,7 +164,7 @@ impl FromWorld for ShadowShaders {
depth_stencil: Some(DepthStencilState {
format: SHADOW_FORMAT,
depth_write_enabled: true,
depth_compare: CompareFunction::LessEqual,
depth_compare: CompareFunction::GreaterEqual,
stencil: StencilState {
front: StencilFaceState::IGNORE,
back: StencilFaceState::IGNORE,
Expand Down Expand Up @@ -200,7 +200,7 @@ impl FromWorld for ShadowShaders {
mag_filter: FilterMode::Linear,
min_filter: FilterMode::Linear,
mipmap_filter: FilterMode::Nearest,
compare: Some(CompareFunction::LessEqual),
compare: Some(CompareFunction::GreaterEqual),
..Default::default()
}),
directional_light_sampler: render_device.create_sampler(&SamplerDescriptor {
Expand All @@ -210,7 +210,7 @@ impl FromWorld for ShadowShaders {
mag_filter: FilterMode::Linear,
min_filter: FilterMode::Linear,
mipmap_filter: FilterMode::Nearest,
compare: Some(CompareFunction::LessEqual),
compare: Some(CompareFunction::GreaterEqual),
..Default::default()
}),
}
Expand Down Expand Up @@ -410,7 +410,7 @@ pub fn prepare_lights(
// 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) {
let projection =
Mat4::perspective_rh(std::f32::consts::FRAC_PI_2, 1.0, 0.1, light.range);
Mat4::perspective_infinite_reverse_rh(std::f32::consts::FRAC_PI_2, 1.0, 0.1);

// 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
Expand Down Expand Up @@ -459,6 +459,7 @@ pub fn prepare_lights(
}

gpu_lights.point_lights[light_index] = GpuPointLight {
projection,
// 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() * light.intensity).into(),
Expand All @@ -467,7 +468,6 @@ pub fn prepare_lights(
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,
};
Expand Down Expand Up @@ -631,7 +631,7 @@ impl Node for ShadowPassNode {
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &view_light.depth_texture_view,
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
load: LoadOp::Clear(0.0),
store: true,
}),
stencil_ops: None,
Expand Down
4 changes: 2 additions & 2 deletions pipelined/bevy_pbr2/src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl FromWorld for PbrShaders {
has_dynamic_offset: true,
// TODO: change this to GpuLights::std140_size_static once crevice fixes this!
// Context: https://github.com/LPGhatguy/crevice/issues/29
min_binding_size: BufferSize::new(1024),
min_binding_size: BufferSize::new(1424),
},
count: None,
},
Expand Down Expand Up @@ -293,7 +293,7 @@ impl FromWorld for PbrShaders {
depth_stencil: Some(DepthStencilState {
format: TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: CompareFunction::Less,
depth_compare: CompareFunction::Greater,
stencil: StencilState {
front: StencilFaceState::IGNORE,
back: StencilFaceState::IGNORE,
Expand Down
29 changes: 20 additions & 9 deletions pipelined/bevy_pbr2/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ struct StandardMaterial {
};

struct PointLight {
projection: mat4x4<f32>;
color: vec4<f32>;
// projection: mat4x4<f32>;
position: vec3<f32>;
inverse_square_range: f32;
radius: f32;
Expand Down Expand Up @@ -400,14 +400,25 @@ fn fetch_point_shadow(light_id: i32, frag_position: vec4<f32>, surface_normal: v
let abs_position_ls = abs(frag_ls);
let major_axis_magnitude = max(abs_position_ls.x, max(abs_position_ls.y, abs_position_ls.z));

// do a full projection
// vec4 clip = light.projection * vec4(0.0, 0.0, -major_axis_magnitude, 1.0);
// float depth = (clip.z / clip.w);
// NOTE: These simplifications come from multiplying:
// projection * vec4(0, 0, -major_axis_magnitude, 1.0)
// and keeping only the terms that have any impact on the depth.
// Projection-agnostic approach:
let projection_cols23_row2 = vec2<f32>(light.projection[2][2], light.projection[3][2]);
let projection_cols23_row3 = vec2<f32>(light.projection[2][3], light.projection[3][3]);
let frag_zw = vec2<f32>(-major_axis_magnitude, 1.0);
let z = dot(projection_cols23_row2, frag_zw);
let w = dot(projection_cols23_row3, frag_zw);

// For perspective_rh:
// let proj_r = light.far / (light.near - light.far);
// let z = -major_axis_magnitude * proj_r + light.near * proj_r;
// let w = major_axis_magnitude;

// For perspective_infinite_reverse_rh:
// let z = light.near;
// let w = major_axis_magnitude;

// alternatively do only the necessary multiplications using near/far
let proj_r = light.far / (light.near - light.far);
let z = -major_axis_magnitude * proj_r + light.near * proj_r;
let w = major_axis_magnitude;
let depth = z / w;

// do the lookup, using HW PCF and comparison
Expand Down Expand Up @@ -518,7 +529,7 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
V = normalize(view.world_position.xyz - in.world_position.xyz);
} else {
// Ortho view vec
V = normalize(vec3<f32>(-view.view_proj.x.z, -view.view_proj.y.z, -view.view_proj.z.z));
V = normalize(vec3<f32>(view.view_proj.x.z, view.view_proj.y.z, view.view_proj.z.z));
}

// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
Expand Down
6 changes: 4 additions & 2 deletions pipelined/bevy_render2/src/camera/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub struct PerspectiveProjection {

impl CameraProjection for PerspectiveProjection {
fn get_projection_matrix(&self) -> Mat4 {
Mat4::perspective_rh(self.fov, self.aspect_ratio, self.near, self.far)
Mat4::perspective_infinite_reverse_rh(self.fov, self.aspect_ratio, self.near)
}

fn update(&mut self, width: f32, height: f32) {
Expand Down Expand Up @@ -88,8 +88,10 @@ impl CameraProjection for OrthographicProjection {
self.right * self.scale,
self.bottom * self.scale,
self.top * self.scale,
self.near,
// NOTE: near and far are swapped to invert the depth range from [0,1] to [1,0]
// This is for interoperability with pipelines using infinite reverse perspective projections.
self.far,
self.near,
)
}

Expand Down
2 changes: 1 addition & 1 deletion pipelined/bevy_render2/src/core_pipeline/main_pass_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl Node for MainPass3dNode {
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: depth_texture,
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
load: LoadOp::Clear(0.0),
store: true,
}),
stencil_ops: None,
Expand Down

0 comments on commit 077489b

Please sign in to comment.