mirror of
https://github.com/bevyengine/bevy
synced 2024-11-25 06:00:20 +00:00
Use the infinite reverse right-handed perspective projection (#2543)
# Objective Forward perspective projections have poor floating point precision distribution over the depth range. Reverse projections fair much better, and instead of having to have a far plane, with the reverse projection, using an infinite far plane is not a problem. The infinite reverse perspective projection has become the industry standard. The renderer rework is a great time to migrate to it. ## Solution All perspective projections, including point lights, have been moved to using `glam::Mat4::perspective_infinite_reverse_rh()` and so have no far plane. As various depth textures are shared between orthographic and perspective projections, a quirk of this PR is that the near and far planes of the orthographic projection are swapped when the Mat4 is computed. This has no impact on 2D/3D orthographic projection usage, and provides consistency in shaders, texture clear values, etc. throughout the codebase. ## Known issues For some reason, when looking along -Z, all geometry is black. The camera can be translated up/down / strafed left/right and geometry will still be black. Moving forward/backward or rotating the camera away from looking exactly along -Z causes everything to work as expected. I have tried to debug this issue but both in macOS and Windows I get crashes when doing pixel debugging. If anyone could reproduce this and debug it I would be very grateful. Otherwise I will have to try to debug it further without pixel debugging, though the projections and such all looked fine to me.
This commit is contained in:
parent
cea7db1050
commit
045f324e97
13 changed files with 153 additions and 44 deletions
|
@ -179,6 +179,10 @@ path = "examples/3d/msaa.rs"
|
|||
name = "orthographic"
|
||||
path = "examples/3d/orthographic.rs"
|
||||
|
||||
[[example]]
|
||||
name = "orthographic_pipelined"
|
||||
path = "examples/3d/orthographic_pipelined.rs"
|
||||
|
||||
[[example]]
|
||||
name = "parenting"
|
||||
path = "examples/3d/parenting.rs"
|
||||
|
|
|
@ -36,11 +36,7 @@ fn setup(
|
|||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
commands.insert_resource(AmbientLight {
|
||||
color: Color::ORANGE_RED,
|
||||
brightness: 0.02,
|
||||
});
|
||||
// plane
|
||||
// ground plane
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Plane { size: 10.0 })),
|
||||
material: materials.add(StandardMaterial {
|
||||
|
@ -51,6 +47,7 @@ fn setup(
|
|||
..Default::default()
|
||||
});
|
||||
|
||||
// left wall
|
||||
let mut transform = Transform::from_xyz(2.5, 2.5, 0.0);
|
||||
transform.rotate(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2));
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
|
@ -63,7 +60,7 @@ fn setup(
|
|||
}),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// back (right) wall
|
||||
let mut transform = Transform::from_xyz(0.0, 2.5, -2.5);
|
||||
transform.rotate(Quat::from_rotation_x(std::f32::consts::FRAC_PI_2));
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
|
@ -76,6 +73,7 @@ fn setup(
|
|||
}),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// cube
|
||||
commands
|
||||
.spawn_bundle(PbrBundle {
|
||||
|
@ -104,7 +102,13 @@ fn setup(
|
|||
})
|
||||
.insert(Movable);
|
||||
|
||||
// light
|
||||
// ambient light
|
||||
commands.insert_resource(AmbientLight {
|
||||
color: Color::ORANGE_RED,
|
||||
brightness: 0.02,
|
||||
});
|
||||
|
||||
// red point light
|
||||
commands
|
||||
.spawn_bundle(PointLightBundle {
|
||||
// transform: Transform::from_xyz(5.0, 8.0, 2.0),
|
||||
|
@ -131,7 +135,7 @@ fn setup(
|
|||
});
|
||||
});
|
||||
|
||||
// light
|
||||
// green point light
|
||||
commands
|
||||
.spawn_bundle(PointLightBundle {
|
||||
// transform: Transform::from_xyz(5.0, 8.0, 2.0),
|
||||
|
@ -158,7 +162,7 @@ fn setup(
|
|||
});
|
||||
});
|
||||
|
||||
// light
|
||||
// blue point light
|
||||
commands
|
||||
.spawn_bundle(PointLightBundle {
|
||||
// transform: Transform::from_xyz(5.0, 8.0, 2.0),
|
||||
|
@ -185,6 +189,7 @@ fn setup(
|
|||
});
|
||||
});
|
||||
|
||||
// directional 'sun' light
|
||||
const HALF_SIZE: f32 = 10.0;
|
||||
commands.spawn_bundle(DirectionalLightBundle {
|
||||
directional_light: DirectionalLight {
|
||||
|
|
71
examples/3d/orthographic_pipelined.rs
Normal file
71
examples/3d/orthographic_pipelined.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use bevy::{
|
||||
ecs::prelude::*,
|
||||
math::Vec3,
|
||||
pbr2::{PbrBundle, PointLightBundle, StandardMaterial},
|
||||
prelude::{App, Assets, Transform},
|
||||
render2::{
|
||||
camera::OrthographicCameraBundle,
|
||||
color::Color,
|
||||
mesh::{shape, Mesh},
|
||||
},
|
||||
PipelinedDefaultPlugins,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(PipelinedDefaultPlugins)
|
||||
.add_startup_system(setup.system())
|
||||
.run();
|
||||
}
|
||||
|
||||
/// set up a simple 3D scene
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
// set up the camera
|
||||
let mut camera = OrthographicCameraBundle::new_3d();
|
||||
camera.orthographic_projection.scale = 3.0;
|
||||
camera.transform = Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y);
|
||||
|
||||
// camera
|
||||
commands.spawn_bundle(camera);
|
||||
|
||||
// plane
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
|
||||
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
|
||||
..Default::default()
|
||||
});
|
||||
// cubes
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
||||
transform: Transform::from_xyz(1.5, 0.5, 1.5),
|
||||
..Default::default()
|
||||
});
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
||||
transform: Transform::from_xyz(1.5, 0.5, -1.5),
|
||||
..Default::default()
|
||||
});
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
||||
transform: Transform::from_xyz(-1.5, 0.5, 1.5),
|
||||
..Default::default()
|
||||
});
|
||||
commands.spawn_bundle(PbrBundle {
|
||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
|
||||
transform: Transform::from_xyz(-1.5, 0.5, -1.5),
|
||||
..Default::default()
|
||||
});
|
||||
// light
|
||||
commands.spawn_bundle(PointLightBundle {
|
||||
transform: Transform::from_xyz(3.0, 8.0, 5.0),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
|
@ -103,6 +103,7 @@ Example | File | Description
|
|||
`load_gltf_pipelined` | [`3d/load_gltf_pipelined.rs`](./3d/load_gltf_pipelined.rs) | Loads and renders a gltf file as a scene
|
||||
`msaa` | [`3d/msaa.rs`](./3d/msaa.rs) | Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges
|
||||
`orthographic` | [`3d/orthographic.rs`](./3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications)
|
||||
`orthographic_pipelined` | [`3d/orthographic_pipelined.rs`](./3d/orthographic_pipelined.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications)
|
||||
`parenting` | [`3d/parenting.rs`](./3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations
|
||||
`pbr` | [`3d/pbr.rs`](./3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
|
||||
`pbr_pipelined` | [`3d/pbr_pipelined.rs`](./3d/pbr_pipelined.rs) | Demonstrates use of Physically Based Rendering (PBR) properties
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -46,7 +46,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;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// NOTE: Keep in sync with pbr.wgsl
|
||||
[[block]]
|
||||
struct View {
|
||||
view_proj: mat4x4<f32>;
|
||||
projection: mat4x4<f32>;
|
||||
world_position: vec3<f32>;
|
||||
};
|
||||
[[group(0), binding(0)]]
|
||||
|
@ -9,7 +11,7 @@ var view: View;
|
|||
|
||||
[[block]]
|
||||
struct Mesh {
|
||||
transform: mat4x4<f32>;
|
||||
model: mat4x4<f32>;
|
||||
};
|
||||
[[group(1), binding(0)]]
|
||||
var mesh: Mesh;
|
||||
|
@ -25,6 +27,6 @@ struct VertexOutput {
|
|||
[[stage(vertex)]]
|
||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.clip_position = view.view_proj * mesh.transform * vec4<f32>(vertex.position, 1.0);
|
||||
out.clip_position = view.view_proj * mesh.model * vec4<f32>(vertex.position, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -54,8 +54,8 @@ pub type ExtractedDirectionalLightShadowMap = DirectionalLightShadowMap;
|
|||
#[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,
|
||||
|
@ -120,7 +120,7 @@ impl FromWorld for ShadowShaders {
|
|||
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),
|
||||
min_binding_size: BufferSize::new(144),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
@ -168,7 +168,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,
|
||||
|
@ -205,7 +205,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 {
|
||||
|
@ -215,7 +215,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()
|
||||
}),
|
||||
}
|
||||
|
@ -435,7 +435,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
|
||||
|
@ -484,6 +484,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(),
|
||||
|
@ -492,7 +493,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,
|
||||
};
|
||||
|
@ -656,7 +656,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,
|
||||
|
|
|
@ -53,7 +53,7 @@ impl FromWorld for PbrShaders {
|
|||
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),
|
||||
min_binding_size: BufferSize::new(144),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
|
@ -66,7 +66,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,
|
||||
},
|
||||
|
@ -225,7 +225,9 @@ impl FromWorld for PbrShaders {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(80),
|
||||
// TODO: change this to MeshUniform::std140_size_static once crevice fixes this!
|
||||
// Context: https://github.com/LPGhatguy/crevice/issues/29
|
||||
min_binding_size: BufferSize::new(144),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
|
@ -291,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,
|
||||
|
@ -455,6 +457,7 @@ struct MeshDrawInfo {
|
|||
#[derive(Debug, AsStd140)]
|
||||
pub struct MeshUniform {
|
||||
model: Mat4,
|
||||
inverse_transpose_model: Mat4,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
|
@ -486,13 +489,16 @@ pub fn prepare_meshes(
|
|||
.transform_uniforms
|
||||
.reserve_and_clear(extracted_meshes.meshes.len(), &render_device);
|
||||
for extracted_mesh in extracted_meshes.meshes.iter_mut() {
|
||||
let model = extracted_mesh.transform;
|
||||
let inverse_transpose_model = model.inverse().transpose();
|
||||
let flags = if extracted_mesh.receives_shadows {
|
||||
MeshFlags::SHADOW_RECEIVER
|
||||
} else {
|
||||
MeshFlags::NONE
|
||||
};
|
||||
extracted_mesh.transform_binding_offset = mesh_meta.transform_uniforms.push(MeshUniform {
|
||||
model: extracted_mesh.transform,
|
||||
model,
|
||||
inverse_transpose_model,
|
||||
flags: flags.bits,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
// TODO: try merging this block with the binding?
|
||||
// NOTE: Keep in sync with depth.wgsl
|
||||
[[block]]
|
||||
struct View {
|
||||
view_proj: mat4x4<f32>;
|
||||
projection: mat4x4<f32>;
|
||||
world_position: vec3<f32>;
|
||||
};
|
||||
|
||||
|
@ -9,6 +11,7 @@ struct View {
|
|||
[[block]]
|
||||
struct Mesh {
|
||||
model: mat4x4<f32>;
|
||||
inverse_transpose_model: mat4x4<f32>;
|
||||
// 'flags' is a bit field indicating various options. u32 is 32 bits so we have up to 32 options.
|
||||
flags: u32;
|
||||
};
|
||||
|
@ -41,9 +44,11 @@ fn vertex(vertex: Vertex) -> VertexOutput {
|
|||
out.uv = vertex.uv;
|
||||
out.world_position = world_position;
|
||||
out.clip_position = view.view_proj * world_position;
|
||||
// FIXME: The inverse transpose of the model matrix should be used to correctly handle scaling
|
||||
// of normals
|
||||
out.world_normal = mat3x3<f32>(mesh.model.x.xyz, mesh.model.y.xyz, mesh.model.z.xyz) * vertex.normal;
|
||||
out.world_normal = mat3x3<f32>(
|
||||
mesh.inverse_transpose_model.x.xyz,
|
||||
mesh.inverse_transpose_model.y.xyz,
|
||||
mesh.inverse_transpose_model.z.xyz
|
||||
) * vertex.normal;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -100,8 +105,8 @@ let STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT: u32 = 16u;
|
|||
let STANDARD_MATERIAL_FLAGS_UNLIT_BIT: u32 = 32u;
|
||||
|
||||
struct PointLight {
|
||||
projection: mat4x4<f32>;
|
||||
color: vec4<f32>;
|
||||
// projection: mat4x4<f32>;
|
||||
position: vec3<f32>;
|
||||
inverse_square_range: f32;
|
||||
radius: f32;
|
||||
|
@ -408,14 +413,22 @@ 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 z = -major_axis_magnitude * light.projection[2][2] + light.projection[3][2];
|
||||
let w = -major_axis_magnitude * light.projection[2][3] + light.projection[3][3];
|
||||
|
||||
// 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
|
||||
|
@ -521,12 +534,12 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
|
|||
// # endif
|
||||
|
||||
var V: vec3<f32>;
|
||||
if (view.view_proj.w.w != 1.0) { // If the projection is not orthographic
|
||||
if (view.projection.w.w != 1.0) { // If the projection is not orthographic
|
||||
// Only valid for a perpective projection
|
||||
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"
|
||||
|
|
|
@ -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) {
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ pub struct ExtractedView {
|
|||
#[derive(Clone, AsStd140)]
|
||||
pub struct ViewUniform {
|
||||
view_proj: Mat4,
|
||||
projection: Mat4,
|
||||
world_position: Vec3,
|
||||
}
|
||||
|
||||
|
@ -64,9 +65,11 @@ fn prepare_views(
|
|||
.uniforms
|
||||
.reserve_and_clear(extracted_views.iter_mut().len(), &render_resources);
|
||||
for (entity, camera) in extracted_views.iter() {
|
||||
let projection = camera.projection;
|
||||
let view_uniforms = ViewUniformOffset {
|
||||
offset: view_meta.uniforms.push(ViewUniform {
|
||||
view_proj: camera.projection * camera.transform.compute_matrix().inverse(),
|
||||
view_proj: projection * camera.transform.compute_matrix().inverse(),
|
||||
projection,
|
||||
world_position: camera.transform.translation,
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ use bevy_render2::{
|
|||
renderer::{RenderContext, RenderDevice},
|
||||
shader::Shader,
|
||||
texture::{BevyDefault, Image},
|
||||
view::{ViewMeta, ViewUniform, ViewUniformOffset},
|
||||
view::{ViewMeta, ViewUniformOffset},
|
||||
RenderWorld,
|
||||
};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
|
@ -42,7 +42,9 @@ impl FromWorld for SpriteShaders {
|
|||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: BufferSize::new(std::mem::size_of::<ViewUniform>() as u64),
|
||||
// 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(144),
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
|
|
Loading…
Reference in a new issue