Limit figures drawn for rain occlusion

This commit is contained in:
IsseW 2022-06-08 15:58:56 +02:00
parent ac82689f83
commit a7c724a46d
7 changed files with 136 additions and 51 deletions

View File

@ -108,6 +108,11 @@ void main() {
vec2 view_pos = vec2(atan2(dir_2d.x, dir_2d.y), z); vec2 view_pos = vec2(atan2(dir_2d.x, dir_2d.y), z);
vec3 cam_wpos = cam_pos.xyz + focus_off.xyz; vec3 cam_wpos = cam_pos.xyz + focus_off.xyz;
// Rain density is now only based on the cameras current position.
// This could be affected by a setting where rain_density_at is instead
// called each iteration of the loop. With the current implementation
// of rain_dir this has issues with being in a place where it doesn't rain
// and seeing rain.
float rain = rain_density_at(cam_wpos.xy); float rain = rain_density_at(cam_wpos.xy);
if (rain > 0.0) { if (rain > 0.0) {
float rain_dist = 50.0; float rain_dist = 50.0;

View File

@ -354,11 +354,12 @@ impl State {
/// Get a mutable reference the current in-game weather grid. /// Get a mutable reference the current in-game weather grid.
pub fn weather_grid_mut(&mut self) -> FetchMut<WeatherGrid> { self.ecs.write_resource() } pub fn weather_grid_mut(&mut self) -> FetchMut<WeatherGrid> { self.ecs.write_resource() }
/// Get the current weather at a position. /// Get the current weather at a position in worldspace.
pub fn weather_at(&self, pos: Vec2<f32>) -> Weather { pub fn weather_at(&self, pos: Vec2<f32>) -> Weather {
self.weather_grid().get_interpolated(pos) self.weather_grid().get_interpolated(pos)
} }
/// Get the max weather near a position in worldspace.
pub fn max_weather_near(&self, pos: Vec2<f32>) -> Weather { pub fn max_weather_near(&self, pos: Vec2<f32>) -> Weather {
self.weather_grid().get_max_near(pos) self.weather_grid().get_max_near(pos)
} }

View File

@ -95,7 +95,7 @@ impl RainOcclusionFigurePipeline {
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Rain occlusion pipeline layout"), label: Some("Rain occlusion figure pipeline layout"),
push_constant_ranges: &[], push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &figure_layout.locals], bind_group_layouts: &[&global_layout.globals, &figure_layout.locals],
}); });

View File

