Skip to content

Commit

Permalink
edit: sun traj & directional light sync
Browse files Browse the repository at this point in the history
  • Loading branch information
Sufhal committed Sep 16, 2024
1 parent be0f1d3 commit 3564611
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 30 deletions.
48 changes: 43 additions & 5 deletions src/modules/core/directional_light.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use cgmath::{perspective, Deg, EuclideanSpace, InnerSpace, Matrix4, Point3, SquareMatrix, Transform, Vector3, Vector4, Zero};
use crate::modules::camera::camera::OPENGL_TO_WGPU_MATRIX;
use crate::modules::{camera::camera::OPENGL_TO_WGPU_MATRIX, environment::sun::Sun, terrain::terrain::Terrain};
use super::texture::Texture;

const SHADOW_MAP_SIZE: u32 = 8192;
// const SHADOW_MAP_SIZE: u32 = 512;

pub struct DirectionalLight {
direction: Vector3<f32>,
pub position: Point3<f32>,
pub target: Point3<f32>,
pub shadow_texture: Texture,
Expand Down Expand Up @@ -39,6 +40,7 @@ impl DirectionalLight {
cascade_splits,
cascade_projections,
cascade_textures,
direction: Vector3::zero(),
}
}

Expand All @@ -55,7 +57,12 @@ impl DirectionalLight {
}
}

