Fixing various things about shadows.

* Correcting optimal LISPSM parameter.
* Figure shadows are cast when they're not visible.
* Chunk shadows stay cast until you look away.
* Seamless cubemaps for point lights.
* Etc.
This commit is contained in:
Joshua Yanovski 2020-07-15 13:30:49 +02:00
parent 6c31e6b562
commit 2e2ab3dc1e
11 changed files with 673 additions and 289 deletions

View File

@ -59,7 +59,7 @@ float attenuation_strength(vec3 rpos) {
// }
vec3 light_at(vec3 wpos, vec3 wnorm) {
const float LIGHT_AMBIENCE = 0.025;
const float LIGHT_AMBIANCE = 0.025;
vec3 light = vec3(0);
@ -78,7 +78,7 @@ vec3 light_at(vec3 wpos, vec3 wnorm) {
// Multiply the vec3 only once
vec3 color = srgb_to_linear(L.light_col.rgb) * (strength * L.light_col.a);
light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIENCE);
light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIANCE);
}
return light;
}
@ -126,7 +126,7 @@ float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, ve
vec3 directed_light = vec3(0.0);
vec3 max_light = vec3(0.0);
const float LIGHT_AMBIENCE = 0.0;//0.015625;
const float LIGHT_AMBIANCE = 0.015625;
for (uint i = 0u; i < /*light_shadow_count.x*//*0u*/light_shadow_count.x/*32u*/; i ++) {
@ -183,10 +183,12 @@ float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, ve
#endif
vec3 direct_light = PI * color * strength * square_factor * light_reflection_factor(/*direct_norm_dir*/wnorm, /*cam_to_frag*/view_dir, direct_light_dir, k_d, k_s, alpha, voxel_norm, voxel_lighting);
float computed_shadow = ShadowCalculationPoint(i, -difference, wnorm, wpos/*, light_distance*/);
// directed_light += is_direct ? max(computed_shadow, /*LIGHT_AMBIENCE*/0.0) * direct_light * square_factor : vec3(0.0);
directed_light += is_direct ? mix(LIGHT_AMBIENCE, 1.0, computed_shadow) * direct_light * square_factor : vec3(0.0);
// ambient_light += is_direct ? vec3(0.0) : vec3(0.0); // direct_light * square_factor * LIGHT_AMBIENCE;
// ambient_light += is_direct ? direct_light * (1.0 - square_factor * LIGHT_AMBIENCE) : vec3(0.0);
// directed_light += is_direct ? max(computed_shadow, /*LIGHT_AMBIANCE*/0.0) * direct_light * square_factor : vec3(0.0);
// directed_light += is_direct ? mix(LIGHT_AMBIANCE, 1.0, computed_shadow) * direct_light * square_factor : vec3(0.0);
directed_light += (is_direct ? 1.0 : LIGHT_AMBIANCE) * max(computed_shadow, /*LIGHT_AMBIANCE*/0.0) * direct_light * square_factor;// : vec3(0.0);
// directed_light += mix(LIGHT_AMBIANCE, 1.0, computed_shadow) * direct_light * square_factor;
// ambient_light += is_direct ? vec3(0.0) : vec3(0.0); // direct_light * square_factor * LIGHT_AMBIANCE;
// ambient_light += is_direct ? direct_light * (1.0 - square_factor * LIGHT_AMBIANCE) : vec3(0.0);
vec3 cam_light_diff = light_pos - focus_pos.xyz;
float cam_distance_2 = dot(cam_light_diff, cam_light_diff);// + 0.0001;
@ -209,12 +211,12 @@ float lights_at(vec3 wpos, vec3 wnorm, vec3 /*cam_to_frag*/view_dir, vec3 mu, ve
// float both_strength = mix(cam_strength, strength, cam_distance_2 / sqrt(cam_distance_2 + distance_2));
max_light += /*max(1.0, cam_strength)*//*min(cam_strength, 1.0)*//*max*//*max(both_strength, 1.0) * *//*cam_strength*/computed_shadow * both_strength * square_factor * square_factor * PI * color;
// max_light += /*max(1.0, cam_strength)*//*min(cam_strength, 1.0)*//*max*/max(cam_strength, 1.0/*, strength*//*1.0*/) * square_factor * square_factor * PI * color;
// light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIENCE);
// light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIANCE);
// Compute emiittance.
// float ambient_sides = clamp(mix(0.15, 0.0, abs(dot(wnorm, light_dir)) * 10000.0), 0.0, 0.15);
// float ambient_sides = 0.0;// max(dot(wnorm, light_dir) - 0.15, 0.15);
// // float ambient_sides = 0.0;
// ambient_light += color * (ambient_sides + LIGHT_AMBIENCE);
// ambient_light += color * (ambient_sides + LIGHT_AMBIANCE);
}
// shadow = shadow_at(wpos, wnorm);

View File

@ -23,6 +23,7 @@ uniform samplerCubeShadow t_point_shadow_maps;
float VectorToDepth (vec3 Vec)
{
// return length(Vec) / screen_res.w;
vec3 AbsVec = abs(Vec);
float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));
// float LocalZcomp = length(Vec);
@ -60,14 +61,6 @@ float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /
return 1.0;
};
float shadow = 0.0;
float bias = 0.0;//0.003;//-0.003;//-0.005;//0.001;//-1.0;//-0.001;//0.001;//0.003;//-0.05;//-0.1;//0.0;//0.1
float viewDistance = length(cam_pos.xyz - fragPos);
vec3 firstDelta = vec3(0.0);///*min(viewDistance, 5.0) * *//**normalize(cam_pos - fragPos)*/fragNorm * 0.5;
fragToLight += firstDelta;
// viewDistance -= length(firstDelta);
fragPos -= firstDelta;
{
float currentDepth = VectorToDepth(fragToLight);// + bias;
@ -84,55 +77,68 @@ float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /
/* if (visibility < 1.0) {
return 0.0;
} */
return visibility == 1.0 ? 1.0 : 0.0;
}
int samples = 20;
// float lightDistance = length(fragToLight);
// float diskRadius = 0.00001;
// float diskRadius = 1.0;
// float diskRadius = 0.05;
float diskRadius = (1.0 + (/*viewDistance*/viewDistance / screen_res.w)) / 25.0;
// float diskRadius = lightDistance;
for(int i = 0; i < samples; ++i)
{
float currentDepth = VectorToDepth(fragToLight + sampleOffsetDirections[i] * diskRadius) + bias;
// float closestDepth = texture(depthMap, fragToLight).r;
// closestDepth *= far_plane; // Undo mapping [0;1]
/* if(currentDepth - bias > closestDepth)
shadow += 1.0;*/
float visibility = texture(t_point_shadow_maps, vec4(fragToLight, currentDepth)/*, -2.5*/);
shadow += visibility;
// float closestDepth = texture(t_shadow_maps, vec3(fragToLight)/*, -2.5*/).r;
// shadow += closestDepth > currentDepth ? 1.0 : 0.0;
}
shadow /= float(samples);
// shadow = shadow * shadow * (3.0 - 2.0 * shadow);
// use the light to fragment vector to sample from the depth map
// float bias = 0.0;///*0.05*/0.01;//0.05;// 0.05;
// float closestDepth = texture(t_shadow_maps, /*vec4*/vec3(fragToLight/*, (lightIndex + 1)*//* * 6*/)/*, 0.0*//*, 0.0*//*, bias*/).r;
// // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, lightIndex), bias);
// // // it is currently in linear range between [0,1]. Re-transform back to original value
// closestDepth = (closestDepth + 0.0) * screen_res.w; // far plane
// // // now test for shadows
// // // float shadow = /*currentDepth*/(screen_res.w - bias) > closestDepth ? 1.0 : 0.0;
// float shadow = currentDepth - bias < closestDepth ? 1.0 : 0.0;
// float visibility = textureProj(t_shadow_maps, vec4(fragToLight, lightIndex), bias);
// float visibility = texture(t_shadow_maps, vec4(fragToLight, lightIndex + 1), -(currentDepth/* + screen_res.z*/) / screen_res.w);// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/);
// currentDepth += bias;
// currentDepth = -1000.0 / (currentDepth + 10000.0);
// currentDepth /= screen_res.w;
// float currentDepth = VectorToDepth(fragToLight) + bias;
// float visibility = texture(t_shadow_maps, vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/);
// return visibility;
/* if (visibility == 1.0) {
return visibility;
} */
return visibility;
// return visibility == 1.0 ? 1.0 : 0.0;
return shadow;
}
// float shadow = 0.0;
// float bias = 0.0;//0.003;//-0.003;//-0.005;//0.001;//-1.0;//-0.001;//0.001;//0.003;//-0.05;//-0.1;//0.0;//0.1
// float viewDistance = length(cam_pos.xyz - fragPos);
// vec3 firstDelta = vec3(0.0);///*min(viewDistance, 5.0) * *//**normalize(cam_pos - fragPos)*/fragNorm * 0.5;
// fragToLight += firstDelta;
// // viewDistance -= length(firstDelta);
// fragPos -= firstDelta;
// int samples = 20;
// // float lightDistance = length(fragToLight);
// // float diskRadius = 0.00001;
// // float diskRadius = 1.0;
// // float diskRadius = 0.05;
// float diskRadius = 5.0 / screen_res.w;// (1.0 + (/*viewDistance*/viewDistance / screen_res.w)) / 25.0;
// // float diskRadius = lightDistance;
// for(int i = 0; i < samples; ++i)
// {
// float currentDepth = VectorToDepth(fragToLight + sampleOffsetDirections[i] * diskRadius) + bias;
// // float closestDepth = texture(depthMap, fragToLight).r;
// // closestDepth *= far_plane; // Undo mapping [0;1]
// /* if(currentDepth - bias > closestDepth)
// shadow += 1.0;*/
// float visibility = texture(t_point_shadow_maps, vec4(fragToLight, currentDepth)/*, -2.5*/);
// shadow += visibility;
// // float closestDepth = texture(t_shadow_maps, vec3(fragToLight)/*, -2.5*/).r;
// // shadow += closestDepth > currentDepth ? 1.0 : 0.0;
// }
// shadow /= float(samples);
// // shadow = shadow * shadow * (3.0 - 2.0 * shadow);
// // use the light to fragment vector to sample from the depth map
// // float bias = 0.0;///*0.05*/0.01;//0.05;// 0.05;
// // float closestDepth = texture(t_shadow_maps, /*vec4*/vec3(fragToLight/*, (lightIndex + 1)*//* * 6*/)/*, 0.0*//*, 0.0*//*, bias*/).r;
// // // // float closestDepth = texture(t_shadow_maps, vec4(fragToLight, lightIndex), bias);
// // // // it is currently in linear range between [0,1]. Re-transform back to original value
// // closestDepth = (closestDepth + 0.0) * screen_res.w; // far plane
// // // // now test for shadows
// // // // float shadow = /*currentDepth*/(screen_res.w - bias) > closestDepth ? 1.0 : 0.0;
// // float shadow = currentDepth - bias < closestDepth ? 1.0 : 0.0;
// // float visibility = textureProj(t_shadow_maps, vec4(fragToLight, lightIndex), bias);
// // float visibility = texture(t_shadow_maps, vec4(fragToLight, lightIndex + 1), -(currentDepth/* + screen_res.z*/) / screen_res.w);// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/);
// // currentDepth += bias;
// // currentDepth = -1000.0 / (currentDepth + 10000.0);
// // currentDepth /= screen_res.w;
// // float currentDepth = VectorToDepth(fragToLight) + bias;
// // float visibility = texture(t_shadow_maps, vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/);
// // return visibility == 1.0 ? 1.0 : 0.0;
// return shadow;
}
float ShadowCalculationDirected(in vec3 fragPos)//in vec4 /*light_pos[2]*/sun_pos, vec3 fragPos)
{
float bias = 0.000;//0.0005;//-0.0001;// 0.05 / (2.0 * view_distance.x);
float bias = 0.001;//0.0005;//-0.0001;// 0.05 / (2.0 * view_distance.x);
float diskRadius = 0.01;
const vec3 sampleOffsetDirections[20] = vec3[]
(
@ -150,16 +156,20 @@ float ShadowCalculationDirected(in vec3 fragPos)//in vec4 /*light_pos[2]*/sun_po
// sun_pos.z += sun_pos.w * bias;
ShadowLocals sun_shadow = shadowMats[0];
vec4 sun_pos = sun_shadow.texture_mat * vec4(fragPos, 1.0);
// sun_pos.z -= sun_pos.w * bias;
float visibility = textureProj(t_directed_shadow_maps, sun_pos);
/* float visibilityLeft = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, -diskRadius, 0.0), 1.0));
float visibilityRight = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, diskRadius, 0.0), 1.0)); */
// float nearVisibility = textureProj(t_directed_shadow_maps + vec3(0.001, sun_pos));
// float visibility = textureProj(t_directed_shadow_maps, vec4(fragPos.xy, /*lightIndex, */fragPos.z + bias, sun_pos.w));
return visibility;
// return visibility;
// return min(visibility, min(visibilityLeft, visibilityRight));
// return mix(visibility, 0.0, sun_pos.z < -1.0);
// return mix(mix(0.0, 1.0, visibility == 1.0), 1.0, sign(sun_pos.w) * sun_pos.z > /*1.0*/abs(sun_pos.w));
// return (visibility - 0.5) * (visibility - 0.5) * 2.0 * sign(visibility - 0.5) + 0.5;// visibility > 0.75 ? visibility : 0.0;// visibility > 0.9 ? 1.0 : 0.0;
return visibility;
// return visibility == 1.0 ? 1.0 : 0.0;
// return abs(fragPos.y - round(fragPos.y)) <= 0.1 || abs(fragPos.x - round(fragPos.x)) <= 0.1 ? ( visibility == 1.0 ? 1.0 : 0.0) : visibility;
/* if (visibility == 1.0) {
return 1.0;
} */
@ -167,32 +177,32 @@ float ShadowCalculationDirected(in vec3 fragPos)//in vec4 /*light_pos[2]*/sun_po
/* if (fragPos.z > 1.0) {
return 1.0;
} */
vec3 snapToZ = abs(fragPos - vec3(ivec3(fragPos))); // fract(abs(fragPos));
// snapToZ = min(snapToZ, 1.0 - snapToZ);
const float EDGE_DIST = 0.01;
snapToZ = mix(vec3(0.0), vec3(1.0), lessThanEqual(snapToZ, vec3(EDGE_DIST)));
// float snapToZDist = dot(snapToZ, snapToZ);
if (visibility <= 0.75 && /*fract(abs(fragPos.xy)), vec2(0.1)))*/ /*snapToZDist <= 0.25*//*all(lessThan(snapToZ, vec3(0.1)))(*/
snapToZ.x + snapToZ.y + snapToZ.z >= 2.0) {
return 0.0;
}
int samples = 20;
float shadow = 0.0;
// float bias = 0.0001;
// float viewDistance = length(cam_pos.xyz - fragPos);
// float diskRadius = 0.2 * (1.0 + (viewDistance / screen_res.w)) / 25.0;
// float diskRadius = 0.0003;//0.005;// / (2.0 * view_distance.x);//(1.0 + (viewDistance / screen_res.w)) / 25.0;
fragPos = sun_pos.xyz / sun_pos.w;
for(int i = 0; i < samples; ++i)
{
vec3 currentDepth = fragPos + vec3(sampleOffsetDirections[i].xyz) * diskRadius + bias;
visibility = texture(t_directed_shadow_maps, currentDepth);//vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/);
// visibility = texture(t_directed_shadow_maps, vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/);
shadow += visibility;
// mix(visibility, 1.0, visibility >= 0.5);
}
shadow /= float(samples);
return shadow;
// vec3 snapToZ = abs(fragPos - vec3(ivec3(fragPos))); // fract(abs(fragPos));
// // snapToZ = min(snapToZ, 1.0 - snapToZ);
// const float EDGE_DIST = 0.01;
// snapToZ = mix(vec3(0.0), vec3(1.0), lessThanEqual(snapToZ, vec3(EDGE_DIST)));
// // float snapToZDist = dot(snapToZ, snapToZ);
// if (visibility <= 0.75 && /*fract(abs(fragPos.xy)), vec2(0.1)))*/ /*snapToZDist <= 0.25*//*all(lessThan(snapToZ, vec3(0.1)))(*/
// snapToZ.x + snapToZ.y + snapToZ.z >= 2.0) {
// return 0.0;
// }
// int samples = 20;
// float shadow = 0.0;
// // float bias = 0.0001;
// // float viewDistance = length(cam_pos.xyz - fragPos);
// // float diskRadius = 0.2 * (1.0 + (viewDistance / screen_res.w)) / 25.0;
// // float diskRadius = 0.0003;//0.005;// / (2.0 * view_distance.x);//(1.0 + (viewDistance / screen_res.w)) / 25.0;
// fragPos = sun_pos.xyz / sun_pos.w;
// for(int i = 0; i < samples; ++i)
// {
// vec3 currentDepth = fragPos + vec3(sampleOffsetDirections[i].xyz) * diskRadius + bias;
// visibility = texture(t_directed_shadow_maps, currentDepth);//vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/);
// // visibility = texture(t_directed_shadow_maps, vec4(currentDepth.xy, lightIndex, currentDepth.z)/*, -2.5*/);
// shadow += visibility;
// // mix(visibility, 1.0, visibility >= 0.5);
// }
// shadow /= float(samples);
// return shadow;
}
#elif (SHADOW_MODE == SHADOW_MODE_NONE || SHADOW_MODE == SHADOW_MODE_CHEAP)
float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /*float currentDepth*/vec3 fragPos)

