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);
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);
if (rain > 0.0) {
float rain_dist = 50.0;

View File

@ -354,11 +354,12 @@ impl State {
/// Get a mutable reference the current in-game weather grid.
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 {
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 {
self.weather_grid().get_max_near(pos)
}

View File

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

View File

@ -17,7 +17,7 @@ use crate::{
camera::{Camera, CameraMode, Dependents},
math,
terrain::Terrain,
SceneData, TrailMgr,
SceneData, TrailMgr, RAIN_THRESHOLD,
},
};
use anim::{
@ -620,6 +620,7 @@ impl FigureMgr {
scene_data: &SceneData,
// Visible chunk data.
visible_psr_bounds: math::Aabr<f32>,
visible_por_bounds: math::Aabr<f32>,
camera: &Camera,
terrain: Option<&Terrain>,
) -> anim::vek::Aabb<f32> {
@ -637,25 +638,27 @@ impl FigureMgr {
// of the image rendered from the light). If the position projected
// with the ray_mat matrix is valid, and shadows are otherwise enabled,
// we mark can_shadow.
let can_shadow_sun = {
let ray_direction = scene_data.get_sun_dir();
let is_daylight = ray_direction.z < 0.0/*0.6*/;
// Are shadows enabled at all?
let can_shadow_sun = renderer.pipeline_modes().shadow.is_map() && is_daylight;
// Rain occlusion is very similar to sun shadows, but using a different ray_mat,
// and only if it's raining.
let (can_shadow_sun, can_occlude_rain) = {
let Dependents {
proj_mat: _,
view_mat: _,
cam_pos,
..
} = 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 ray_mat: math::Mat4<f32> =
math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, math::Vec3::unit_y());
let sun_dir = scene_data.get_sun_dir();
let is_daylight = sun_dir.z < 0.0/*0.6*/;
// 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 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 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"))]
return min.partial_cmple(&max).reduce_and();
};
move |pos: (anim::vek::Vec3<f32>,), radius: f32| {
// Short circuit when there are no shadows to cast.
if !can_shadow_sun {
return false;
let can_shadow = |ray_direction: Vec3<f32>,
enabled: bool,
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).
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_psr_bounds)
}
};
(
can_shadow(sun_dir, can_shadow_sun, visible_psr_bounds),
can_shadow(
weather.rain_vel(),
weather.rain > RAIN_THRESHOLD,
visible_por_bounds,
),
)
};
// Get player position.
@ -812,6 +833,8 @@ impl FigureMgr {
} else if vd_frac > 1.0 {
state.as_mut().map(|state| state.visible = false);
// 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 {
continue;
}
@ -841,6 +864,7 @@ impl FigureMgr {
} else {
// Check whether we can shadow.
meta.can_shadow_sun = can_shadow_sun(pos, radius);
meta.can_occlude_rain = can_occlude_rain(pos, radius);
}
(in_frustum, lpindex)
} else {
@ -5551,17 +5575,16 @@ impl FigureMgr {
visible_aabb
}
pub fn render_shadows<'a>(
fn render_shadow_mapping<'a>(
&'a self,
drawer: &mut FigureShadowDrawer<'_, 'a>,
state: &State,
tick: u64,
(camera, figure_lod_render_distance): CameraData,
filter_state: impl Fn(&FigureStateMeta) -> bool,
) {
span!(_guard, "render_shadows", "FigureManager::render_shadows");
let ecs = state.ecs();
let items = ecs.read_storage::<Item>();
(
&ecs.entities(),
&ecs.read_storage::<Pos>(),
@ -5590,7 +5613,7 @@ impl FigureMgr {
Some(Collider::Volume(vol)) => vol.mut_count,
_ => 0,
},
|state| state.can_shadow_sun(),
&filter_state,
if matches!(body, Body::ItemDrop(_)) { items.get(entity).map(ItemKey::from) } else { None },
) {
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>(
&'a self,
drawer: &mut FigureDrawer<'_, 'a>,
@ -6237,6 +6286,7 @@ pub struct FigureStateMeta {
last_ori: anim::vek::Quaternion<f32>,
lpindex: u8,
can_shadow_sun: bool,
can_occlude_rain: bool,
visible: bool,
last_pos: Option<anim::vek::Vec3<f32>>,
avg_vel: anim::vek::Vec3<f32>,
@ -6253,6 +6303,11 @@ impl FigureStateMeta {
// Either visible, or explicitly a shadow caster.
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> {
@ -6311,6 +6366,7 @@ impl<S: Skeleton> FigureState<S> {
lpindex: 0,
visible: false,
can_shadow_sun: false,
can_occlude_rain: false,
last_pos: None,
avg_vel: anim::vek::Vec3::zero(),
last_light: 1.0,

View File

@ -183,6 +183,8 @@ impl Lod {
}
}
// 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 size = weather.size().as_::<u32>();
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
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.
pub type LightData<'a> = (bool, &'a [Light]);
@ -705,14 +708,19 @@ impl Scene {
self.debug.maintain(renderer);
// Maintain the terrain.
let (_visible_bounds, visible_light_volume, visible_psr_bounds, visible_occlusion_volume) =
self.terrain.maintain(
renderer,
scene_data,
focus_pos,
self.loaded_distance,
&self.camera,
);
let (
_visible_bounds,
visible_light_volume,
visible_psr_bounds,
visible_occlusion_volume,
visible_por_bounds,
) = self.terrain.maintain(
renderer,
scene_data,
focus_pos,
self.loaded_distance,
&self.camera,
);
// Maintain the figures.
let _figure_bounds = self.figure_mgr.maintain(
@ -720,6 +728,7 @@ impl Scene {
&mut self.trail_mgr,
scene_data,
visible_psr_bounds,
visible_por_bounds,
&self.camera,
Some(&self.terrain),
);
@ -1003,7 +1012,7 @@ impl Scene {
let weather = client
.state()
.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 rain_vel = weather.rain_vel();
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 focus_pos = self.camera.get_focus_pos();
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);
@ -1178,7 +1187,7 @@ impl Scene {
self.terrain
.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(),
state,
tick,

View File

@ -18,7 +18,7 @@ use crate::{
use super::{
camera::{self, Camera},
math, SceneData,
math, SceneData, RAIN_THRESHOLD,
};
use common::{
assets::{self, AssetExt, DotVoxAsset},
@ -821,6 +821,7 @@ impl<V: RectRasterableVol> Terrain<V> {
Vec<math::Vec3<f32>>,
math::Aabr<f32>,
Vec<math::Vec3<f32>>,
math::Aabr<f32>,
) {
let camera::Dependents {
view_mat,
@ -1313,6 +1314,9 @@ impl<V: RectRasterableVol> Terrain<V> {
#[cfg(not(feature = "simd"))]
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
&& renderer.pipeline_modes().shadow.is_map()
{
@ -1335,9 +1339,7 @@ impl<V: RectRasterableVol> Terrain<V> {
.map(|v| v.as_::<f32>())
.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 ray_mat = math::Mat4::look_at_rh(cam_pos, cam_pos + ray_direction, up);
let visible_bounds = math::Aabr::from(math::fit_psr(
ray_mat,
@ -1407,7 +1409,7 @@ impl<V: RectRasterableVol> Terrain<V> {
span!(guard, "Rain occlusion magic");
// Check if there is rain near the camera
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> {
min: math::Vec3::from(visible_bounding_box.min - 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
// calc_focused_light_volume_points makes the assumption that the
// 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,
ray_direction.as_::<f64>(),
visible_bounds_fine,
1e-6,
)
.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 {
Vec::new()
(Vec::new(), math::Aabr::default())
};
drop(guard);
@ -1440,6 +1451,7 @@ impl<V: RectRasterableVol> Terrain<V> {
visible_light_volume,
visible_psr_bounds,
visible_occlusion_volume,
visible_por_bounds,
)
}