pub fn update(&mut self, camera_view_proj: Matrix4<f32>) {
pub fn update(&mut self, camera_view_proj: Matrix4<f32>, terrain: &Terrain) {
let [x, y, z, _] = terrain.environment.sun.uniform.sun_position;
let source = Vector3::from([x, y, z]);
let mut center = Vector3::from(terrain.center);
center.y -= 200.0; // this helps to have less stretched shadows when the sun is low
self.direction = (center - source).normalize();
for i in 0..3 {
let near = self.cascade_splits[i];
let far = self.cascade_splits[i + 1];
Expand Down Expand Up @@ -135,15 +142,19 @@ fn calculate_split_dist(near: f32, far: f32, i: u32, splits: u32, lambda: f32) -
}

fn calculate_light_view_proj(frustum_corners: &[Point3<f32>; 8], directional_light: &DirectionalLight) -> Matrix4<f32> {
let light_direction = Vector3::new(-0.5, -1.0, -0.5).normalize(); // Diagonale depuis la gauche et vers l'arrière
// let light_direction = Vector3::new(-0.5, -1.0, -0.5).normalize(); // Diagonale depuis la gauche et vers l'arrière
let light_direction = directional_light.direction; // Diagonale depuis la gauche et vers l'arrière
let frustum_center = get_frustum_center(&frustum_corners);

let stabilized_frustum_center = frustum_center;
// let stabilized_frustum_center = stabilize_cascade_center(frustum_center, directional_light.cascade_projections[0]);

let light_distance = 100.0;
let light_position = frustum_center - light_direction * light_distance;
let light_position = stabilized_frustum_center - light_direction * light_distance;

let light_view = Matrix4::look_at_rh(
light_position,
frustum_center,
stabilized_frustum_center,
Vector3::unit_y()
);

Expand Down Expand Up @@ -212,4 +223,31 @@ fn get_frustum_center(frustum_corners: &[Point3<f32>; 8]) -> Point3<f32> {
center.z /= 8.0;

center
}

const TEXEL_SCALE: f32 = 2.0 / SHADOW_MAP_SIZE as f32; // résolution de la shadow map de 1024px
const INV_TEXEL_SCALE: f32 = 1.0 / TEXEL_SCALE;

fn stabilize_cascade_center(center: Point3<f32>, cascade_view_projection: Matrix4<f32>) -> Point3<f32> {
// Projeter le nouveau centre en utilisant la projection précédente
let projected_center = cascade_view_projection * center.to_homogeneous();

// Diviser par w pour obtenir les coordonnées normalisées
let w = projected_center.w;

// Arrondir les coordonnées x et y à des valeurs de texels entiers
let x = ((projected_center.x / w) * INV_TEXEL_SCALE).floor() * TEXEL_SCALE;
let y = ((projected_center.y / w) * INV_TEXEL_SCALE).floor() * TEXEL_SCALE;
let z = projected_center.z / w;

// Re-projeter dans l'espace monde
let inv_cascade_view_projection = cascade_view_projection.invert().unwrap();
let corrected_center = inv_cascade_view_projection * Vector4::new(x, y, z, 1.0);

// Diviser par w pour obtenir les coordonnées 3D finales
Point3::new(
corrected_center.x / corrected_center.w,
corrected_center.y / corrected_center.w,
corrected_center.z / corrected_center.w
)
}
4 changes: 2 additions & 2 deletions src/modules/environment/cycle.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::modules::utils::functions::{denormalize_f32, normalize_f32};

const REAL_TIME: bool = true;
const REAL_TIME: bool = false;
const MS_IN_DAY: u64 = 24 * 3600 * 1000;
const UTC: u64 = 2;
const DAY_START_HOUR: u64 = 5;
Expand Down Expand Up @@ -43,7 +43,7 @@ impl Cycle {

pub fn update(&mut self, mut delta: f32) {
if REAL_TIME == false {
delta *= (86_400_000.0 / 60_000.0) * 2.0; // 24h in 1min
delta *= (86_400_000.0 / 60_000.0) * 4.0; // 24h in 1min
// delta *= 86_400_000.0 / 60_000.0; // 24h in 1min
}
self.delta = delta;
Expand Down
4 changes: 2 additions & 2 deletions src/modules/environment/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ pub struct Environment {

impl Environment {

pub async fn load(name: &str, state: &State<'_>) -> anyhow::Result<Self> {
pub async fn load(name: &str, center: [f32; 3], state: &State<'_>) -> anyhow::Result<Self> {
let cycle = Cycle::new();
let day_msenv = MsEnv::read(name).await?;
let night_msenv = MsEnv::read("moonlight04").await?;
Ok(Self {
cycle,
fog: Fog::new(&day_msenv, &night_msenv),
sun: Sun::new(&day_msenv, &night_msenv, state),
sun: Sun::new(&day_msenv, &night_msenv, center, state),
sky: Sky::new(&day_msenv, &night_msenv, state),
clouds: Clouds::new(&day_msenv, state).await?
})
Expand Down
17 changes: 14 additions & 3 deletions src/modules/environment/sun.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use cgmath::{Deg, Matrix4, Quaternion, Rotation3, Vector3};
use cgmath::{Angle, Deg, Matrix4, Quaternion, Rad, Rotation3, Vector3};
use crate::modules::{core::model::{CustomMesh, TransformUniform}, geometry::plane::Plane, state::State};
use super::{cycle::Cycle, environment::MsEnv, Position};

const DAY_POSITION: [f32; 3] = [2600.0, 000.0, -1000.0];
const NIGHT_POSITION: [f32; 3] = [1400.0, 1400.0, 1400.0];

pub struct Sun {
center: [f32; 3],
position: Position,
pub uniform: SunUniform,
pub mesh: CustomMesh,
}

impl Sun {
pub fn new(day_msenv: &MsEnv, night_msenv: &MsEnv, state: &State<'_>) -> Self {
pub fn new(day_msenv: &MsEnv, night_msenv: &MsEnv, center: [f32; 3], state: &State<'_>) -> Self {
let position = DAY_POSITION;
let plane = Plane::new(500.0, 500.0, 1, 1);
let mut uniform = SunUniform::default();
Expand All @@ -38,6 +39,7 @@ impl Sun {
position,
uniform,
mesh,
center,
}
}

Expand All @@ -48,7 +50,8 @@ impl Sun {
}
if cycle.day_factor > 0.0 {
let angle = 180.0 * cycle.day_factor;
self.position = (Quaternion::from_axis_angle(Vector3::unit_z(), Deg(angle)) * Vector3::from(DAY_POSITION)).into();
// self.position = (Quaternion::from_axis_angle(Vector3::unit_z(), Deg(angle)) * Vector3::from(DAY_POSITION)).into();
self.position = compute_sun_position(&self.center, 2000.0, angle);
self.uniform.sun_position = [self.position[0], self.position[1], self.position[2], 0.0];
}
queue.write_buffer(
Expand Down Expand Up @@ -109,4 +112,12 @@ impl Default for SunUniform {
night_character_ambient: Default::default(),
}
}
}

fn compute_sun_position(center: &[f32; 3], radius: f32, angle_deg: f32) -> [f32; 3] {
let angle_rad = Rad::from(cgmath::Deg(angle_deg));
let x = center[0] + radius * angle_rad.cos();
let z = center[2];
let y = center[1] + radius * angle_rad.sin();
[x, y, z]
}
4 changes: 3 additions & 1 deletion src/modules/geometry/plane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl Plane {
}
}

pub fn set_vertices_height(&mut self, vertices_height: Vec<f32>) {
pub fn set_vertices_height(&mut self, vertices_height: &Vec<f32>) {
if vertices_height.len() != self.vertices.len() {
panic!("Impossible to set vertices height with incompatible surface vertices count");
}
Expand Down Expand Up @@ -425,5 +425,7 @@ impl Plane {
clouds_buffer
)
}


}

5 changes: 3 additions & 2 deletions src/modules/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ impl<'a> State<'a> {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: wgpu::Features::all_webgpu_mask(),
required_features: wgpu::Features::DEPTH_CLIP_CONTROL,
// required_features: wgpu::Features::all_webgpu_mask(),
// required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
Expand Down Expand Up @@ -453,7 +454,7 @@ impl<'a> State<'a> {
}

// self.shadow_pipeline.uniforms.directional_light = self.directional_light.uniform(self.projection.aspect);
self.directional_light.update(self.common_pipeline.uniforms.camera.view_proj.into());
self.directional_light.update(self.common_pipeline.uniforms.camera.view_proj.into(), &self.terrains[0]);
self.shadow_pipeline.uniforms.directional_light = self.directional_light.uniform_from_camera(self.common_pipeline.uniforms.camera.view_proj.into());
self.queue.write_buffer(&self.shadow_pipeline.buffers.directional_light, 0, bytemuck::cast_slice(&[self.shadow_pipeline.uniforms.directional_light]));
self.queue.write_buffer(&self.common_pipeline.buffers.directional_light, 0, bytemuck::cast_slice(&[self.shadow_pipeline.uniforms.directional_light]));
Expand Down
7 changes: 4 additions & 3 deletions src/modules/terrain/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub struct Chunk {
pub terrain_mesh: CustomMesh,
pub water_mesh: CustomMesh,
pub depth_buffer: wgpu::Buffer,
pub mean_height: f32,
water_buffer: wgpu::Buffer,
area_data: AreaData,
}
Expand All @@ -24,8 +25,8 @@ impl Chunk {
let name = Self::name_from(*x, *y);
let chunk_path = &format!("{terrain_path}/{name}");
let height = Height::read(&chunk_path, setting.height_scale).await?;
let mean_height = height.vertices.iter().fold(0.0, |acc, v| acc + *v) / height.vertices.len() as f32;
let water = Water::read(&chunk_path).await?;
// dbg!(&water);
let textures_set = ChunkTextureSet::read(&chunk_path).await?;
let mut alpha_maps = Vec::new();
for i in 0..textures_set.textures.len() {
Expand All @@ -50,7 +51,7 @@ impl Chunk {
(*y as f32 * size) + (size / 2.0)
];
let mut terrain_geometry = Plane::new(size, size, segments, segments);
terrain_geometry.set_vertices_height(height.vertices);
terrain_geometry.set_vertices_height(&height.vertices);
let terrain_mesh = terrain_geometry.to_terrain_mesh(
&state.device,
&state.terrain_pipeline,
Expand Down Expand Up @@ -79,13 +80,13 @@ impl Chunk {
&water_textures,
);
let area_data = AreaData::read(&chunk_path).await?;

Ok(Self {
terrain_mesh,
water_mesh,
area_data,
water_buffer,
depth_buffer,
mean_height,
})
}

Expand Down
14 changes: 11 additions & 3 deletions src/modules/terrain/terrain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ pub struct Terrain {
water_texture: WaterTexture,
pub setting: Setting,
pub environment: Environment,
pub chunks: Vec<Chunk>
pub chunks: Vec<Chunk>,
pub center: [f32; 3],
}

impl Terrain {
Expand All @@ -16,7 +17,6 @@ impl Terrain {
let setting = Setting::read(&path).await?;
let texture_set = TextureSet::read(&path).await?;
let water_texture = WaterTexture::load(state).await?;
let environment = Environment::load(&setting.environment, state).await?;
let textures = texture_set.load_textures(&state.device, &state.queue).await?;
let mut chunks = Vec::new();
for x in 0..setting.map_size[0] {
Expand Down Expand Up @@ -70,11 +70,19 @@ impl Terrain {
for chunk in &mut chunks {
chunk.load_objects_instances(state).await;
}
let center = {
let y = chunks.iter().fold(0.0, |acc, v| acc + v.mean_height) / chunks.len() as f32;
let x = (setting.map_size[0] as f32 * 256.0) / 2.0;
let z = (setting.map_size[1] as f32 * 256.0) / 2.0;
[x, y, z]
};
let environment = Environment::load(&setting.environment, center, state).await?;
Ok(Self {
setting,
chunks,
environment,
water_texture
water_texture,
center,
})
}

Expand Down
27 changes: 18 additions & 9 deletions src/shaders/terrain.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ fn srgb_to_linear(color: vec3<f32>) -> vec3<f32> {
return vec3<f32>(r, g, b);
}

const SHADOW_START: f32 = 0.10;
const SHADOW_ZENITH: f32 = 0.5;
const SHADOW_END: f32 = 0.90;

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
Expand Down Expand Up @@ -219,7 +222,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let material_emissive: vec3<f32> = mix(sun.night_material_emissive.rgb, sun.day_material_emissive.rbg, sun_light_factor);
let background_diffuse: vec3<f32> = mix(sun.night_background_diffuse.rgb, sun.day_background_diffuse.rbg, sun_light_factor);

let ambient_strength = mix(0.6, 0.3, sun_light_factor);
let ambient_strength = mix(0.7, 0.3, sun_light_factor);
let ambient_color = material_ambient * ambient_strength;

let sun_light_dir = normalize(sun.sun_position.xyz - in.world_position);
Expand All @@ -245,29 +248,35 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
// proj_coords.xy,
// light_space_position.z * proj_correction,
// );

// Calculer le biais pour éviter le shadow acne
let bias = 0.0005;

// Appliquer le PCF (Percentage Closer Filtering)
// PCF (Percentage Closer Filtering)
var shadow: f32 = 0.0;
let texel_size = 1.0 / 8192.0; // Ajustez cette valeur selon la taille de votre shadow map

let texel_size = 1.0 / 8192.0;
for (var x: i32 = -1; x <= 1; x++) {
for (var y: i32 = -1; y <= 1; y++) {
let offset = vec2<f32>(f32(x), f32(y)) * texel_size;
shadow += textureSampleCompare(
shadow_texture,
shadow_sampler,
// proj_coords.xy,
proj_coords.xy + offset,
// proj_coords.z - bias
light_space_position.z * proj_correction
);
}
}
shadow /= 9.0;
shadow = denormalize_value_between(shadow, 0.25, 1.0); // attenuate the opacity of the shadow;

var shadow_strength = 0.0;

final_color *= denormalize_value_between(shadow, 0.2, 1.0);
if cycle.day_factor >= SHADOW_START && cycle.day_factor < SHADOW_ZENITH {
shadow_strength = ease_out_expo(normalize_value_between(cycle.day_factor, SHADOW_START, SHADOW_ZENITH));
}
else if cycle.day_factor >= SHADOW_ZENITH && cycle.day_factor <= SHADOW_END {
shadow_strength = ease_out_expo(1.0 - normalize_value_between(cycle.day_factor, SHADOW_ZENITH, SHADOW_END));
}
shadow = mix(1.0, shadow, shadow_strength);
final_color *= shadow;

// fog
if fog.enabled == 1.0 {
Expand Down

0 comments on commit 3564611

Please sign in to comment.