View File

@ -21,8 +21,8 @@
#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN
// // Currently, we only need globals for the far plane.
// #include <globals.glsl>
// Currently, we only need globals for the far plane.
#include <globals.glsl>
// // Currently, we only need lights for the light position
// #include <light.glsl>
@ -33,16 +33,17 @@ void main()
{
// Only need to do anything with point lights, since sun and moon should already have nonlinear
// distance.
/*if (FragLayer > 0) */{
// get distance between fragment and light source
// float lightDistance = length(FragPos - lights[((/*FragLayer*/1 - 1) & 31)].light_pos.xyz);
///*if (FragLayer > 0) */{
// // get distance between fragment and light source
// float lightDistance = length(FragPos);
// // float lightDistance = length(FragPos - lights[((/*FragLayer*/1 - 1) & 31)].light_pos.xyz);
// // map to [0;1] range by dividing by far_plane
// // // map to [0;1] range by dividing by far_plane
// lightDistance = lightDistance / screen_res.w;//FragPos.w;//screen_res.w;
// // write this as modified depth
// // lightDistance = -1000.0 / (lightDistance + 10000.0);
// // lightDistance /= screen_res.w;
// // // write this as modified depth
// // // lightDistance = -1000.0 / (lightDistance + 10000.0);
// // // lightDistance /= screen_res.w;
// gl_FragDepth = lightDistance;// / /*FragPos.w;*/screen_res.w;//-1000.0 / (lightDistance + 1000.0);//lightDistance
}
//}
}

View File