@ -17,7 +17,7 @@ use crate::{
camera::{Camera, CameraMode, Dependents}, camera::{Camera, CameraMode, Dependents},
math, math,
terrain::Terrain, terrain::Terrain,
SceneData, TrailMgr, SceneData, TrailMgr, RAIN_THRESHOLD,
}, },
}; };
use anim::{ use anim::{
@ -620,6 +620,7 @@ impl FigureMgr {
scene_data: &SceneData, scene_data: &SceneData,
// Visible chunk data. // Visible chunk data.
visible_psr_bounds: math::Aabr<f32>, visible_psr_bounds: math::Aabr<f32>,
visible_por_bounds: math::Aabr<f32>,
camera: &Camera, camera: &Camera,
terrain: Option<&Terrain>, terrain: Option<&Terrain>,
) -> anim::vek::Aabb<f32> { ) -> anim::vek::Aabb<f32> {
@ -637,25 +638,27 @@ impl FigureMgr {
// of the image rendered from the light). If the position projected // of the image rendered from the light). If the position projected
// with the ray_mat matrix is valid, and shadows are otherwise enabled, // with the ray_mat matrix is valid, and shadows are otherwise enabled,
// we mark can_shadow. // we mark can_shadow.
let can_shadow_sun = { // Rain occlusion is very similar to sun shadows, but using a different ray_mat,
let ray_direction = scene_data.get_sun_dir(); // and only if it's raining.
let is_daylight = ray_direction.z < 0.0/*0.6*/; let (can_shadow_sun, can_occlude_rain) = {
// Are shadows enabled at all?
let can_shadow_sun = renderer.pipeline_modes().shadow.is_map() && is_daylight;
let Dependents { let Dependents {
proj_mat: _, proj_mat: _,
view_mat: _, view_mat: _,
cam_pos, cam_pos,
.. ..
} = camera.dependents(); } = camera.dependents();
let cam_pos = math::Vec3::from(cam_pos);
let ray_direction = math::Vec3::from(ray_direction);
// Transform (semi) world space to light space. let sun_dir = scene_data.get_sun_dir();
let ray_mat: math::Mat4<f32> = let is_daylight = sun_dir.z < 0.0/*0.6*/;
math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, math::Vec3::unit_y()); // Are shadows enabled at all?
let can_shadow_sun = renderer.pipeline_modes().shadow.is_map() && is_daylight;
let weather = scene_data.state.weather_at(cam_pos.xy());
let cam_pos = math::Vec3::from(cam_pos);
let focus_off = math::Vec3::from(camera.get_focus_pos().map(f32::trunc)); let focus_off = math::Vec3::from(camera.get_focus_pos().map(f32::trunc));
let ray_mat = ray_mat * math::Mat4::translation_3d(-focus_off); let focus_off_mat = math::Mat4::translation_3d(-focus_off);
let collides_with_aabr = |a: math::Aabr<f32>, b: math::Aabr<f32>| { let collides_with_aabr = |a: math::Aabr<f32>, b: math::Aabr<f32>| {
let min = math::Vec4::new(a.min.x, a.min.y, b.min.x, b.min.y); let min = math::Vec4::new(a.min.x, a.min.y, b.min.x, b.min.y);
@ -665,22 +668,40 @@ impl FigureMgr {
#[cfg(not(feature = "simd"))] #[cfg(not(feature = "simd"))]
return min.partial_cmple(&max).reduce_and(); return min.partial_cmple(&max).reduce_and();
}; };
move |pos: (anim::vek::Vec3<f32>,), radius: f32| {
// Short circuit when there are no shadows to cast. let can_shadow = |ray_direction: Vec3<f32>,
if !can_shadow_sun { enabled: bool,
return false; visible_bounds: math::Aabr<f32>| {
let ray_direction = math::Vec3::from(ray_direction);
// Transform (semi) world space to light space.
let ray_mat: math::Mat4<f32> =
math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, math::Vec3::unit_y());
let ray_mat = ray_mat * focus_off_mat;
move |pos: (anim::vek::Vec3<f32>,), radius: f32| {
// Short circuit when there are no shadows to cast.
if !enabled {
return false;
}
// First project center onto shadow map.
let center = (ray_mat * math::Vec4::new(pos.0.x, pos.0.y, pos.0.z, 1.0)).xy();
// Then, create an approximate bounding box (± radius).
let figure_box = math::Aabr {
min: center - radius,
max: center + radius,
};
// Quick intersection test for membership in the PSC (potential shader caster)
// list.
collides_with_aabr(figure_box, visible_bounds)
} }
// First project center onto shadow map. };
let center = (ray_mat * math::Vec4::new(pos.0.x, pos.0.y, pos.0.z, 1.0)).xy(); (
// Then, create an approximate bounding box (± radius). can_shadow(sun_dir, can_shadow_sun, visible_psr_bounds),
let figure_box = math::Aabr { can_shadow(
min: center - radius, weather.rain_vel(),
max: center + radius, weather.rain > RAIN_THRESHOLD,
}; visible_por_bounds,
// Quick intersection test for membership in the PSC (potential shader caster) ),
// list. )
collides_with_aabr(figure_box, visible_psr_bounds)
}
}; };
// Get player position. // Get player position.
@ -812,6 +833,8 @@ impl FigureMgr {
} else if vd_frac > 1.0 { } else if vd_frac > 1.0 {
state.as_mut().map(|state| state.visible = false); state.as_mut().map(|state| state.visible = false);
// Keep processing if this might be a shadow caster. // Keep processing if this might be a shadow caster.
// NOTE: Not worth to do for rain_occlusion, since that only happens in closeby
// chunks.
if !can_shadow_prev { if !can_shadow_prev {
continue; continue;
} }
@ -841,6 +864,7 @@ impl FigureMgr {
} else { } else {
// Check whether we can shadow. // Check whether we can shadow.
meta.can_shadow_sun = can_shadow_sun(pos, radius); meta.can_shadow_sun = can_shadow_sun(pos, radius);
meta.can_occlude_rain = can_occlude_rain(pos, radius);
} }
(in_frustum, lpindex) (in_frustum, lpindex)
} else { } else {
@ -5551,17 +5575,16 @@ impl FigureMgr {
visible_aabb visible_aabb
} }
pub fn render_shadows<'a>( fn render_shadow_mapping<'a>(
&'a self, &'a self,
drawer: &mut FigureShadowDrawer<'_, 'a>, drawer: &mut FigureShadowDrawer<'_, 'a>,
state: &State, state: &State,
tick: u64, tick: u64,
(camera, figure_lod_render_distance): CameraData, (camera, figure_lod_render_distance): CameraData,
filter_state: impl Fn(&FigureStateMeta) -> bool,
) { ) {
span!(_guard, "render_shadows", "FigureManager::render_shadows");
let ecs = state.ecs(); let ecs = state.ecs();
let items = ecs.read_storage::<Item>(); let items = ecs.read_storage::<Item>();
( (
&ecs.entities(), &ecs.entities(),
&ecs.read_storage::<Pos>(), &ecs.read_storage::<Pos>(),
@ -5590,7 +5613,7 @@ impl FigureMgr {
Some(Collider::Volume(vol)) => vol.mut_count, Some(Collider::Volume(vol)) => vol.mut_count,
_ => 0, _ => 0,
}, },
|state| state.can_shadow_sun(), &filter_state,
if matches!(body, Body::ItemDrop(_)) { items.get(entity).map(ItemKey::from) } else { None }, if matches!(body, Body::ItemDrop(_)) { items.get(entity).map(ItemKey::from) } else { None },
) { ) {
drawer.draw(model, bound); drawer.draw(model, bound);
@ -5598,6 +5621,32 @@ impl FigureMgr {
}); });
} }
pub fn render_shadows<'a>(
&'a self,
drawer: &mut FigureShadowDrawer<'_, 'a>,
state: &State,
tick: u64,
camera_data: CameraData,
) {
span!(_guard, "render_shadows", "FigureManager::render_shadows");
self.render_shadow_mapping(drawer, state, tick, camera_data, |state| {
state.can_shadow_sun()
})
}
pub fn render_rain_occlusion<'a>(
&'a self,
drawer: &mut FigureShadowDrawer<'_, 'a>,
state: &State,
tick: u64,
camera_data: CameraData,
) {
span!(_guard, "render_rain_occlusion", "FigureManager::render_rain_occlusion");
self.render_shadow_mapping(drawer, state, tick, camera_data, |state| {
state.can_occlude_rain()
})
}
pub fn render<'a>( pub fn render<'a>(
&'a self, &'a self,
drawer: &mut FigureDrawer<'_, 'a>, drawer: &mut FigureDrawer<'_, 'a>,
@ -6237,6 +6286,7 @@ pub struct FigureStateMeta {
last_ori: anim::vek::Quaternion<f32>, last_ori: anim::vek::Quaternion<f32>,
lpindex: u8, lpindex: u8,
can_shadow_sun: bool, can_shadow_sun: bool,
can_occlude_rain: bool,
visible: bool, visible: bool,
last_pos: Option<anim::vek::Vec3<f32>>, last_pos: Option<anim::vek::Vec3<f32>>,
avg_vel: anim::vek::Vec3<f32>, avg_vel: anim::vek::Vec3<f32>,
@ -6253,6 +6303,11 @@ impl FigureStateMeta {
// Either visible, or explicitly a shadow caster. // Either visible, or explicitly a shadow caster.
self.visible || self.can_shadow_sun self.visible || self.can_shadow_sun
} }
pub fn can_occlude_rain(&self) -> bool {
// Either visible, or explicitly a rain occluder.
self.visible || self.can_occlude_rain
}
} }
pub struct FigureState<S> { pub struct FigureState<S> {
@ -6311,6 +6366,7 @@ impl<S: Skeleton> FigureState<S> {
lpindex: 0, lpindex: 0,
visible: false, visible: false,
can_shadow_sun: false, can_shadow_sun: false,
can_occlude_rain: false,
last_pos: None, last_pos: None,
avg_vel: anim::vek::Vec3::zero(), avg_vel: anim::vek::Vec3::zero(),
last_light: 1.0, last_light: 1.0,

View File

@ -183,6 +183,8 @@ impl Lod {
} }
} }
// Update weather texture // Update weather texture
// NOTE: consider moving the lerping to a shader if the overhead of uploading to
// the gpu each frame becomes an issue.
let weather = client.state().weather_grid(); let weather = client.state().weather_grid();
let size = weather.size().as_::<u32>(); let size = weather.size().as_::<u32>();
renderer.update_texture( renderer.update_texture(

View File

@ -65,6 +65,9 @@ const SHADOW_FAR: f32 = 128.0; // Far plane for shadow map point light rendering
/// Used for first person camera effects /// Used for first person camera effects
const RUNNING_THRESHOLD: f32 = 0.7; const RUNNING_THRESHOLD: f32 = 0.7;
/// The threashold for starting calculations with rain.
const RAIN_THRESHOLD: f32 = 0.0;
/// is_daylight, array of active lights. /// is_daylight, array of active lights.
pub type LightData<'a> = (bool, &'a [Light]); pub type LightData<'a> = (bool, &'a [Light]);
@ -705,14 +708,19 @@ impl Scene {
self.debug.maintain(renderer); self.debug.maintain(renderer);
// Maintain the terrain. // Maintain the terrain.
let (_visible_bounds, visible_light_volume, visible_psr_bounds, visible_occlusion_volume) = let (
self.terrain.maintain( _visible_bounds,
renderer, visible_light_volume,
scene_data, visible_psr_bounds,
focus_pos, visible_occlusion_volume,
self.loaded_distance, visible_por_bounds,
&self.camera, ) = self.terrain.maintain(
); renderer,
scene_data,
focus_pos,
self.loaded_distance,
&self.camera,
);
// Maintain the figures. // Maintain the figures.
let _figure_bounds = self.figure_mgr.maintain( let _figure_bounds = self.figure_mgr.maintain(
@ -720,6 +728,7 @@ impl Scene {
&mut self.trail_mgr, &mut self.trail_mgr,
scene_data, scene_data,
visible_psr_bounds, visible_psr_bounds,
visible_por_bounds,
&self.camera, &self.camera,
Some(&self.terrain), Some(&self.terrain),
); );
@ -1003,7 +1012,7 @@ impl Scene {
let weather = client let weather = client
.state() .state()
.max_weather_near(focus_off.xy() + cam_pos.xy()); .max_weather_near(focus_off.xy() + cam_pos.xy());
if weather.rain > 0.0 { if weather.rain > RAIN_THRESHOLD {
let weather = client.state().weather_at(focus_off.xy() + cam_pos.xy()); let weather = client.state().weather_at(focus_off.xy() + cam_pos.xy());
let rain_vel = weather.rain_vel(); let rain_vel = weather.rain_vel();
let rain_view_mat = math::Mat4::look_at_rh(look_at, look_at + rain_vel, up); let rain_view_mat = math::Mat4::look_at_rh(look_at, look_at + rain_vel, up);
@ -1139,7 +1148,7 @@ impl Scene {
let is_daylight = sun_dir.z < 0.0; let is_daylight = sun_dir.z < 0.0;
let focus_pos = self.camera.get_focus_pos(); let focus_pos = self.camera.get_focus_pos();
let cam_pos = self.camera.dependents().cam_pos + focus_pos.map(|e| e.trunc()); let cam_pos = self.camera.dependents().cam_pos + focus_pos.map(|e| e.trunc());
let is_rain = state.max_weather_near(cam_pos.xy()).rain > 0.0; let is_rain = state.max_weather_near(cam_pos.xy()).rain > RAIN_THRESHOLD;
let camera_data = (&self.camera, scene_data.figure_lod_render_distance); let camera_data = (&self.camera, scene_data.figure_lod_render_distance);
@ -1178,7 +1187,7 @@ impl Scene {
self.terrain self.terrain
.render_rain_occlusion(&mut occlusion_pass.draw_terrain_shadows(), cam_pos); .render_rain_occlusion(&mut occlusion_pass.draw_terrain_shadows(), cam_pos);
self.figure_mgr.render_shadows( self.figure_mgr.render_rain_occlusion(
&mut occlusion_pass.draw_figure_shadows(), &mut occlusion_pass.draw_figure_shadows(),
state, state,
tick, tick,

View File

@ -18,7 +18,7 @@ use crate::{
use super::{ use super::{
camera::{self, Camera}, camera::{self, Camera},
math, SceneData, math, SceneData, RAIN_THRESHOLD,
}; };
use common::{ use common::{
assets::{self, AssetExt, DotVoxAsset}, assets::{self, AssetExt, DotVoxAsset},
@ -821,6 +821,7 @@ impl<V: RectRasterableVol> Terrain<V> {
Vec<math::Vec3<f32>>, Vec<math::Vec3<f32>>,
math::Aabr<f32>, math::Aabr<f32>,
Vec<math::Vec3<f32>>, Vec<math::Vec3<f32>>,
math::Aabr<f32>,
) { ) {
let camera::Dependents { let camera::Dependents {
view_mat, view_mat,
@ -1313,6 +1314,9 @@ impl<V: RectRasterableVol> Terrain<V> {
#[cfg(not(feature = "simd"))] #[cfg(not(feature = "simd"))]
return min.partial_cmple(&max).reduce_and(); return min.partial_cmple(&max).reduce_and();
}; };
let cam_pos = math::Vec4::from(view_mat.inverted() * Vec4::unit_w()).xyz();
let (visible_light_volume, visible_psr_bounds) = if ray_direction.z < 0.0 let (visible_light_volume, visible_psr_bounds) = if ray_direction.z < 0.0
&& renderer.pipeline_modes().shadow.is_map() && renderer.pipeline_modes().shadow.is_map()
{ {
@ -1335,9 +1339,7 @@ impl<V: RectRasterableVol> Terrain<V> {
.map(|v| v.as_::<f32>()) .map(|v| v.as_::<f32>())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let cam_pos = math::Vec4::from(view_mat.inverted() * Vec4::unit_w()).xyz();
let up: math::Vec3<f32> = { math::Vec3::unit_y() }; let up: math::Vec3<f32> = { math::Vec3::unit_y() };
let ray_mat = math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, up); let ray_mat = math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, up);
let visible_bounds = math::Aabr::from(math::fit_psr( let visible_bounds = math::Aabr::from(math::fit_psr(
ray_mat, ray_mat,
@ -1407,7 +1409,7 @@ impl<V: RectRasterableVol> Terrain<V> {
span!(guard, "Rain occlusion magic"); span!(guard, "Rain occlusion magic");
// Check if there is rain near the camera // Check if there is rain near the camera
let max_weather = scene_data.state.max_weather_near(focus_pos.xy()); let max_weather = scene_data.state.max_weather_near(focus_pos.xy());
let visible_occlusion_volume = if max_weather.rain > 0.0 { let (visible_occlusion_volume, visible_por_bounds) = if max_weather.rain > RAIN_THRESHOLD {
let visible_bounding_box = math::Aabb::<f32> { let visible_bounding_box = math::Aabb::<f32> {
min: math::Vec3::from(visible_bounding_box.min - focus_off), min: math::Vec3::from(visible_bounding_box.min - focus_off),
max: math::Vec3::from(visible_bounding_box.max - focus_off), max: math::Vec3::from(visible_bounding_box.max - focus_off),
@ -1422,16 +1424,25 @@ impl<V: RectRasterableVol> Terrain<V> {
// NOTE: We use proj_mat_treeculler here because // NOTE: We use proj_mat_treeculler here because
// calc_focused_light_volume_points makes the assumption that the // calc_focused_light_volume_points makes the assumption that the
// near plane lies before the far plane. // near plane lies before the far plane.
math::calc_focused_light_volume_points( let visible_volume = math::calc_focused_light_volume_points(
inv_proj_view, inv_proj_view,
ray_direction.as_::<f64>(), ray_direction.as_::<f64>(),
visible_bounds_fine, visible_bounds_fine,
1e-6, 1e-6,
) )
.map(|v| v.as_::<f32>()) .map(|v| v.as_::<f32>())
.collect::<Vec<_>>() .collect::<Vec<_>>();
let ray_mat =
math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, math::Vec3::unit_y());
let visible_bounds = math::Aabr::from(math::fit_psr(
ray_mat,
visible_volume.iter().copied(),
|p| p,
));
(visible_volume, visible_bounds)
} else { } else {
Vec::new() (Vec::new(), math::Aabr::default())
}; };
drop(guard); drop(guard);
@ -1440,6 +1451,7 @@ impl<V: RectRasterableVol> Terrain<V> {
visible_light_volume, visible_light_volume,
visible_psr_bounds, visible_psr_bounds,
visible_occlusion_volume, visible_occlusion_volume,
visible_por_bounds,
) )
} }