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
|
#ifdef EXPERIMENTAL_RAIN
|
||||||
vec3 old_color = color.rgb;
|
vec3 old_color = color.rgb;
|
||||||
|
|
||||||
|
// If this value is changed also change it in voxygen/src/scene/mod.rs
|
||||||
float fall_rate = 70.0;
|
float fall_rate = 70.0;
|
||||||
|
|
||||||
dir.xy += wind_vel * dir.z / fall_rate;
|
dir.xy += wind_vel * dir.z / fall_rate;
|
||||||
dir = normalize(dir);
|
dir = normalize(dir);
|
||||||
|
|
||||||
|
@ -17,15 +17,6 @@ uniform u_rain_occlusion {
|
|||||||
float rain_occlusion_at(in vec3 fragPos)
|
float rain_occlusion_at(in vec3 fragPos)
|
||||||
{
|
{
|
||||||
float bias = -0.2;
|
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);
|
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;
|
const float RAIN_CLOUD = 0.05;
|
||||||
|
|
||||||
float rain_density_at(vec2 pos) {
|
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);
|
//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_figures_visible: u32,
|
||||||
pub num_particles: u32,
|
pub num_particles: u32,
|
||||||
pub num_particles_visible: u32,
|
pub num_particles_visible: u32,
|
||||||
pub weather: Weather,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HudInfo {
|
pub struct HudInfo {
|
||||||
|
@ -722,13 +722,300 @@ impl Scene {
|
|||||||
Some(&self.terrain),
|
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 sun_dir = scene_data.get_sun_dir();
|
||||||
let is_daylight = sun_dir.z < 0.0;
|
let is_daylight = sun_dir.z < 0.0;
|
||||||
if renderer.pipeline_modes().shadow.is_map() && (is_daylight || !lights.is_empty()) {
|
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();
|
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
|
// NOTE: The aspect ratio is currently always 1 for our cube maps, since they
|
||||||
// are equal on all sides.
|
// are equal on all sides.
|
||||||
@ -737,289 +1024,17 @@ impl Scene {
|
|||||||
// and moon.
|
// and moon.
|
||||||
let directed_light_dir = math::Vec3::from(sun_dir);
|
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.
|
// We upload view matrices as well, to assist in linearizing vertex positions.
|
||||||
// (only for directional lights, so far).
|
// (only for directional lights, so far).
|
||||||
let mut directed_shadow_mats = Vec::with_capacity(6);
|
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);
|
let light_view_mat = math::Mat4::look_at_rh(look_at, look_at + directed_light_dir, up);
|
||||||
{
|
let (shadow_mat, texture_mat) = directed_mats(light_view_mat, directed_light_dir);
|
||||||
// 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),
|
|
||||||
);
|
|
||||||
|
|
||||||
v_p.z = 0.0;
|
let shadow_locals = ShadowLocals::new(shadow_mat, texture_mat);
|
||||||
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 * light_view_mat;
|
renderer.update_consts(&mut self.data.shadow_mats, &[shadow_locals]);
|
||||||
// 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;
|
|
||||||
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);
|
directed_shadow_mats.push(light_view_mat);
|
||||||
// This leaves us with five dummy slots, which we push as defaults.
|
// This leaves us with five dummy slots, which we push as defaults.
|
||||||
directed_shadow_mats
|
directed_shadow_mats
|
||||||
|
@ -1104,7 +1104,6 @@ impl PlayState for SessionState {
|
|||||||
num_particles: self.scene.particle_mgr().particle_count() as u32,
|
num_particles: self.scene.particle_mgr().particle_count() as u32,
|
||||||
num_particles_visible: self.scene.particle_mgr().particle_count_visible()
|
num_particles_visible: self.scene.particle_mgr().particle_count_visible()
|
||||||
as u32,
|
as u32,
|
||||||
weather: client.current_weather(),
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user