mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Rain occlusion projection matrix
This commit is contained in:
parent
295b89d446
commit
234ed5afb2
@ -102,8 +102,8 @@ void main() {
|
||||
#ifdef EXPERIMENTAL_RAIN
|
||||
vec3 old_color = color.rgb;
|
||||
|
||||
// If this value is changed also change it in voxygen/src/scene/mod.rs
|
||||
float fall_rate = 70.0;
|
||||
|
||||
dir.xy += wind_vel * dir.z / fall_rate;
|
||||
dir = normalize(dir);
|
||||
|
||||
|
@ -17,15 +17,6 @@ uniform u_rain_occlusion {
|
||||
float rain_occlusion_at(in vec3 fragPos)
|
||||
{
|
||||
float bias = -0.2;
|
||||
float diskRadius = 0.01;
|
||||
const vec3 sampleOffsetDirections[20] = vec3[]
|
||||
(
|
||||
vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1),
|
||||
vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1),
|
||||
vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0),
|
||||
vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1),
|
||||
vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1)
|
||||
);
|
||||
|
||||
vec4 rain_pos = occlusion_texture_mat * vec4(fragPos, 1.0) - vec4(0, 0, bias, 0);
|
||||
|
||||
|
@ -124,7 +124,7 @@ float cloud_tendency_at(vec2 pos) {
|
||||
const float RAIN_CLOUD = 0.05;
|
||||
|
||||
float rain_density_at(vec2 pos) {
|
||||
return 1.0; //sample_weather(pos).g;
|
||||
return sample_weather(pos).g;
|
||||
//return clamp((cloud_tendency_at(pos) - RAIN_CLOUD) * 10, 0, 1);
|
||||
}
|
||||
|
||||
|
@ -494,7 +494,6 @@ pub struct DebugInfo {
|
||||
pub num_figures_visible: u32,
|
||||
pub num_particles: u32,
|
||||
pub num_particles_visible: u32,
|
||||
pub weather: Weather,
|
||||
}
|
||||
|
||||
pub struct HudInfo {
|
||||
|
@ -722,13 +722,300 @@ impl Scene {
|
||||
Some(&self.terrain),
|
||||
);
|
||||
|
||||
let fov = self.camera.get_effective_fov();
|
||||
let aspect_ratio = self.camera.get_aspect_ratio();
|
||||
let view_dir = ((focus_pos.map(f32::fract)) - cam_pos).normalized();
|
||||
|
||||
// We need to compute these offset matrices to transform world space coordinates
|
||||
// to the translated ones we use when multiplying by the light space
|
||||
// matrix; this helps avoid precision loss during the
|
||||
// multiplication.
|
||||
let look_at = math::Vec3::from(cam_pos);
|
||||
let new_dir = math::Vec3::from(view_dir);
|
||||
let new_dir = new_dir.normalized();
|
||||
let up: math::Vec3<f32> = math::Vec3::unit_y();
|
||||
|
||||
// Optimal warping for directed lights:
|
||||
//
|
||||
// n_opt = 1 / sin y (z_n + √(z_n + (f - n) sin y))
|
||||
//
|
||||
// where n is near plane, f is far plane, y is the tilt angle between view and
|
||||
// light direction, and n_opt is the optimal near plane.
|
||||
// We also want a way to transform and scale this matrix (* 0.5 + 0.5) in order
|
||||
// to transform it correctly into texture coordinates, as well as
|
||||
// OpenGL coordinates. Note that the matrix for directional light
|
||||
// is *already* linear in the depth buffer.
|
||||
//
|
||||
// Also, observe that we flip the texture sampling matrix in order to account
|
||||
// for the fact that DirectX renders top-down.
|
||||
let texture_mat = Mat4::<f32>::scaling_3d::<Vec3<f32>>(Vec3::new(0.5, -0.5, 1.0))
|
||||
* Mat4::translation_3d(Vec3::new(1.0, -1.0, 0.0));
|
||||
|
||||
let directed_mats = |d_view_mat: math::Mat4<f32>,
|
||||
d_dir: math::Vec3<f32>|
|
||||
-> (Mat4<f32>, Mat4<f32>) {
|
||||
// NOTE: Light view space, right-handed.
|
||||
let v_p_orig = math::Vec3::from(d_view_mat * math::Vec4::from_direction(new_dir));
|
||||
let mut v_p = v_p_orig.normalized();
|
||||
let cos_gamma = new_dir.map(f64::from).dot(d_dir.map(f64::from));
|
||||
let sin_gamma = (1.0 - cos_gamma * cos_gamma).sqrt();
|
||||
let gamma = sin_gamma.asin();
|
||||
let view_mat = math::Mat4::from_col_array(view_mat.into_col_array());
|
||||
// coordinates are transformed from world space (right-handed) to view space
|
||||
// (right-handed).
|
||||
let bounds1 = math::fit_psr(
|
||||
view_mat.map_cols(math::Vec4::from),
|
||||
visible_light_volume.iter().copied(),
|
||||
math::Vec4::homogenized,
|
||||
);
|
||||
let n_e = f64::from(-bounds1.max.z);
|
||||
let factor = compute_warping_parameter_perspective(
|
||||
gamma,
|
||||
n_e,
|
||||
f64::from(fov),
|
||||
f64::from(aspect_ratio),
|
||||
);
|
||||
|
||||
v_p.z = 0.0;
|
||||
v_p.normalize();
|
||||
let l_r: math::Mat4<f32> = if factor > EPSILON_UPSILON {
|
||||
// NOTE: Our coordinates are now in left-handed space, but v_p isn't; however,
|
||||
// v_p has no z component, so we don't have to adjust it for left-handed
|
||||
// spaces.
|
||||
math::Mat4::look_at_lh(math::Vec3::zero(), math::Vec3::unit_z(), v_p)
|
||||
} else {
|
||||
math::Mat4::identity()
|
||||
};
|
||||
// Convert from right-handed to left-handed coordinates.
|
||||
let directed_proj_mat = math::Mat4::new(
|
||||
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
|
||||
);
|
||||
|
||||
let light_all_mat = l_r * directed_proj_mat * d_view_mat;
|
||||
// coordinates are transformed from world space (right-handed) to rotated light
|
||||
// space (left-handed).
|
||||
let bounds0 = math::fit_psr(
|
||||
light_all_mat,
|
||||
visible_light_volume.iter().copied(),
|
||||
math::Vec4::homogenized,
|
||||
);
|
||||
// Vague idea: project z_n from the camera view to the light view (where it's
|
||||
// tilted by γ).
|
||||
//
|
||||
// NOTE: To transform a normal by M, we multiply by the transpose of the inverse
|
||||
// of M. For the cases below, we are transforming by an
|
||||
// already-inverted matrix, so the transpose of its inverse is
|
||||
// just the transpose of the original matrix.
|
||||
let (z_0, z_1) = {
|
||||
let f_e = f64::from(-bounds1.min.z).max(n_e);
|
||||
// view space, right-handed coordinates.
|
||||
let p_z = bounds1.max.z;
|
||||
// rotated light space, left-handed coordinates.
|
||||
let p_y = bounds0.min.y;
|
||||
let p_x = bounds0.center().x;
|
||||
// moves from view-space (right-handed) to world space (right-handed)
|
||||
let view_inv = view_mat.inverted();
|
||||
// moves from rotated light space (left-handed) to world space (right-handed).
|
||||
let light_all_inv = light_all_mat.inverted();
|
||||
|
||||
// moves from view-space (right-handed) to world-space (right-handed).
|
||||
let view_point = view_inv
|
||||
* math::Vec4::from_point(
|
||||
-math::Vec3::unit_z() * p_z, /* + math::Vec4::unit_w() */
|
||||
);
|
||||
let view_plane = view_mat.transposed() * -math::Vec4::unit_z();
|
||||
|
||||
// moves from rotated light space (left-handed) to world space (right-handed).
|
||||
let light_point = light_all_inv
|
||||
* math::Vec4::from_point(
|
||||
math::Vec3::unit_y() * p_y, /* + math::Vec4::unit_w() */
|
||||
);
|
||||
let light_plane = light_all_mat.transposed() * math::Vec4::unit_y();
|
||||
|
||||
// moves from rotated light space (left-handed) to world space (right-handed).
|
||||
let shadow_point = light_all_inv
|
||||
* math::Vec4::from_point(
|
||||
math::Vec3::unit_x() * p_x, /* + math::Vec4::unit_w() */
|
||||
);
|
||||
let shadow_plane = light_all_mat.transposed() * math::Vec4::unit_x();
|
||||
|
||||
// Find the point at the intersection of the three planes; note that since the
|
||||
// equations are already in right-handed world space, we don't need to negate
|
||||
// the z coordinates.
|
||||
let solve_p0 = math::Mat4::new(
|
||||
view_plane.x,
|
||||
view_plane.y,
|
||||
view_plane.z,
|
||||
0.0,
|
||||
light_plane.x,
|
||||
light_plane.y,
|
||||
light_plane.z,
|
||||
0.0,
|
||||
shadow_plane.x,
|
||||
shadow_plane.y,
|
||||
shadow_plane.z,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
|
||||
// in world-space (right-handed).
|
||||
let plane_dist = math::Vec4::new(
|
||||
view_plane.dot(view_point),
|
||||
light_plane.dot(light_point),
|
||||
shadow_plane.dot(shadow_point),
|
||||
1.0,
|
||||
);
|
||||
let p0_world = solve_p0.inverted() * plane_dist;
|
||||
// in rotated light-space (left-handed).
|
||||
let p0 = light_all_mat * p0_world;
|
||||
let mut p1 = p0;
|
||||
// in rotated light-space (left-handed).
|
||||
p1.y = bounds0.max.y;
|
||||
|
||||
// transforms from rotated light-space (left-handed) to view space
|
||||
// (right-handed).
|
||||
let view_from_light_mat = view_mat * light_all_inv;
|
||||
// z0 and z1 are in view space (right-handed).
|
||||
let z0 = view_from_light_mat * p0;
|
||||
let z1 = view_from_light_mat * p1;
|
||||
|
||||
// Extract the homogenized forward component (right-handed).
|
||||
//
|
||||
// NOTE: I don't think the w component should be anything but 1 here, but
|
||||
// better safe than sorry.
|
||||
(
|
||||
f64::from(z0.homogenized().dot(-math::Vec4::unit_z())).clamp(n_e, f_e),
|
||||
f64::from(z1.homogenized().dot(-math::Vec4::unit_z())).clamp(n_e, f_e),
|
||||
)
|
||||
};
|
||||
|
||||
// all of this is in rotated light-space (left-handed).
|
||||
let mut light_focus_pos: math::Vec3<f32> = math::Vec3::zero();
|
||||
light_focus_pos.x = bounds0.center().x;
|
||||
light_focus_pos.y = bounds0.min.y;
|
||||
light_focus_pos.z = bounds0.center().z;
|
||||
|
||||
let d = f64::from(bounds0.max.y - bounds0.min.y).abs();
|
||||
|
||||
let w_l_y = d;
|
||||
|
||||
// NOTE: See section 5.1.2.2 of Lloyd's thesis.
|
||||
// NOTE: Since z_1 and z_0 are in the same coordinate space, we don't have to
|
||||
// worry about the handedness of their ratio.
|
||||
let alpha = z_1 / z_0;
|
||||
let alpha_sqrt = alpha.sqrt();
|
||||
let directed_near_normal = if factor < 0.0 {
|
||||
// Standard shadow map to LiSPSM
|
||||
(1.0 + alpha_sqrt - factor * (alpha - 1.0)) / ((alpha - 1.0) * (factor + 1.0))
|
||||
} else {
|
||||
// LiSPSM to PSM
|
||||
((alpha_sqrt - 1.0) * (factor * alpha_sqrt + 1.0)).recip()
|
||||
};
|
||||
|
||||
// Equation 5.14 - 5.16
|
||||
let y_ = |v: f64| w_l_y * (v + directed_near_normal).abs();
|
||||
let directed_near = y_(0.0) as f32;
|
||||
let directed_far = y_(1.0) as f32;
|
||||
light_focus_pos.y = if factor > EPSILON_UPSILON {
|
||||
light_focus_pos.y - directed_near
|
||||
} else {
|
||||
light_focus_pos.y
|
||||
};
|
||||
// Left-handed translation.
|
||||
let w_v: math::Mat4<f32> = math::Mat4::translation_3d(-math::Vec3::new(
|
||||
light_focus_pos.x,
|
||||
light_focus_pos.y,
|
||||
light_focus_pos.z,
|
||||
));
|
||||
let shadow_view_mat: math::Mat4<f32> = w_v * light_all_mat;
|
||||
let w_p: math::Mat4<f32> = {
|
||||
if factor > EPSILON_UPSILON {
|
||||
// Projection for y
|
||||
let near = directed_near;
|
||||
let far = directed_far;
|
||||
let left = -1.0;
|
||||
let right = 1.0;
|
||||
let bottom = -1.0;
|
||||
let top = 1.0;
|
||||
let s_x = 2.0 * near / (right - left);
|
||||
let o_x = (right + left) / (right - left);
|
||||
let s_z = 2.0 * near / (top - bottom);
|
||||
let o_z = (top + bottom) / (top - bottom);
|
||||
|
||||
let s_y = (far + near) / (far - near);
|
||||
let o_y = -2.0 * far * near / (far - near);
|
||||
|
||||
math::Mat4::new(
|
||||
s_x, o_x, 0.0, 0.0, 0.0, s_y, 0.0, o_y, 0.0, o_z, s_z, 0.0, 0.0, 1.0, 0.0,
|
||||
0.0,
|
||||
)
|
||||
} else {
|
||||
math::Mat4::identity()
|
||||
}
|
||||
};
|
||||
|
||||
let shadow_all_mat: math::Mat4<f32> = w_p * shadow_view_mat;
|
||||
// coordinates are transformed from world space (right-handed)
|
||||
// to post-warp light space (left-handed), then homogenized.
|
||||
let math::Aabb::<f32> {
|
||||
min:
|
||||
math::Vec3 {
|
||||
x: xmin,
|
||||
y: ymin,
|
||||
z: zmin,
|
||||
},
|
||||
max:
|
||||
math::Vec3 {
|
||||
x: xmax,
|
||||
y: ymax,
|
||||
z: zmax,
|
||||
},
|
||||
} = math::fit_psr(
|
||||
shadow_all_mat,
|
||||
visible_light_volume.iter().copied(),
|
||||
math::Vec4::homogenized,
|
||||
);
|
||||
let s_x = 2.0 / (xmax - xmin);
|
||||
let s_y = 2.0 / (ymax - ymin);
|
||||
let s_z = 1.0 / (zmax - zmin);
|
||||
let o_x = -(xmax + xmin) / (xmax - xmin);
|
||||
let o_y = -(ymax + ymin) / (ymax - ymin);
|
||||
let o_z = -zmin / (zmax - zmin);
|
||||
let directed_proj_mat = Mat4::new(
|
||||
s_x, 0.0, 0.0, o_x, 0.0, s_y, 0.0, o_y, 0.0, 0.0, s_z, o_z, 0.0, 0.0, 0.0, 1.0,
|
||||
);
|
||||
|
||||
let shadow_all_mat: Mat4<f32> = Mat4::from_col_arrays(shadow_all_mat.into_col_arrays());
|
||||
|
||||
let directed_texture_proj_mat = texture_mat * directed_proj_mat;
|
||||
(
|
||||
directed_proj_mat * shadow_all_mat,
|
||||
directed_texture_proj_mat * shadow_all_mat,
|
||||
)
|
||||
};
|
||||
|
||||
let weather = client.current_weather_wpos(focus_off.xy() + cam_pos.xy());
|
||||
if true || weather.rain > 0.001
|
||||
// TODO: check if rain map mode is on
|
||||
{
|
||||
// If this value is changed also change it in cloud-frag.glsl
|
||||
const FALL_RATE: f32 = 70.0;
|
||||
let rain_dir =
|
||||
math::Vec3::from(-Vec3::unit_z() + weather.wind / FALL_RATE).normalized();
|
||||
let rain_view_mat = math::Mat4::look_at_rh(look_at, look_at + rain_dir, up);
|
||||
|
||||
let (shadow_mat, texture_mat) = directed_mats(rain_view_mat, rain_dir);
|
||||
|
||||
let rain_occlusion_locals = RainOcclusionLocals::new(shadow_mat, texture_mat);
|
||||
renderer.update_consts(&mut self.data.rain_occlusion_mats, &[rain_occlusion_locals]);
|
||||
}
|
||||
|
||||
let sun_dir = scene_data.get_sun_dir();
|
||||
let is_daylight = sun_dir.z < 0.0;
|
||||
if renderer.pipeline_modes().shadow.is_map() && (is_daylight || !lights.is_empty()) {
|
||||
let fov = self.camera.get_effective_fov();
|
||||
let aspect_ratio = self.camera.get_aspect_ratio();
|
||||
|
||||
let view_dir = ((focus_pos.map(f32::fract)) - cam_pos).normalized();
|
||||
let (point_shadow_res, _directed_shadow_res) = renderer.get_shadow_resolution();
|
||||
// NOTE: The aspect ratio is currently always 1 for our cube maps, since they
|
||||
// are equal on all sides.
|
||||
@ -737,289 +1024,17 @@ impl Scene {
|
||||
// and moon.
|
||||
let directed_light_dir = math::Vec3::from(sun_dir);
|
||||
|
||||
// Optimal warping for directed lights:
|
||||
//
|
||||
// n_opt = 1 / sin y (z_n + √(z_n + (f - n) sin y))
|
||||
//
|
||||
// where n is near plane, f is far plane, y is the tilt angle between view and
|
||||
// light direction, and n_opt is the optimal near plane.
|
||||
// We also want a way to transform and scale this matrix (* 0.5 + 0.5) in order
|
||||
// to transform it correctly into texture coordinates, as well as
|
||||
// OpenGL coordinates. Note that the matrix for directional light
|
||||
// is *already* linear in the depth buffer.
|
||||
//
|
||||
// Also, observe that we flip the texture sampling matrix in order to account
|
||||
// for the fact that DirectX renders top-down.
|
||||
let texture_mat = Mat4::<f32>::scaling_3d::<Vec3<f32>>(Vec3::new(0.5, -0.5, 1.0))
|
||||
* Mat4::translation_3d(Vec3::new(1.0, -1.0, 0.0));
|
||||
// We need to compute these offset matrices to transform world space coordinates
|
||||
// to the translated ones we use when multiplying by the light space
|
||||
// matrix; this helps avoid precision loss during the
|
||||
// multiplication.
|
||||
let look_at = math::Vec3::from(cam_pos);
|
||||
// We upload view matrices as well, to assist in linearizing vertex positions.
|
||||
// (only for directional lights, so far).
|
||||
let mut directed_shadow_mats = Vec::with_capacity(6);
|
||||
let new_dir = math::Vec3::from(view_dir);
|
||||
let new_dir = new_dir.normalized();
|
||||
let up: math::Vec3<f32> = math::Vec3::unit_y();
|
||||
|
||||
let light_view_mat = math::Mat4::look_at_rh(look_at, look_at + directed_light_dir, up);
|
||||
{
|
||||
// NOTE: Light view space, right-handed.
|
||||
let v_p_orig =
|
||||
math::Vec3::from(light_view_mat * math::Vec4::from_direction(new_dir));
|
||||
let mut v_p = v_p_orig.normalized();
|
||||
let cos_gamma = new_dir
|
||||
.map(f64::from)
|
||||
.dot(directed_light_dir.map(f64::from));
|
||||
let sin_gamma = (1.0 - cos_gamma * cos_gamma).sqrt();
|
||||
let gamma = sin_gamma.asin();
|
||||
let view_mat = math::Mat4::from_col_array(view_mat.into_col_array());
|
||||
// coordinates are transformed from world space (right-handed) to view space
|
||||
// (right-handed).
|
||||
let bounds1 = math::fit_psr(
|
||||
view_mat.map_cols(math::Vec4::from),
|
||||
visible_light_volume.iter().copied(),
|
||||
math::Vec4::homogenized,
|
||||
);
|
||||
let n_e = f64::from(-bounds1.max.z);
|
||||
let factor = compute_warping_parameter_perspective(
|
||||
gamma,
|
||||
n_e,
|
||||
f64::from(fov),
|
||||
f64::from(aspect_ratio),
|
||||
);
|
||||
let (shadow_mat, texture_mat) = directed_mats(light_view_mat, directed_light_dir);
|
||||
|
||||
v_p.z = 0.0;
|
||||
v_p.normalize();
|
||||
let l_r: math::Mat4<f32> = if factor > EPSILON_UPSILON {
|
||||
// NOTE: Our coordinates are now in left-handed space, but v_p isn't; however,
|
||||
// v_p has no z component, so we don't have to adjust it for left-handed
|
||||
// spaces.
|
||||
math::Mat4::look_at_lh(math::Vec3::zero(), math::Vec3::unit_z(), v_p)
|
||||
} else {
|
||||
math::Mat4::identity()
|
||||
};
|
||||
// Convert from right-handed to left-handed coordinates.
|
||||
let directed_proj_mat = math::Mat4::new(
|
||||
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
|
||||
);
|
||||
let shadow_locals = ShadowLocals::new(shadow_mat, texture_mat);
|
||||
|
||||
let light_all_mat = l_r * directed_proj_mat * light_view_mat;
|
||||
// coordinates are transformed from world space (right-handed) to rotated light
|
||||
// space (left-handed).
|
||||
let bounds0 = math::fit_psr(
|
||||
light_all_mat,
|
||||
visible_light_volume.iter().copied(),
|
||||
math::Vec4::homogenized,
|
||||
);
|
||||
// Vague idea: project z_n from the camera view to the light view (where it's
|
||||
// tilted by γ).
|
||||
//
|
||||
// NOTE: To transform a normal by M, we multiply by the transpose of the inverse
|
||||
// of M. For the cases below, we are transforming by an
|
||||
// already-inverted matrix, so the transpose of its inverse is
|
||||
// just the transpose of the original matrix.
|
||||
let (z_0, z_1) = {
|
||||
let f_e = f64::from(-bounds1.min.z).max(n_e);
|
||||
// view space, right-handed coordinates.
|
||||
let p_z = bounds1.max.z;
|
||||
// rotated light space, left-handed coordinates.
|
||||
let p_y = bounds0.min.y;
|
||||
let p_x = bounds0.center().x;
|
||||
// moves from view-space (right-handed) to world space (right-handed)
|
||||
let view_inv = view_mat.inverted();
|
||||
// moves from rotated light space (left-handed) to world space (right-handed).
|
||||
let light_all_inv = light_all_mat.inverted();
|
||||
renderer.update_consts(&mut self.data.shadow_mats, &[shadow_locals]);
|
||||
|
||||
// moves from view-space (right-handed) to world-space (right-handed).
|
||||
let view_point = view_inv
|
||||
* math::Vec4::from_point(
|
||||
-math::Vec3::unit_z() * p_z, /* + math::Vec4::unit_w() */
|
||||
);
|
||||
let view_plane = view_mat.transposed() * -math::Vec4::unit_z();
|
||||
|
||||
// moves from rotated light space (left-handed) to world space (right-handed).
|
||||
let light_point = light_all_inv
|
||||
* math::Vec4::from_point(
|
||||
math::Vec3::unit_y() * p_y, /* + math::Vec4::unit_w() */
|
||||
);
|
||||
let light_plane = light_all_mat.transposed() * math::Vec4::unit_y();
|
||||
|
||||
// moves from rotated light space (left-handed) to world space (right-handed).
|
||||
let shadow_point = light_all_inv
|
||||
* math::Vec4::from_point(
|
||||
math::Vec3::unit_x() * p_x, /* + math::Vec4::unit_w() */
|
||||
);
|
||||
let shadow_plane = light_all_mat.transposed() * math::Vec4::unit_x();
|
||||
|
||||
// Find the point at the intersection of the three planes; note that since the
|
||||
// equations are already in right-handed world space, we don't need to negate
|
||||
// the z coordinates.
|
||||
let solve_p0 = math::Mat4::new(
|
||||
view_plane.x,
|
||||
view_plane.y,
|
||||
view_plane.z,
|
||||
0.0,
|
||||
light_plane.x,
|
||||
light_plane.y,
|
||||
light_plane.z,
|
||||
0.0,
|
||||
shadow_plane.x,
|
||||
shadow_plane.y,
|
||||
shadow_plane.z,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
|
||||
// in world-space (right-handed).
|
||||
let plane_dist = math::Vec4::new(
|
||||
view_plane.dot(view_point),
|
||||
light_plane.dot(light_point),
|
||||
shadow_plane.dot(shadow_point),
|
||||
1.0,
|
||||
);
|
||||
let p0_world = solve_p0.inverted() * plane_dist;
|
||||
// in rotated light-space (left-handed).
|
||||
let p0 = light_all_mat * p0_world;
|
||||
let mut p1 = p0;
|
||||
// in rotated light-space (left-handed).
|
||||
p1.y = bounds0.max.y;
|
||||
|
||||
// transforms from rotated light-space (left-handed) to view space
|
||||
// (right-handed).
|
||||
let view_from_light_mat = view_mat * light_all_inv;
|
||||
// z0 and z1 are in view space (right-handed).
|
||||
let z0 = view_from_light_mat * p0;
|
||||
let z1 = view_from_light_mat * p1;
|
||||
|
||||
// Extract the homogenized forward component (right-handed).
|
||||
//
|
||||
// NOTE: I don't think the w component should be anything but 1 here, but
|
||||
// better safe than sorry.
|
||||
(
|
||||
f64::from(z0.homogenized().dot(-math::Vec4::unit_z())).clamp(n_e, f_e),
|
||||
f64::from(z1.homogenized().dot(-math::Vec4::unit_z())).clamp(n_e, f_e),
|
||||
)
|
||||
};
|
||||
|
||||
// all of this is in rotated light-space (left-handed).
|
||||
let mut light_focus_pos: math::Vec3<f32> = math::Vec3::zero();
|
||||
light_focus_pos.x = bounds0.center().x;
|
||||
light_focus_pos.y = bounds0.min.y;
|
||||
light_focus_pos.z = bounds0.center().z;
|
||||
|
||||
let d = f64::from(bounds0.max.y - bounds0.min.y).abs();
|
||||
|
||||
let w_l_y = d;
|
||||
|
||||
// NOTE: See section 5.1.2.2 of Lloyd's thesis.
|
||||
// NOTE: Since z_1 and z_0 are in the same coordinate space, we don't have to
|
||||
// worry about the handedness of their ratio.
|
||||
let alpha = z_1 / z_0;
|
||||
let alpha_sqrt = alpha.sqrt();
|
||||
let directed_near_normal = if factor < 0.0 {
|
||||
// Standard shadow map to LiSPSM
|
||||
(1.0 + alpha_sqrt - factor * (alpha - 1.0)) / ((alpha - 1.0) * (factor + 1.0))
|
||||
} else {
|
||||
// LiSPSM to PSM
|
||||
((alpha_sqrt - 1.0) * (factor * alpha_sqrt + 1.0)).recip()
|
||||
};
|
||||
|
||||
// Equation 5.14 - 5.16
|
||||
let y_ = |v: f64| w_l_y * (v + directed_near_normal).abs();
|
||||
let directed_near = y_(0.0) as f32;
|
||||
let directed_far = y_(1.0) as f32;
|
||||
light_focus_pos.y = if factor > EPSILON_UPSILON {
|
||||
light_focus_pos.y - directed_near
|
||||
} else {
|
||||
light_focus_pos.y
|
||||
};
|
||||
// Left-handed translation.
|
||||
let w_v: math::Mat4<f32> = math::Mat4::translation_3d(-math::Vec3::new(
|
||||
light_focus_pos.x,
|
||||
light_focus_pos.y,
|
||||
light_focus_pos.z,
|
||||
));
|
||||
let shadow_view_mat: math::Mat4<f32> = w_v * light_all_mat;
|
||||
let w_p: math::Mat4<f32> = {
|
||||
if factor > EPSILON_UPSILON {
|
||||
// Projection for y
|
||||
let near = directed_near;
|
||||
let far = directed_far;
|
||||
let left = -1.0;
|
||||
let right = 1.0;
|
||||
let bottom = -1.0;
|
||||
let top = 1.0;
|
||||
let s_x = 2.0 * near / (right - left);
|
||||
let o_x = (right + left) / (right - left);
|
||||
let s_z = 2.0 * near / (top - bottom);
|
||||
let o_z = (top + bottom) / (top - bottom);
|
||||
|
||||
let s_y = (far + near) / (far - near);
|
||||
let o_y = -2.0 * far * near / (far - near);
|
||||
|
||||
math::Mat4::new(
|
||||
s_x, o_x, 0.0, 0.0, 0.0, s_y, 0.0, o_y, 0.0, o_z, s_z, 0.0, 0.0, 1.0,
|
||||
0.0, 0.0,
|
||||
)
|
||||
} else {
|
||||
math::Mat4::identity()
|
||||
}
|
||||
};
|
||||
|
||||
let shadow_all_mat: math::Mat4<f32> = w_p * shadow_view_mat;
|
||||
// coordinates are transformed from world space (right-handed)
|
||||
// to post-warp light space (left-handed), then homogenized.
|
||||
let math::Aabb::<f32> {
|
||||
min:
|
||||
math::Vec3 {
|
||||
x: xmin,
|
||||
y: ymin,
|
||||
z: zmin,
|
||||
},
|
||||
max:
|
||||
math::Vec3 {
|
||||
x: xmax,
|
||||
y: ymax,
|
||||
z: zmax,
|
||||
},
|
||||
} = math::fit_psr(
|
||||
shadow_all_mat,
|
||||
visible_light_volume.iter().copied(),
|
||||
math::Vec4::homogenized,
|
||||
);
|
||||
let s_x = 2.0 / (xmax - xmin);
|
||||
let s_y = 2.0 / (ymax - ymin);
|
||||
let s_z = 1.0 / (zmax - zmin);
|
||||
let o_x = -(xmax + xmin) / (xmax - xmin);
|
||||
let o_y = -(ymax + ymin) / (ymax - ymin);
|
||||
let o_z = -zmin / (zmax - zmin);
|
||||
let directed_proj_mat = Mat4::new(
|
||||
s_x, 0.0, 0.0, o_x, 0.0, s_y, 0.0, o_y, 0.0, 0.0, s_z, o_z, 0.0, 0.0, 0.0, 1.0,
|
||||
);
|
||||
|
||||
let shadow_all_mat: Mat4<f32> =
|
||||
Mat4::from_col_arrays(shadow_all_mat.into_col_arrays());
|
||||
|
||||
let directed_texture_proj_mat = texture_mat * directed_proj_mat;
|
||||
let shadow_locals = ShadowLocals::new(
|
||||
directed_proj_mat * shadow_all_mat,
|
||||
directed_texture_proj_mat * shadow_all_mat,
|
||||
);
|
||||
|
||||
renderer.update_consts(&mut self.data.shadow_mats, &[shadow_locals]);
|
||||
|
||||
let rain_occlusion_locals = RainOcclusionLocals::new(
|
||||
directed_proj_mat * shadow_all_mat,
|
||||
directed_texture_proj_mat * shadow_all_mat,
|
||||
);
|
||||
renderer
|
||||
.update_consts(&mut self.data.rain_occlusion_mats, &[rain_occlusion_locals]);
|
||||
}
|
||||
directed_shadow_mats.push(light_view_mat);
|
||||
// This leaves us with five dummy slots, which we push as defaults.
|
||||
directed_shadow_mats
|
||||
|
@ -1104,7 +1104,6 @@ impl PlayState for SessionState {
|
||||
num_particles: self.scene.particle_mgr().particle_count() as u32,
|
||||
num_particles_visible: self.scene.particle_mgr().particle_count_visible()
|
||||
as u32,
|
||||
weather: client.current_weather(),
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user