@ -30,6 +30,17 @@
// // Currently, we only need lights for the light position
// #include <light.glsl>
/* struct Light {
vec4 light_pos;
vec4 light_col;
// mat4 light_proj;
};
layout (std140)
uniform u_lights {
Light lights[31];
}; */
// Since our output primitive is a triangle strip, we have to render three vertices
// each.
#define VERTICES_PER_FACE 3
@ -236,8 +247,8 @@ void main() {
for(int i = 0; i < VERTICES_PER_FACE; ++i) // for each triangle vertex
{
// NOTE: See above, we don't make FragPos a uniform.
vec3 FragPos = gl_in[i].gl_Position.xyz;
// FragPos = gl_in[i].gl_Position.xyz;
vec3 fragPos = gl_in[i].gl_Position.xyz;
// FragPos = fragPos - (lights[((/*FragLayer*/layer - 1u) & 31u)].light_pos.xyz - focus_off.xyz);
// FragLayer = layer;
// float lightDistance = length(FragPos - lights[((layer - 1) & 31)].light_pos.xyz);
// lightDistance /= screen_res.w;
@ -250,7 +261,7 @@ void main() {
// int face = int(((floatBitsToUint(gl_Position.w) >> 29) & 0x7u) ^ 0x1u);
int layer_face = layer_base + face;
gl_Layer = face;//layer_face; // built-in variable that specifies to which face we render.
gl_Position = shadowMats[layer_face].shadowMatrices * vec4(FragPos, 1.0);
gl_Position = shadowMats[layer_face].shadowMatrices * vec4(fragPos, 1.0);
// gl_Position.z = -((gl_Position.z + screen_res.z) / (screen_res.w - screen_res.z)) * lightDistance;
// gl_Position.z = gl_Position.z / screen_res.w;
// gl_Position.z = gl_Position.z / gl_Position.w;

View File

@ -226,6 +226,7 @@ pub struct DebugInfo {
pub ori: Option<comp::Ori>,
pub num_chunks: u32,
pub num_visible_chunks: u32,
pub num_shadow_chunks: u32,
pub num_figures: u32,
pub num_figures_visible: u32,
}
@ -594,7 +595,7 @@ impl Hud {
&mut self,
client: &Client,
global_state: &GlobalState,
debug_info: DebugInfo,
debug_info: &Option<DebugInfo>,
dt: Duration,
info: HudInfo,
) -> Vec<Event> {
@ -602,10 +603,6 @@ impl Hud {
let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets();
// pulse time for pulsating elements
self.pulse = self.pulse + dt.as_secs_f32();
self.velocity = match debug_info.velocity {
Some(velocity) => velocity.0.magnitude(),
None => 0.0,
};
let version = format!(
"{}-{}",
@ -1239,7 +1236,11 @@ impl Hud {
}
// Display debug window.
if global_state.settings.gameplay.toggle_debug {
if let Some(debug_info) = debug_info {
self.velocity = match debug_info.velocity {
Some(velocity) => velocity.0.magnitude(),
None => 0.0,
};
// Alpha Version
Text::new(&version)
.top_left_with_margins_on(ui_widgets.window, 5.0, 5.0)
@ -1345,8 +1346,8 @@ impl Hud {
// Number of chunks
Text::new(&format!(
"Chunks: {} ({} visible)",
debug_info.num_chunks, debug_info.num_visible_chunks,
"Chunks: {} ({} visible) & {} (shadow)",
debug_info.num_chunks, debug_info.num_visible_chunks, debug_info.num_shadow_chunks,
))
.color(TEXT_COLOR)
.down_from(self.ids.entity_count, 5.0)
@ -2213,7 +2214,7 @@ impl Hud {
&mut self,
client: &Client,
global_state: &mut GlobalState,
debug_info: DebugInfo,
debug_info: &Option<DebugInfo>,
camera: &Camera,
dt: Duration,
info: HudInfo,

View File

@ -1,6 +1,6 @@
use super::{
super::{
ColLightFmt, ColLightInfo, Pipeline, RenderError, Renderer, ShadowDepthStencilFmt,
ColLightFmt, ColLightInfo, Light, Pipeline, RenderError, Renderer, ShadowDepthStencilFmt,
TerrainLocals, Texture,
},
figure, terrain, Globals,
@ -30,7 +30,7 @@ gfx_defines! {
locals: gfx::ConstantBuffer<TerrainLocals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
// lights: gfx::ConstantBuffer<Light> = "u_lights",
lights: gfx::ConstantBuffer<Light> = "u_lights",
// shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
// map: gfx::TextureSampler<[f32; 4]> = "t_map",
@ -42,7 +42,7 @@ gfx_defines! {
light_shadows: gfx::ConstantBuffer<Locals> = "u_light_shadows",
tgt_depth_stencil: gfx::DepthTarget<ShadowDepthStencilFmt> = gfx::state::Depth {
fun: gfx::state::Comparison::Less,
fun: gfx::state::Comparison::LessEqual,
write: true,
},
// tgt_depth_stencil: gfx::DepthTarget<ShadowDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,//,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))),
@ -67,7 +67,7 @@ gfx_defines! {
light_shadows: gfx::ConstantBuffer<Locals> = "u_light_shadows",
tgt_depth_stencil: gfx::DepthTarget<ShadowDepthStencilFmt> = gfx::state::Depth {
fun: gfx::state::Comparison::Less,
fun: gfx::state::Comparison::LessEqual,
write: true,
},
// tgt_depth_stencil: gfx::DepthTarget<ShadowDepthStencilFmt> = gfx::preset::depth::LESS_WRITE,//,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))),

View File

@ -1388,8 +1388,8 @@ impl Renderer {
globals: &Consts<Globals>,
terrain_locals: &Consts<terrain::Locals>,
locals: &Consts<shadow::Locals>,
/* lights: &Consts<Light>,
* shadows: &Consts<Shadow>,
lights: &Consts<Light>,
/* shadows: &Consts<Shadow>,
* map: &Texture<LodColorFmt>,
* horizon: &Texture<LodTextureFmt>, */
) {
@ -1419,7 +1419,7 @@ impl Renderer {
vbuf: model.vbuf.clone(),
locals: terrain_locals.buf.clone(),
globals: globals.buf.clone(),
// lights: lights.buf.clone(),
lights: lights.buf.clone(),
// shadows: shadows.buf.clone(),
// noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
// map: (map.srv.clone(), map.sampler.clone()),
@ -1443,8 +1443,8 @@ impl Renderer {
globals: &Consts<Globals>,
terrain_locals: &Consts<terrain::Locals>,
locals: &Consts<shadow::Locals>,
/* lights: &Consts<Light>,
* shadows: &Consts<Shadow>,
lights: &Consts<Light>,
/* shadows: &Consts<Shadow>,
* map: &Texture<LodColorFmt>,
* horizon: &Texture<LodTextureFmt>, */
) {
@ -1474,7 +1474,7 @@ impl Renderer {
vbuf: model.vbuf.clone(),
locals: terrain_locals.buf.clone(),
globals: globals.buf.clone(),
// lights: lights.buf.clone(),
lights: lights.buf.clone(),
// shadows: shadows.buf.clone(),
// noise: (self.noise_tex.srv.clone(), self.noise_tex.sampler.clone()),
// map: (map.srv.clone(), map.sampler.clone()),
@ -2089,8 +2089,8 @@ fn create_pipelines(
)
.unwrap(),
&include_ctx,
gfx::state::CullFace::Back,
None, //Some(gfx::state::Offset(2, 10)),
gfx::state::CullFace::Front,
Some(gfx::state::Offset(2, /* 10 */ 0)),
) {
Ok(pipe) => Some(pipe),
Err(err) => {
@ -2108,9 +2108,10 @@ fn create_pipelines(
&directed_shadow_frag,
&include_ctx,
gfx::state::CullFace::Back,
/* None, */
/* Some(gfx::state::Offset(4, 10)), */
None,
/* Some(gfx::state::Offset(4, 10)),
* Some(gfx::state::Offset(2, 10)), */
// Some(gfx::state::Offset(2, /*10*/1)),
) {
Ok(pipe) => Some(pipe),
Err(err) => {
@ -2131,9 +2132,10 @@ fn create_pipelines(
&directed_shadow_frag,
&include_ctx,
gfx::state::CullFace::Back,
None,
/* Some(gfx::state::Offset(4, 10)),
* Some(gfx::state::Offset(2, 10)), */
/* None, */
/* Some(gfx::state::Offset(4, 10)), */
/*Some(gfx::state::Offset(2, 1))*/None,
/* Some(gfx::state::Offset(2, 10)), */
) {
Ok(pipe) => Some(pipe),
Err(err) => {

View File

@ -12,8 +12,8 @@ use crate::{
RenderError, Renderer, Shadow, ShadowLocals, ShadowPipeline, Texture,
},
scene::{
camera::{Camera, CameraMode},
LodData, SceneData,
camera::{Camera, CameraMode, Dependents},
math, LodData, SceneData,
},
};
use anim::{
@ -221,56 +221,68 @@ impl FigureMgrStates {
fn count_visible(&self) -> usize {
self.character_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.quadruped_small_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.quadruped_medium_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.quadruped_low_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.bird_medium_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.critter_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.dragon_states
.iter()
.filter(|(_, c)| c.visible())
.count()
+ self.dragon_states.iter().filter(|(_, c)| c.visible).count()
+ self
.fish_medium_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.bird_small_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.fish_small_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.biped_large_states
.iter()
.filter(|(_, c)| c.visible)
.filter(|(_, c)| c.visible())
.count()
+ self
.golem_states
.iter()
.filter(|(_, c)| c.visible())
.count()
+ self
.object_states
.iter()
.filter(|(_, c)| c.visible())
.count()
+ self.golem_states.iter().filter(|(_, c)| c.visible).count()
+ self.object_states.iter().filter(|(_, c)| c.visible).count()
}
}
@ -404,6 +416,8 @@ impl FigureMgr {
&mut self,
renderer: &mut Renderer,
scene_data: &SceneData,
// Visible chunk data.
visible_psr_bounds: math::Aabr<f32>,
camera: &Camera,
) -> Aabb<f32> {
let state = scene_data.state;
@ -413,6 +427,81 @@ impl FigureMgr {
let view_distance = scene_data.view_distance;
let dt = state.get_delta_time();
let frustum = camera.frustum();
// Sun shadows--find the bounding box of the shadow map plane (i.e. the bounds
// 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.render_mode().shadow.is_map() && is_daylight;
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::up());
let focus_off = math::Vec3::from(camera.get_focus_pos().map(f32::trunc));
/* let visible_bounding_box = Aabb {
min: visible_bounding_box.min - focus_off,
max: visible_bounding_box.max - focus_off,
};
let visible_bounds_fine = math::Aabb::<f64> {
min: math::Vec3::from(visible_bounding_box.min.map(f64::from)),
max: math::Vec3::from(visible_bounding_box.max.map(f64::from)),
};
let inv_proj_view = math::Mat4::from_col_arrays(
(proj_mat * view_mat/* * Mat4::translation_3d(-focus_off)*/).into_col_arrays(),
)
.map(f64::from)
.inverted();
let visible_light_volume = math::calc_focused_light_volume_points(
inv_proj_view,
ray_direction.map(f64::from),
visible_bounds_fine,
1e-6,
)
.map(|v| v.map(|e| e as f32));
// Now that the work that requires high accuracy is done, switch from focus-relative
// to proper world space coordinates.
let visible_bounds = math::Aabr::from(math::fit_psr(
ray_mat,
/* super::aabb_to_points(visible_bounding_box).iter().copied() */
visible_light_volume,
|p| p, //math::Vec3::from(p), /* / p.w */
)); */
let ray_mat = ray_mat * math::Mat4::translation_3d(-focus_off);
let collides_with_aabr = |a: math::Aabr<f32>, b: math::Aabr<f32>| {
a.min.partial_cmple(&b.max).reduce_and() && a.max.partial_cmpge(&b.min).reduce_and()
};
// println!("Aabr: {:?}", visible_bounds);
move |pos: Pos, radius: f32| {
// Short circuit when there are no shadows to cast.
if !can_shadow_sun {
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,
};
// println!("center: {:?}, radius: {:?}", center, figure_box);
// Quick intersection test for membership in the PSC (potential shader caster)
// list.
collides_with_aabr(figure_box, visible_psr_bounds)
}
};
// Get player position.
let player_pos = ecs
.read_storage::<Pos>()
@ -467,6 +556,7 @@ impl FigureMgr {
// TODO: Investigate passing the velocity into the shader so we can at least
// interpolate motion
const MIN_PERFECT_RATE_DIST: f32 = 50.0;
if (i as u64 + tick)
% (1 + ((pos.0.distance_squared(camera.get_focus_pos()).powf(0.25)
- MIN_PERFECT_RATE_DIST.powf(0.5))
@ -477,6 +567,13 @@ impl FigureMgr {
continue;
}
// Check whether we could have been shadowing last frame.
let mut state = self.states.get_mut(body, &entity);
let can_shadow_prev = state
.as_mut()
.map(|state| state.can_shadow_sun())
.unwrap_or(false);
// Don't process figures outside the vd
let vd_frac = Vec2::from(pos.0 - player_pos)
.map2(TerrainChunk::RECT_SIZE, |d: f32, sz| {
@ -484,16 +581,18 @@ impl FigureMgr {
})
.magnitude()
/ view_distance as f32;
// Keep from re-adding/removing entities on the border of the vd
if vd_frac > 1.2 {
self.states.remove(body, &entity);
continue;
} else if vd_frac > 1.0 {
self.states
.get_mut(body, &entity)
.map(|state| state.visible = false);
state.as_mut().map(|state| state.visible = false);
// Keep processing if this might be a shadow caster.
if !can_shadow_prev {
continue;
}
}
// Don't display figures outside the frustum spectrum (this is important to do
// for any figure that potentially casts a shadow, since we use this
@ -502,22 +601,25 @@ impl FigureMgr {
// shadow correctly until their next update. For now, we treat this
// as an acceptable tradeoff.
let radius = scale.unwrap_or(&Scale(1.0)).0 * 2.0;
let (in_frustum, lpindex) = if let Some(mut meta) = self.states.get_mut(body, &entity) {
let (in_frustum, lpindex) = if let Some(mut meta) = state {
let (in_frustum, lpindex) = BoundingSphere::new(pos.0.into_array(), radius)
.coherent_test_against_frustum(frustum, meta.lpindex);
meta.visible = in_frustum;
meta.lpindex = lpindex;
(in_frustum, lpindex)
} else {
(true, 0)
};
if in_frustum {
// Update visible bounds.
visible_aabb.expand_to_contain(Aabb {
min: pos.0 - radius,
max: pos.0 + radius,
});
} else {
// Check whether we can shadow.
meta.can_shadow_sun = can_shadow_sun(pos, radius);
}
(in_frustum, lpindex)
} else {
(true, 0)
};
// Change in health as color!
let col = stats
@ -1839,7 +1941,7 @@ impl FigureMgr {
false,
pos.0,
figure_lod_render_distance,
|state| state.visible,
|state| state.can_shadow_sun(),
) {
renderer.render_figure_shadow_directed(
model,
@ -1900,7 +2002,7 @@ impl FigureMgr {
false,
pos.0,
figure_lod_render_distance,
|state| state.visible,
|state| state.visible(),
) {
renderer.render_figure(
model,
@ -1964,7 +2066,7 @@ impl FigureMgr {
true,
pos.0,
figure_lod_render_distance,
|state| state.visible,
|state| state.visible(),
) {
renderer.render_player(
model,
@ -2160,7 +2262,10 @@ impl FigureMgr {
.0,
)
}),
Body::BirdMedium(_) => bird_medium_states.get(&entity).map(move |state| {
Body::BirdMedium(_) => bird_medium_states
.get(&entity)
.filter(|state| filter_state(&*state))
.map(move |state| {
(
state.locals(),
state.bone_consts(),
@ -2177,7 +2282,10 @@ impl FigureMgr {
.0,
)
}),
Body::FishMedium(_) => fish_medium_states.get(&entity).map(move |state| {
Body::FishMedium(_) => fish_medium_states
.get(&entity)
.filter(|state| filter_state(&*state))
.map(move |state| {
(
state.locals(),
state.bone_consts(),
@ -2490,11 +2598,21 @@ pub struct FigureStateMeta {
state_time: f64,
last_ori: Vec3<f32>,
lpindex: u8,
can_shadow_sun: bool,
visible: bool,
last_pos: Option<Vec3<f32>>,
avg_vel: Vec3<f32>,
}
impl FigureStateMeta {
pub fn visible(&self) -> bool { self.visible }
pub fn can_shadow_sun(&self) -> bool {
// Either visible, or explicitly a shadow caster.
self.visible || self.can_shadow_sun
}
}
pub struct FigureState<S> {
meta: FigureStateMeta,
skeleton: S,
@ -2525,6 +2643,7 @@ impl<S: Skeleton> FigureState<S> {
last_ori: Vec3::zero(),
lpindex: 0,
visible: false,
can_shadow_sun: false,
last_pos: None,
avg_vel: Vec3::zero(),
},

View File

@ -41,8 +41,8 @@ const NUM_DIRECTED_LIGHTS: usize = 1;
const LIGHT_DIST_RADIUS: f32 = 64.0; // The distance beyond which lights may not emit light from their origin
const SHADOW_DIST_RADIUS: f32 = 8.0;
const SHADOW_MAX_DIST: f32 = 96.0; // The distance beyond which shadows may not be visible
/* /// The minimum sin γ we will use before switching to uniform mapping.
const EPSILON_GAMMA: f64 = 0.25; */
/// The minimum sin γ we will use before switching to uniform mapping.
const EPSILON_UPSILON: f64 = -1.0;
// const NEAR_PLANE: f32 = 0.5;
// const FAR_PLANE: f32 = 100000.0;
@ -178,15 +178,37 @@ fn compute_scalar_fov<F: Float>(_near_plane: F, fov: F, aspect: F) -> F {
/// -1 + (η_b + 1)(1 + cos(90 (γ - γ_a)/(γ_b - γ_a))) γ_a ≤ γ < γ_b
/// η_b + (η_c - η_b) sin(90 (γ - γ_b)/(γ_c - γ_b)) γ_b ≤ γ < γ_c
/// η_c γ_c ≤ γ
///
/// NOTE: Equation's described behavior is *wrong!* I have pieced together a
/// slightly different function that seems to more closely satisfy the author's
/// intent:
///
/// η =
/// -1 γ < γ_a
/// -1 + (η_b + 1) (γ - γ_a)/(γ_b - γ_a) γ_a ≤ γ < γ_b
/// η_b + (η_c - η_b) sin(90 (γ - γ_b)/(γ_c - γ_b)) γ_b ≤ γ < γ_c
/// η_c γ_c ≤ γ
///
/// There are other alternatives that may have more desirable properties, such
/// as:
///
/// η =
/// -1 γ < γ_a
/// -1 + (η_b + 1)(1 - cos(90 (γ - γ_a)/(γ_b - γ_a))) γ_a ≤ γ < γ_b
/// η_b + (η_c - η_b) sin(90 (γ - γ_b)/(γ_c - γ_b)) γ_b ≤ γ < γ_c
/// η_c γ_c ≤ γ
fn compute_warping_parameter<F: Float + FloatConst>(
gamma: F,
(gamma_a, gamma_b, gamma_c): (F, F, F),
(eta_b, eta_c): (F, F),
) -> F {
if gamma < gamma_a {
F::zero()
-F::one()
/* F::zero() */
} else if gamma_a <= gamma && gamma < gamma_b {
-F::one() + (eta_b + F::one()) * (F::one() + (F::FRAC_PI_2() * (gamma - gamma_a) / (gamma_b - gamma_a)).cos())
/* -F::one() + (eta_b + F::one()) * (F::one() + (F::FRAC_PI_2() * (gamma - gamma_a) / (gamma_b - gamma_a)).cos()) */
-F::one() + (eta_b + F::one()) * (F::one() - (F::FRAC_PI_2() * (gamma - gamma_a) / (gamma_b - gamma_a)).cos())
// -F::one() + (eta_b + F::one()) * ((gamma - gamma_a) / (gamma_b - gamma_a))
} else if gamma_b <= gamma && gamma < gamma_c {
eta_b + (eta_c - eta_b) * (F::FRAC_PI_2() * (gamma - gamma_b) / (gamma_c - gamma_b)).sin()
} else {
@ -227,7 +249,7 @@ fn compute_warping_parameter_perspective<F: Float + FloatConst>(
theta,
theta + (three / ten) * (F::FRAC_PI_2() - theta),
),
(-two / ten, F::zero()),
(-two/*F::one()*/ / ten, F::zero()),
)
}
@ -546,7 +568,11 @@ impl Scene {
self.lod.maintain(renderer, time_of_day);
// Maintain the terrain.
let (_scene_bounds, visible_bounds, _psc_bounds) = self.terrain.maintain(
let (
/* _scene_bounds, visible_bounds, _psc_bounds */ visible_bounds,
visible_light_volume,
visible_psr_bounds,
) = self.terrain.maintain(
renderer,
&scene_data,
focus_pos,
@ -556,7 +582,9 @@ impl Scene {
);
// Maintain the figures.
let _figure_bounds = self.figure_mgr.maintain(renderer, scene_data, &self.camera);
let _figure_bounds =
self.figure_mgr
.maintain(renderer, scene_data, visible_psr_bounds, &self.camera);
let sun_dir = scene_data.get_sun_dir();
let is_daylight = sun_dir.z < 0.0/*0.6*/;
@ -570,7 +598,7 @@ impl Scene {
}; */
// let focus_frac = focus_pos.map(|e| e.fract());
let visible_bounds = math::Aabb::<f32> {
/* let visible_bounds = math::Aabb::<f32> {
min: math::Vec3::from(visible_bounds.min - focus_off),
max: math::Vec3::from(visible_bounds.max - focus_off),
};
@ -598,7 +626,7 @@ impl Scene {
(proj_mat * view_mat/* * Mat4::translation_3d(-focus_off)*/).into_col_arrays(),
)
.map(f64::from)
.inverted();
.inverted(); */
let fov = self.camera.get_fov();
let aspect_ratio = self.camera.get_aspect_ratio();
@ -620,11 +648,11 @@ impl Scene {
.collect::<Vec<_>>();
// println!("light_volume: {:?}", light_volume); */
// let visible_light_volume = light_volume.clone();
let visible_light_volume = math::calc_focused_light_volume_points(inv_proj_view, directed_light_dir.map(f64::from), visible_bounds_fine, 1e-6)
/* let visible_light_volume = math::calc_focused_light_volume_points(inv_proj_view, directed_light_dir.map(f64::from), visible_bounds_fine, 1e-6)
// .map(|e| e - focus_off)
// NOTE: Hopefully not out of bounds.
.map(|v| v.map(|e| e as f32))
.collect::<Vec<_>>();
.collect::<Vec<_>>(); */
// println!("visible_light_volume: {:?}", visible_light_volume);
// let bounds0 = fit_psr(Mat4::identity()/* * inverse_visible*/,
// light_volume.iter().copied(), |p| Vec3::from(p) / p.w);
@ -695,19 +723,20 @@ impl Scene {
// let look_at = bounds0.center();//Vec3::zero();//
// scene_bounds.center();//Vec3::zero(); let look_at =
// bounds0.center();
let look_at = math::Vec3::from(cam_pos); // /*Vec3::zero()*/scene_bounds.center()/*cam_pos*/;// - focus_off;// focus_off;
let look_at = /*Vec3::zero()*/math::Vec3::from(cam_pos); // /*Vec3::zero()*/scene_bounds.center()/*cam_pos*/;// - focus_off;// focus_off;
let _light_scale = 1.5 * /*(directed_near + directed_far) / 2.0*/radius;
// 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: Vec3<f32> = light_volume/*visible_light_volume*/.iter().map(|p|
// p - cam_pos).sum();
// let new_dir: math::Vec3::<_> =
// /*light_volume*/visible_light_volume.iter().copied().map(math::Vec3::from).
// map(|p| p - look_at).sum();
let new_dir = new_dir.normalized();
/* let dot_prod = f64::from(directed_light_dir.dot(new_dir));
let sin_gamma = (1.0 - dot_prod * dot_prod).sqrt();
/* let cos_gamma = f64::from(directed_light_dir.dot(new_dir));
let sin_gamma = (1.0 - cos_gamma * cos_gamma).sqrt();
// let sin_gamma = 0.0;
let new_dir = if /*sin_gamma > EPISLON_GAMMA*/factor != -1.0 {
let new_dir = if /*sin_gamma > EPISLON_GAMMA*/factor > EPSILON_UPSILON {
new_dir
} else {
// For uniform mapping, align shadow map t axis with viewer's y axis to maximize
@ -746,9 +775,12 @@ impl Scene {
.scaled_3d(Vec3::new(proj_mat[(0, 0)], proj_mat[(1, 1)], 1.0));
let focus_off = focus_pos.map(|e| e.trunc()); */
let z_n = 1.0; //f64::from(camera::NEAR_PLANE);
let _z_f = f64::from(camera::FAR_PLANE);
let _scalar_fov = f64::from(fov / 2.0); // compute_scalar_fov(z_n, f64::from(fov), f64::from(aspect_ratio));
shadow_mats.extend(directed_shadow_mats.iter().map(move |&light_view_mat| {
let z_f = f64::from(camera::FAR_PLANE);
let scalar_fov = /*f64::from(fov / 2.0)*/compute_scalar_fov(z_n, f64::from(fov), f64::from(aspect_ratio));
shadow_mats.extend(directed_shadow_mats.iter().enumerate().map(move |(idx, &light_view_mat)| {
if idx >= NUM_DIRECTED_LIGHTS {
return ShadowLocals::new(Mat4::identity(), Mat4::identity());
}
/* let visible_light_volume = {
let light_view_mat = light_view_mat.map(f64::from);
// (See http://www.songho.ca/opengl/gl_normaltransform.html)
@ -873,13 +905,22 @@ impl Scene {
//
let mut e_p: Vec3<f32> = Vec3::zero();
v_p.z = 0.0; */
let mut v_p = math::Vec3::from(light_view_mat * math::Vec4::from_direction(new_dir));
v_p.normalize();
// let dot_prod = f64::from(v_p.z);
let dot_prod = new_dir.map(f64::from).dot(directed_light_dir.map(f64::from));
let sin_gamma = (1.0 - dot_prod * dot_prod).sqrt();
let gamma = sin_gamma.asin();
let factor = compute_warping_parameter_perspective(gamma, f64::from(camera::NEAR_PLANE), f64::from(fov), f64::from(aspect_ratio));
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 = f64::from(v_p.z);
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()/*cos_gamma.acos()*/;
let bounds1 = math::fit_psr(view_mat, visible_light_volume.iter().copied(), math::Vec4::homogenized);
let n_e = f64::from(-bounds1.max.z);
// let f_e = f64::from(-bounds1.min.z);
// let fov = 2.0 * aspect_ratio * (fov / 2.0).tan();
let factor = compute_warping_parameter_perspective(gamma, /*f64::from(camera::NEAR_PLANE)*/n_e, f64::from(/*fov*//*50.0.to_radians()*/fov/* / 2.0*/), f64::from(aspect_ratio));
/* if v_p.z > 0.5 {
-1.0
} else {
0.0
}; */
/* let factor = if factor > 0.0 {
-1.0
} else {
@ -888,7 +929,7 @@ impl Scene {
v_p.z = 0.0;
v_p.normalize();
let l_r: math::Mat4<f32> = if /*v_p.magnitude_squared() > 1e-3*//*sin_gamma > EPISLON_GAMMA*/factor != -1.0 {
let l_r: math::Mat4<f32> = if /*v_p.magnitude_squared() > 1e-3*//*sin_gamma > EPISLON_GAMMA*/factor > EPSILON_UPSILON {
math::Mat4::look_at_rh(math::Vec3::zero(), math::Vec3::forward_rh(), v_p)
} else {
math::Mat4::identity()
@ -902,8 +943,8 @@ impl Scene {
// let l_r: Mat4<f32> = Mat4::look_at_rh(/*Vec3::from(e_p) - Vec3::from(v_p)*/Vec3::zero(), /*Vec3::from(e_p)*/-Vec3::forward_rh(), /*Vec3::up()*/-Vec3::from(v_p));
// let l_r: Mat4<f32> = Mat4::look_at_rh(/*Vec3::from(e_p) - Vec3::from(v_p)*/Vec3::back_rh(), /*Vec3::from(e_p)*/Vec3::zero(), /*Vec3::up()*/Vec3::from(v_p));
// let l_r: Mat4<f32> = Mat4::identity();
let bounds0 = math::fit_psr(light_view_mat, visible_light_volume.iter().copied(), /*|p| math::Vec3::from(p) / p.w*/math::Vec4::homogenized);
let directed_proj_mat = math::Mat4::orthographic_rh_no(FrustumPlanes {
/* let bounds0 = math::fit_psr(light_view_mat, visible_light_volume.iter().copied(), /*|p| math::Vec3::from(p) / p.w*/math::Vec4::homogenized); */
let directed_proj_mat = /*math::Mat4::orthographic_rh_no(FrustumPlanes {
// TODO: Consider adjusting resolution based on view distance.
left: bounds0.min.x,
right: bounds0.max.x,
@ -911,20 +952,71 @@ impl Scene {
top: bounds0.max.y,
near: bounds0.min.z,
far: bounds0.max.z,
})/* /Mat4::identity() */;
})*//* /Mat4::identity() */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;
// let bounds1 = fit_psr(light_all_mat/* * inverse_visible*/, light_volume.iter().copied(), |p| Vec3::from(p) / p.w);
let bounds0 = math::fit_psr(/*l_r*/light_all_mat/* * inverse_visible*/, visible_light_volume.iter().copied(), /*|p| math::Vec3::from(p) / p.w*/math::Vec4::homogenized);
// Vague idea: project z_n from the camera view to the light view (where it's
// tilted by γ).
let (z_0, z_1) = {
let p_z = bounds1.max.z;
let p_y = bounds0.min.y;
let p_x = bounds0.center().x;
// println!("p_x (light near plane, s-axis) = {:?}, p_y (light xy plane, t-axis) = {:?}, p_z (near plane, z-axis) = {:?}", p_x, p_y, p_z);
let view_inv = view_mat.inverted();
let light_all_inv = light_all_mat.inverted();
let view_point = view_inv * math::Vec4::new(0.0, 0.0, p_z, 1.0);
let view_plane = view_inv * math::Vec4::from_direction(math::Vec3::unit_z());
let light_point = light_all_inv * math::Vec4::new(0.0, p_y, 0.0, 1.0);
let light_plane = light_all_inv * math::Vec4::from_direction(math::Vec3::unit_y());
let shadow_point = light_all_inv * math::Vec4::new(p_x, 0.0, 0.0, 1.0);
let shadow_plane = light_all_inv * math::Vec4::from_direction(math::Vec3::unit_x());
let solve_p0 = math::Mat4::new(
view_plane.x, view_plane.y, view_plane.z, -view_plane.dot(view_point),
light_plane.x, light_plane.y, light_plane.z, -light_plane.dot(light_point),
shadow_plane.x, shadow_plane.y, shadow_plane.z, -shadow_plane.dot(shadow_point),
0.0, 0.0, 0.0, 1.0,
);
let w_p_arr = solve_p0.cols.iter().map(|e| (e.x, e.y, e.z, e.w)).collect::<Vec<_>>();
// println!("mat4 solve_p0 = mat4(vec4{:?}, vec4{:?}, vec4{:?}, vec4{:?});", w_p_arr[0], w_p_arr[1], w_p_arr[2], w_p_arr[3]);
let p0_world = solve_p0.inverted() * math::Vec4::unit_w();
let p0 = light_all_mat * p0_world;
let mut p1 = p0;
p1.y = bounds0.max.y;
// println!("p0 = {:?}, p1 = {:?}", p0, p1);
let view_from_light_mat = view_mat * light_all_inv;
let z0 = view_from_light_mat * p0;
let z1 = view_from_light_mat * p1;
// println!("z0 = {:?}, z1 = {:?}", z0, z1);
(f64::from(z0.z), f64::from(z1.z))
};
// let bounds1 = fit_psr(light_all_mat/* * inverse_visible*/, aabb_to_points(visible_bounds).iter().copied(), |p| Vec3::from(p) / p.w);
// let mut light_focus_pos: Vec3<f32> = Vec3::from(light_all_mat * Vec4::from_point(focus_pos.map(f32::fract)));
let mut light_focus_pos: math::Vec3<f32> = math::Vec3::zero();//bounds0.center();// l_r * directed_proj_mat * light_view_mat * Vec4::from_point(focus_pos.map(|e| e.fract()));
let mut light_focus_pos: math::Vec3<f32> = math::Vec3::zero()/*bounds0.center()*/;// l_r * directed_proj_mat * light_view_mat * Vec4::from_point(focus_pos.map(|e| e.fract()));
light_focus_pos.x = bounds0.center().x;
light_focus_pos.y = bounds0.min.y/*z_0 as f32*/;
light_focus_pos.z = bounds0.center().z;
// let mut light_focus_pos: Vec3<f32> = bounds0.center();// l_r * directed_proj_mat * light_view_mat * Vec4::from_point(focus_pos.map(|e| e.fract()));
// println!("cam_pos: {:?}, focus_pos: {:?}, light_focus_pos: {:?}, v_p: {:?} bounds: {:?}, l_r: {:?}, light_view_mat: {:?}, light_all_mat: {:?}", cam_pos, focus_pos - focus_off, light_focus_pos, v_p, /*bounds1*/bounds0, l_r, light_view_mat, light_all_mat);
// let w_v = Mat4::translation_3d(-Vec3::new(xmax + xmin, ymax + ymin, /*zmax + zmin*/0.0) / 2.0);
// let dot_prod = /*new_dir*//*up_dir*/view_dir.map(f64::from).dot(directed_light_dir.map(f64::from));
// let sin_gamma = (1.0 - dot_prod * dot_prod).sqrt();//.clamped(1e-1, 1.0);
// let cos_gamma = /*new_dir*//*up_dir*/view_dir.map(f64::from).dot(directed_light_dir.map(f64::from));
// let sin_gamma = (1.0 - cos_gamma * cos_gamma).sqrt();//.clamped(1e-1, 1.0);
// let sin_gamma = 0.0;
// let factor = -1.0;//1.0 / sin_gamma;
// println!("Warp factor for γ (sin γ = {:?}, γ = {:?}, near_plane = {:?}, fov = {:?}, scalar fov = {:?}, aspect ratio = {:?}): η = {:?}", sin_gamma, gamma.to_degrees(), camera::NEAR_PLANE, fov.to_degrees(), scalar_fov.to_degrees(), aspect_ratio, factor);
@ -938,13 +1030,53 @@ impl Scene {
// let z_f = z_n + d * camera::FAR_PLANE/* / scalar_fov.cos()*/;
// let z_0 = f64::from(bounds0.min.y);
// Vague idea: project z_n from the camera view to the light view (where it's
// tilted by γ).
let z_0 = z_n;// / sin_gamma;// / sin_gamma;
/* let v_p_orig: math::Vec3::<f32> = math::Vec3::from(light_all_mat * math::Vec4::from_direction(new_dir));
let n_e: f64 = /*bounds0.y.min() * scalar_fov.cos() *//*bounds0.min.y / scalar_fov.cos();*/f64::from(camera::NEAR_PLANE) * f64::from(v_p_orig.y);
let f_e = /*bounds0.size().y / scalar_fov.cos();*//*f64::from(camera::FAR_PLANE);*//*f64::from(camera::FAR_PLANE) * v_p_orig*/
d
/*d * scalar_fov.cos()*/;
// See Lloyd's thesis, section 5.2 (p. 104-105).
let w_e = 2.0 * n_e * scalar_fov.tan();
let w_n = w_e;
// let w_f = w_n * f_e / n_e;
let w_s = (f_e - n_e) / scalar_fov.cos();
let w_n_ = w_n * gamma.cos();
let w_s_1_ = w_s * (1.0 - (scalar_fov - gamma).cos());
let w_s_2_ = w_s * (scalar_fov - gamma).sin();
/* let w_l_y = w_n_ + w_s_2_ + if gamma < scalar_fov {
w_s_1_
} else {
0.0
}; */
let v_s_1 = if gamma < scalar_fov {
w_s_1_ / w_l_y
} else {
0.0
};
let v_s_2 = v_s_1 + w_n_ / w_l_y;
let d_e = |v: f64| n_e + (f_e - n_e) *
(w_l_y * (v_s_1 - v) / w_s_1_)
.max(0.0)
.max(w_l_y * (1.0 - v) / w_s_2_); */
/* let w_l_x = (n_ + w_l_y) * if gamma < scalar_fov {
w_f / n_
} else {
w_f / (n_ + w_s_1_)
}; */
/* let z_0 = /*z_n*//*z_n*/n_e/* / sin_gamma*/;// / sin_gamma;// / sin_gamma;
// let z_1 = z_0 + d;
// Vague idea: project d from the light view back to the camera view (undoing the
// tilt by γ).
let z_1 = /*z_n*/z_0 + d * sin_gamma;
let z_1 = /*z_n*/z_0/*n_e*/ + d * /*sin_gamma*/f64::from(v_p_orig.y)/* * sin_gamma*/;
// 1/φ' (zn + √(zn(zn + (f - n)φ')))
// (f-n)/φ' (zn + √(zn(zn + (f - n)φ')))
// zn/φ' (1 + zn√(1 + (f - n)φ' / zn)) */
let w_l_y = /* z_f - z_n */d;/*/*f64::from(camera::FAR_PLANE - camera::NEAR_PLANE)*//*(z_f - z_n)*/d * scalar_fov.cos();*/
// let z_f = z_n + d;
// let near_dist = directed_near;
@ -962,6 +1094,10 @@ impl Scene {
// Standard shadow map to LiSPSM
(1.0 + alpha_sqrt - factor * (alpha - 1.0)) / ((alpha - 1.0) * (factor + 1.0))
// 1+sqrt(z_f/z_n)/((z_f/z_n - 1)*2)
//
// η = 0:
// (1 + √(z₁/z₀)) / (z₁ / z₀ - 1)
// (z₀ + √(z₀z₁)) / (z₁ - z₀)
} else {
// LiSPSM to PSM
((alpha_sqrt - 1.0) * (factor * alpha_sqrt + 1.0)).recip()
@ -969,18 +1105,40 @@ impl Scene {
// = 1 / ((√α - 1)(1))
// = 1 / (√α - 1)
// = (1 + √α) / (α - 1)
// = (a + √(z_f/z_n)) / (z_f/z_n - 1)
// = (1 + √(z₁/z₀)) / (z₁ / z₀ - 1)
};
// let factor = -1.0;
// (f - n) * (1 + √(z₁/z₀) - sin γ * (z₁/z₀ - 1)) / ((z₁/z₀ - 1) * (sin γ + 1))
//
// (f - n) * (1 + √(z_f/z_n) - (-1 / sin γ) * (z_f/z_n - 1)) / ((z_f/z_n - 1) * ((-1 / sin γ) + 1))
// (f - n) * (1 + √(z_f/z_n) + 1 / sin γ * (z_f/z_n - 1)) / ((z_f/z_n - 1) * (1 - 1 / sin γ))
// (f - n) * (1 + √(z_f/z_n) + 1 / sin γ * (z_f/z_n - 1)) / ((1 / sin γ)((z_f/z_n - 1) * (sin γ - 1)))
// (f - n)sin γ * (1 + √(z_f/z_n) + 1 / sin γ * (z_f/z_n - 1)) / ((z_f/z_n - 1) * (sin γ - 1))
// (f - n)sin γ / sin γ * (sin γ + √(z_f/z_n)sin γ + (z_f/z_n - 1)) / ((z_f/z_n - 1) * (sin γ - 1))
// (f - n) * (sin γ + √(z_f/z_n)sin γ + (z_f/z_n - 1)) / ((z_f/z_n - 1) * (sin γ - 1))
// (f - n) * (sin γ + √(z_f/z_n)sin γ + (z_f/z_n - 1)) / ((1 / z_n)(z_f - z_n) * (sin γ - 1))
// (f - n)z_n * (sin γ + √(z_f/z_n)sin γ + (z_f/z_n - 1)) / ((z_f - z_n) * (sin γ - 1))
// (f - n)z_n / z_n * (z_n sin γ + √(z_f z_n)sin γ + (z_f - z_n)) / ((z_f - z_n) * (sin γ - 1))
// (f - n) (z_n sin γ + √(z_f z_n)sin γ + (z_f - z_n)) / ((z_f - z_n) * (sin γ - 1))
//
// (f - n) * (1 + √(f_e/n_e) - F * (f_e/n_e - 1)) / ((f_e/n_e - 1) * (F + 1))
// (f - n) * n_e / n_e (n_e + √(n_e * f_e) - F * (f_e - n_e)) / ((f_e - n_e) * (F + 1))
// (f - n) (n_e + √(n_e * f_e) - F * (f_e - n_e)) / ((f_e - n_e) * (F + 1))
//
// (f - n) (n_e + √(n_e * f_e) - (sin γ - 1) * (f_e - n_e)) / ((f_e - n_e) * ((sin γ - 1) + 1))
// (f - n) (n_e + √(n_e * f_e) - (f_e - n_e) sin γ + f_e - n_e) / ((f_e - n_e) * sin γ)
// (f - n) (√(n_e * f_e) - (f_e - n_e) sin γ + f_e) / ((f_e - n_e) * sin γ)
// Equation 5.14 - 5.16
// let directed_near_normal = 1.0 / d * (z_0 + (z_0 * z_1).sqrt());
// let directed_near = w_l_y / d * (z_0 + (z_0 * z_1).sqrt());
/* let directed_near = directed_near_normal as f32;
let directed_far = (directed_near_normal + d) as f32; */
let directed_near = (w_l_y * directed_near_normal).abs() as f32;
let directed_far = (w_l_y * (directed_near_normal + 1.0)).abs() as f32;
let (directed_near, directed_far) = (directed_near.min(directed_far), directed_near.max(directed_far));
let y_ = |v: f64| w_l_y * (v + directed_near_normal).abs();
let directed_near = y_(0.0) as f32;// (w_l_y * directed_near_normal).abs() as f32;
let directed_far = y_(1.0) as f32;// (w_l_y * (directed_near_normal + 1.0)).abs() as f32;
/* let directed_far = (w_l_y * (directed_near_normal + 1.0)).abs() as f32;
let (directed_near, directed_far) = (directed_near.min(directed_far), directed_near.max(directed_far)); */
// let directed_near = w_l_y / d * (z_0 + (z_0 * z_1).sqrt());
// println!("θ = {:?} η = {:?} z_n = {:?} z_f = {:?} γ = {:?} d = {:?} z_0 = {:?} z_1 = {:?} w_l_y: {:?} α = {:?} √α = {:?} n'₀ = {:?} n' = {:?} f' = {:?}", scalar_fov.to_degrees(), factor, z_n, z_f, gamma.to_degrees(), d, z_0, z_1, w_l_y, alpha, alpha_sqrt, directed_near_normal, directed_near, directed_far);
@ -990,7 +1148,7 @@ impl Scene {
/* // let directed_near = 1.0;
let directed_near = ((z_n + (z_f * z_n).sqrt()) / /*sin_gamma*/factor) as f32; //1.0; */
// let directed_far = directed_near + d as f32;
// println!("view_dir: {:?}, new_dir: {:?}, directed_light_dir: {:?}, dot_prod: {:?}, sin_gamma: {:?}, near_dist: {:?}, d: {:?}, z_n: {:?}, z_f: {:?}, directed_near: {:?}, directed_far: {:?}", view_dir, new_dir, directed_light_dir, dot_prod, sin_gamma, near_dist, d, z_n, z_f, directed_near, directed_far);
// println!("view_dir: {:?}, new_dir: {:?}, directed_light_dir: {:?}, cos_gamma: {:?}, sin_gamma: {:?}, near_dist: {:?}, d: {:?}, z_n: {:?}, z_f: {:?}, directed_near: {:?}, directed_far: {:?}", view_dir, new_dir, directed_light_dir, cos_gamma, sin_gamma, near_dist, d, z_n, z_f, directed_near, directed_far);
/* let size1 = bounds1.half_size();
let center1 = bounds1.center(); */
/* let look_at = cam_pos - (directed_near - near_dist) * up;
@ -1001,9 +1159,9 @@ impl Scene {
// let w_v: Mat4<f32> = Mat4::translation_3d(/*-bounds1.center()*/-center1);
//new observer point n-1 behind eye position
//pos = eyePos-up*(n-nearDist)
// let directed_near = if /*sin_gamma > EPISLON_GAMMA*/factor != -1.0 { directed_near } else { near_dist/*0.0*//*-(near_dist *//*- light_focus_pos.y)*/ };
light_focus_pos.y = if factor != -1.0 {
/*near_dist*/z_n as f32 - directed_near
// let directed_near = if /*sin_gamma > EPISLON_GAMMA*/factor > EPSILON_UPSILON { directed_near } else { near_dist/*0.0*//*-(near_dist *//*- light_focus_pos.y)*/ };
light_focus_pos.y = if factor > EPSILON_UPSILON {
light_focus_pos.y/* - directed_near*/+ (/*near_dist*//*z_0 as f32*/ - directed_near)
} else {
light_focus_pos.y
};
@ -1029,7 +1187,7 @@ impl Scene {
let _bounds0 = math::fit_psr(/*l_r*/shadow_view_mat/* * inverse_visible*/, visible_light_volume.iter().copied(), /*|p| math::Vec3::from(p) / p.w*/math::Vec4::homogenized);
// let factor = -1.0;
let w_p: math::Mat4<f32> = {
if /*sin_gamma > EPISLON_GAMMA*/factor != -1.0 {
if /*sin_gamma > EPISLON_GAMMA*/factor > EPSILON_UPSILON {
// Projection for y
let n = directed_near;// - near_dist;
let f = directed_far;
@ -1153,7 +1311,7 @@ impl Scene {
near: zmin,//directed_near,
far: zmax,//directed_far,
}); */
let shadow_all_mat: math::Mat4<f32> = w_p * shadow_view_mat/*w_v * light_all_mat*/;
let shadow_all_mat: math::Mat4<f32> = /*(w_v * l_r).inverted() * */w_p * shadow_view_mat/*w_v * light_all_mat*/;
let _w_p_arr = shadow_all_mat.cols.iter().map(|e| (e.x, e.y, e.z, e.w)).collect::<Vec<_>>();
// println!("mat4 shadow_all_mat = mat4(vec4{:?}, vec4{:?}, vec4{:?}, vec4{:?});", w_p_arr[0], w_p_arr[1], w_p_arr[2], w_p_arr[3]);
let math::Aabb::<f32> { min: math::Vec3 { x: xmin, y: ymin, z: zmin }, max: math::Vec3 { x: xmax, y: ymax, z: zmax } } =
@ -1173,7 +1331,7 @@ impl Scene {
let o_x = -(xmax + xmin) / (xmax - xmin);
let o_y = -(ymax + ymin) / (ymax - ymin);
let o_z = -(zmax + zmin) / (zmax - zmin);
let directed_proj_mat = if /*sin_gamma > EPISLON_GAMMA*/factor != -1.0 {
let directed_proj_mat = if /*sin_gamma > EPISLON_GAMMA*/factor > EPSILON_UPSILON {
// Mat4::identity()
Mat4::new(
s_x, 0.0, 0.0, o_x,
@ -1283,6 +1441,7 @@ impl Scene {
self.terrain.render_shadows(
renderer,
&self.globals,
&self.lights,
&self.shadow_mats,
&self.light_data,
is_daylight,

View File

@ -455,6 +455,15 @@ struct SpriteData {
pub struct Terrain<V: RectRasterableVol> {
atlas: AtlasAllocator,
chunks: HashMap<Vec2<i32>, TerrainChunkData>,
/// Temporary storage for dead chunks that might still be shadowing chunks
/// in view. We wait until either the chunk definitely cannot be
/// shadowing anything the player can see, the chunk comes back into
/// view, or for daylight to end, before removing it (whichever comes
/// first).
///
/// Note that these chunks are not complete; for example, they are missing
/// texture data.
shadow_chunks: Vec<(Vec2<i32>, TerrainChunkData)>,
/* /// Secondary index into the terrain chunk table, used to sort through chunks by z index from
/// the top down.
z_index_down: BTreeSet<Vec3<i32>>,
@ -2239,6 +2248,7 @@ impl<V: RectRasterableVol> Terrain<V> {
Self {
atlas,
chunks: HashMap::default(),
shadow_chunks: Vec::default(),
mesh_send_tmp: send,
mesh_recv: recv,
mesh_todo: HashMap::default(),
@ -2328,7 +2338,7 @@ impl<V: RectRasterableVol> Terrain<V> {
Ok((atlas, texture))
}
fn remove_chunk_meta(&mut self, _pos: Vec2<i32>, chunk: TerrainChunkData) {
fn remove_chunk_meta(&mut self, _pos: Vec2<i32>, chunk: &TerrainChunkData) {
/* println!("Terrain chunk already existed: {:?}", pos); */
self.atlas.deallocate(chunk.col_lights);
/* let (zmin, zmax) = chunk.z_bounds;
@ -2338,7 +2348,7 @@ impl<V: RectRasterableVol> Terrain<V> {
fn insert_chunk(&mut self, pos: Vec2<i32>, chunk: TerrainChunkData) {
if let Some(old) = self.chunks.insert(pos, chunk) {
self.remove_chunk_meta(pos, old);
self.remove_chunk_meta(pos, &old);
}
/* let (zmin, zmax) = chunk.z_bounds;
self.z_index_up.insert(Vec3::from(zmin, pos.x, pos.y));
@ -2348,7 +2358,9 @@ impl<V: RectRasterableVol> Terrain<V> {
fn remove_chunk(&mut self, pos: Vec2<i32>) {
// println!("Terrain chunk removed: {:?}", pos);
if let Some(chunk) = self.chunks.remove(&pos) {
self.remove_chunk_meta(pos, chunk);
self.remove_chunk_meta(pos, &chunk);
// Temporarily remember dead chunks for shadowing purposes.
self.shadow_chunks.push((pos, chunk));
}
if let Some(_todo) = self.mesh_todo.remove(&pos) {
/* println!("Terrain chunk was being meshed: {:?}",
@ -2367,7 +2379,11 @@ impl<V: RectRasterableVol> Terrain<V> {
loaded_distance: f32,
view_mat: Mat4<f32>,
proj_mat: Mat4<f32>,
) -> (Aabb<f32>, Aabb<f32>, Aabb<f32>) {
) -> (
Aabb<f32>,
/* Aabb<f32>, Aabb<f32> */ Vec<math::Vec3<f32>>,
math::Aabr<f32>,
) {
let current_tick = scene_data.tick;
let current_time = scene_data.state.get_time();
let mut visible_bounding_box: Option<Aabb<f32>> = None;
@ -2855,7 +2871,9 @@ impl<V: RectRasterableVol> Terrain<V> {
let collides_with_aabr = |a: math::Aabr<f32>, b: math::Aabr<f32>| {
a.min.partial_cmple(&b.max).reduce_and() && a.max.partial_cmpge(&b.min).reduce_and()
};
if ray_direction.z < 0.0 && renderer.render_mode().shadow.is_map() {
let (visible_light_volume, visible_psr_bounds) = if ray_direction.z < 0.0
&& renderer.render_mode().shadow.is_map()
{
let visible_bounding_box = Aabb {
min: visible_bounding_box.min - focus_off,
max: visible_bounding_box.max - focus_off,
@ -2912,47 +2930,81 @@ impl<V: RectRasterableVol> Terrain<V> {
let visible_bounds = math::Aabr::from(math::fit_psr(
ray_mat,
/* super::aabb_to_points(visible_bounding_box).iter().copied() */
visible_light_volume.into_iter(),
visible_light_volume.iter().copied(),
|p| p, //math::Vec3::from(p), /* / p.w */
));
let ray_mat = ray_mat * math::Mat4::translation_3d(-focus_off);
/* let visible_bounds_old = Aabr::from(super::fit_psr(ray_mat, super::aabb_to_points(visible_bounding_box).iter().copied(), |p| Vec3::from(p) / p.w));
println!("old: {:?} new: {:?}", visible_bounds_old, visible_bounds); */
self.chunks.iter_mut()
// NOTE: We deliberately avoid doing this computation for chunks we already know
// are visible, since by definition they'll always intersect the visible view
// frustum.
.filter(|chunk| chunk.1.visible == Visibility::InRange)
.for_each(|(pos, chunk)| {
let can_shadow_sun = |pos: Vec2<i32>, chunk: &TerrainChunkData| {
let chunk_pos = pos.map(|e| e as f32 * chunk_sz);
// Ensure the chunk is within the view frustum
let chunk_min = [chunk_pos.x, chunk_pos.y, chunk.z_bounds.0];
let chunk_max = [
// Ensure the chunk is within the PSR set.
let chunk_box = math::Aabb {
min: math::Vec3::new(chunk_pos.x, chunk_pos.y, chunk.z_bounds.0),
max: math::Vec3::new(
chunk_pos.x + chunk_sz,
chunk_pos.y + chunk_sz,
chunk.z_bounds.1,
];
let chunk_box = math::Aabb {
min: math::Vec3::from(chunk_min) - focus_off,
max: math::Vec3::from(chunk_max) - focus_off,
),
};
let chunk_from_light = math::Aabr::from(math::fit_psr(ray_mat, math::aabb_to_points(chunk_box).iter().copied(), |p| p/*math::Vec3::from(p)/* / p.w*/*/));
let chunk_from_light = math::Aabr::from(math::fit_psr(
ray_mat,
math::aabb_to_points(chunk_box).iter().copied(),
|p| p, /* math::Vec3::from(p)/* / p.w*/ */
));
/* let chunk_from_light = Aabr {
min: (ray_mat * Vec4::from_point(chunk_box.min)).xy(),
max: (ray_mat * Vec4::from_point(chunk_box.max)).xy(),
}.made_valid(); */
let can_shadow_sun = collides_with_aabr(chunk_from_light, visible_bounds);
/* let can_shadow_sun = */
collides_with_aabr(chunk_from_light, visible_bounds)
/* let can_shadow_sun_old = collides_with_aabr(chunk_from_light, visible_bounds_old);
if can_shadow_sun != can_shadow_sun_old {
println!("Different results for chunk {:?} (from light = {:?}):\n\
old = {:?} new = {:?}",
chunk_box, chunk_from_light, can_shadow_sun_old, can_shadow_sun);
} */
chunk.can_shadow_sun = can_shadow_sun;
};
// Handle potential shadow casters (chunks that aren't visible, but are still in
// range) to see if they could cast shadows.
self.chunks.iter_mut()
// NOTE: We deliberately avoid doing this computation for chunks we already know
// are visible, since by definition they'll always intersect the visible view
// frustum.
.filter(|chunk| chunk.1.visible <= Visibility::InRange)
.for_each(|(&pos, chunk)| {
chunk.can_shadow_sun = can_shadow_sun(pos, chunk);
});
}
// Handle dead chunks that we kept around only to make sure shadows don't blink
// out when a chunk disappears.
//
// If the sun can currently cast shadows, we retain only those shadow chunks
// that both: 1. have not been replaced by a real chunk instance,
// and 2. are currently potential shadow casters (as witnessed by
// `can_shadow_sun` returning true).
//
// NOTE: Please make sure this runs *after* any code that could insert a chunk!
// Otherwise we may end up with multiple instances of the chunk trying to cast
// shadows at the same time.
let chunks = &self.chunks;
self.shadow_chunks
.retain(|(pos, chunk)| !chunks.contains_key(pos) && can_shadow_sun(*pos, chunk));
(visible_light_volume, visible_bounds)
} else {
// There's no daylight or no shadows, so there's no reason to keep any
// shadow chunks around.
self.shadow_chunks.clear();
(Vec::new(), math::Aabr {
min: math::Vec2::zero(),
max: math::Vec2::zero(),
})
};
/* let cam_pos = Vec3::from(view_mat.inverted() * Vec4::unit_w()) + focus_off; let look_at = visible_box.center();
let view_dir = (focus_pos - cam_pos).normalized();
let up_vec = ray_direction.cross(view_dir).cross(light_dir).normalized();
@ -3008,7 +3060,12 @@ impl<V: RectRasterableVol> Terrain<V> {
}
} */
(scene_bounding_box, visible_bounding_box, psc_bounding_box)
(
/* scene_bounding_box, visible_bounding_box, psc_bounding_box */
visible_bounding_box,
visible_light_volume,
visible_psr_bounds,
)
}
pub fn chunk_count(&self) -> usize { self.chunks.len() }
@ -3020,10 +3077,13 @@ impl<V: RectRasterableVol> Terrain<V> {
.count()
}
pub fn shadow_chunk_count(&self) -> usize { self.shadow_chunks.len() }
pub fn render_shadows(
&self,
renderer: &mut Renderer,
globals: &Consts<Globals>,
lights: &Consts<Light>,
shadow_mats: &Consts<ShadowLocals>,
light_data: &[Light],
is_daylight: bool,
@ -3040,16 +3100,23 @@ impl<V: RectRasterableVol> Terrain<V> {
let chunk_iter = Spiral2d::new()
.filter_map(|rpos| {
let pos = focus_chunk + rpos;
self.chunks.get(&pos).map(|c| (pos, c))
self.chunks.get(&pos)
})
.take(self.chunks.len());
// let is_daylight = sun_dir.z < 0.0/*0.6*/;
// Directed shadows
//
// NOTE: We also render shadows for dead chunks that were found to still be
// potential shadow casters, to avoid shadows suddenly disappearing at
// very steep sun angles (e.g. sunrise / sunset).
if is_daylight {
for (_, chunk) in chunk_iter.clone() {
if chunk.can_shadow_sun() {
chunk_iter
.clone()
.filter(|chunk| chunk.can_shadow_sun())
.chain(self.shadow_chunks.iter().map(|(_, chunk)| chunk))
.for_each(|chunk| {
// Directed light shadows.
renderer.render_terrain_shadow_directed(
// &chunk.shadow_model,
@ -3057,18 +3124,20 @@ impl<V: RectRasterableVol> Terrain<V> {
globals,
&chunk.locals,
shadow_mats,
/* lights,
* shadows,
lights,
/* shadows,
* &lod.map,
* &lod.horizon, */
);
}
}
});
}
// Point shadows
for _light in light_data.iter().take(1) {
for (_, chunk) in chunk_iter.clone() {
//
// NOTE: We don't bother retaining chunks unless they cast sun shadows, so we
// don't use `shadow_chunks` here.
light_data.iter().take(1).for_each(|_light| {
chunk_iter.clone().for_each(|chunk| {
if chunk.can_shadow_point {
// shadow_vertex_count += chunk.shadow_model.vertex_range.len();
renderer.render_shadow_point(
@ -3077,14 +3146,14 @@ impl<V: RectRasterableVol> Terrain<V> {
globals,
&chunk.locals,
shadow_mats,
/* lights,
* shadows,
lights,
/* shadows,
* &lod.map,
* &lod.horizon, */
);
}
}
}
});
});
}
pub fn render(

View File

@ -644,11 +644,11 @@ impl PlayState for SessionState {
self.scene
.camera_mut()
.compute_dependents(&*self.client.borrow().state().terrain());
// Extract HUD events ensuring the client borrow gets dropped.
let mut hud_events = self.hud.maintain(
&self.client.borrow(),
global_state,
DebugInfo {
// Generate debug info, if needed (it iterates through enough data that we might
// as well avoid it unless we need it).
let debug_info = if global_state.settings.gameplay.toggle_debug {
Some(DebugInfo {
tps: clock.get_tps(),
ping_ms: self.client.borrow().get_ping_ms_rolling_avg(),
coordinates: self
@ -677,9 +677,19 @@ impl PlayState for SessionState {
.cloned(),
num_chunks: self.scene.terrain().chunk_count() as u32,
num_visible_chunks: self.scene.terrain().visible_chunk_count() as u32,
num_shadow_chunks: self.scene.terrain().shadow_chunk_count() as u32,
num_figures: self.scene.figure_mgr().figure_count() as u32,
num_figures_visible: self.scene.figure_mgr().figure_count_visible() as u32,
},
})
} else {
None
};
// Extract HUD events ensuring the client borrow gets dropped.
let mut hud_events = self.hud.maintain(
&self.client.borrow(),
global_state,
&debug_info,
&self.scene.camera(),
clock.get_last_delta(),
HudInfo {