Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Size shadow map based on camera view direction #194

Merged
merged 1 commit into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions korangar/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ use settings::AudioSettings;
use wgpu::Queue;
use wgpu::{Device, Dx12Compiler, Instance, InstanceFlags, MemoryHints};
use winit::application::ApplicationHandler;
use winit::dpi::PhysicalSize;
use winit::dpi::{LogicalSize, PhysicalSize};
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::keyboard::PhysicalKey;
Expand Down Expand Up @@ -114,6 +114,11 @@ const MAIN_MENU_CLICK_SOUND_EFFECT: &str = "¹öÆ°¼Ò¸®.wav";
// number that should be playable on most devices.
const NUMBER_OF_POINT_LIGHTS_WITH_SHADOWS: usize = 6;

const INITIAL_SCREEN_SIZE: ScreenSize = ScreenSize {
width: 1280.0,
height: 720.0,
};

static ICON_DATA: &[u8] = include_bytes!("../archive/data/icon.png");

// Create the `threads` module.
Expand Down Expand Up @@ -271,12 +276,6 @@ struct Client {

impl Client {
fn init() -> Self {
// We don't know the window size yet, so these values are dummy values.
let initial_screen_size = ScreenSize {
width: 800.0,
height: 600.0,
};

time_phase!("load settings", {
let picker_value = Arc::new(AtomicU64::new(0));
let input_system = InputSystem::new(picker_value.clone());
Expand Down Expand Up @@ -401,15 +400,15 @@ impl Client {
});

let interface_renderer = InterfaceRenderer::new(
initial_screen_size,
INITIAL_SCREEN_SIZE,
font_loader.clone(),
&texture_loader,
*high_quality_interface.get(),
);
let bottom_interface_renderer = GameInterfaceRenderer::new(initial_screen_size, &texture_loader);
let bottom_interface_renderer = GameInterfaceRenderer::new(INITIAL_SCREEN_SIZE, &texture_loader);
let middle_interface_renderer = GameInterfaceRenderer::from_renderer(&bottom_interface_renderer);
let top_interface_renderer = GameInterfaceRenderer::from_renderer(&bottom_interface_renderer);
let effect_renderer = EffectRenderer::new(initial_screen_size);
let effect_renderer = EffectRenderer::new(INITIAL_SCREEN_SIZE);
#[cfg(feature = "debug")]
let debug_marker_renderer = DebugMarkerRenderer::new();

Expand Down Expand Up @@ -446,7 +445,7 @@ impl Client {

time_phase!("initialize interface", {
let application = InterfaceSettings::load_or_default();
let mut interface = Interface::new(initial_screen_size);
let mut interface = Interface::new(INITIAL_SCREEN_SIZE);
let mut focus_state = FocusState::default();
let mouse_cursor = MouseCursor::new(&mut sprite_loader, &mut action_loader);
let dialog_system = DialogSystem::default();
Expand Down Expand Up @@ -1843,12 +1842,12 @@ impl Client {
self.start_camera.update(delta_time);
self.player_camera.update(delta_time);

let zoom_scale: f32 = match self.entities.is_empty() {
true => self.start_camera.get_zoom_scale(),
false => self.player_camera.get_zoom_scale(),
let view_direction = match self.entities.is_empty() {
true => self.start_camera.view_direction(),
false => self.player_camera.view_direction(),
};

self.directional_shadow_camera.update(directional_light_direction, zoom_scale);
self.directional_shadow_camera.update(directional_light_direction, view_direction);

#[cfg(feature = "debug")]
update_cameras_measurement.stop();
Expand Down Expand Up @@ -2389,7 +2388,13 @@ impl ApplicationHandler for Client {
assert_eq!(image_buffer.width(), image_buffer.height(), "icon must be square");
let icon = Icon::from_rgba(image_data, image_buffer.width(), image_buffer.height()).unwrap();

let window_attributes = Window::default_attributes().with_title(CLIENT_NAME).with_window_icon(Some(icon));
let window_attributes = Window::default_attributes()
.with_inner_size(LogicalSize {
width: INITIAL_SCREEN_SIZE.width,
height: INITIAL_SCREEN_SIZE.height,
})
.with_title(CLIENT_NAME)
.with_window_icon(Some(icon));
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());

let backend_name = self.graphics_engine.get_backend_name();
Expand Down
37 changes: 24 additions & 13 deletions korangar/src/world/cameras/directional_shadow.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
use cgmath::{InnerSpace, Matrix4, Point3, Vector2, Vector3, Zero};
use korangar_util::math::lerp;

use super::Camera;
use crate::graphics::orthographic_reverse_lh;

const FAR_PLANE: f32 = 500.0;
const NEAR_PLANE: f32 = -500.0;
const MIN_SCALE: f32 = 0.4;
const MAX_BOUNDS: f32 = 300.0;
const FRONT_SCALE: f32 = 1.5;
const SIDE_SCALE: f32 = 1.0;
const BACK_SCALE: f32 = 0.5;
const LOOK_UP: Vector3<f32> = Vector3::new(0.0, 1.0, 0.0);

pub struct DirectionalShadowCamera {
focus_point: Point3<f32>,
camera_position: Point3<f32>,
view_direction: Vector3<f32>,
zoom_scale: f32,
main_camera_view_direction: Vector3<f32>,
view_matrix: Matrix4<f32>,
projection_matrix: Matrix4<f32>,
view_projection_matrix: Matrix4<f32>,
Expand All @@ -25,7 +28,7 @@ impl DirectionalShadowCamera {
focus_point: Point3::new(0.0, 0.0, 0.0),
camera_position: Point3::new(0.0, 0.0, 0.0),
view_direction: Vector3::zero(),
zoom_scale: 0.0,
main_camera_view_direction: Vector3::zero(),
view_matrix: Matrix4::zero(),
projection_matrix: Matrix4::zero(),
view_projection_matrix: Matrix4::zero(),
Expand All @@ -34,29 +37,37 @@ impl DirectionalShadowCamera {

pub fn set_focus_point(&mut self, focus_point: Point3<f32>) {
// We need to snap the camera to a grid, or else shadows get too noisy.
let grid_size = 25.0;
let grid_size = 10.0;
self.focus_point = Point3::new(
(focus_point.x / grid_size).floor() * grid_size,
(focus_point.y / grid_size).floor() * grid_size,
(focus_point.z / grid_size).floor() * grid_size,
);
}

// The zoom_scale is used to scale the shadow map and give the best possible
// resolution for objects shadows.
pub fn update(&mut self, direction_to_light: Vector3<f32>, zoom_scale: f32) {
pub fn update(&mut self, direction_to_light: Vector3<f32>, main_camera_view_direction: Vector3<f32>) {
// TODO: NHA Currently the directional light is the direction TO the light.
// We should change that to make it the direction the light shines.
let direction_to_light = direction_to_light.normalize();
let scaled_direction = direction_to_light * 100.0;
self.camera_position = self.focus_point + scaled_direction;
self.view_direction = -direction_to_light;
self.zoom_scale = zoom_scale;
self.main_camera_view_direction = main_camera_view_direction;
}

fn calculate_bounds(&self) -> f32 {
let adjusted_scale = MIN_SCALE + (1.0 - MIN_SCALE) * self.zoom_scale;
MAX_BOUNDS * adjusted_scale
fn calculate_bounds(&self) -> (f32, f32, f32, f32) {
let flat_main_view = Vector3::new(self.main_camera_view_direction.x, 0.0, self.main_camera_view_direction.z).normalize();
let flat_light_direction = Vector3::new(self.view_direction.x, 0.0, self.view_direction.z).normalize();

let angle = flat_main_view.dot(flat_light_direction);
let lerp_factor = (-angle + 1.0) * 0.5;

let front = lerp(MAX_BOUNDS * FRONT_SCALE, MAX_BOUNDS * SIDE_SCALE, lerp_factor).round();
let back = lerp(-MAX_BOUNDS * BACK_SCALE, -MAX_BOUNDS * SIDE_SCALE, lerp_factor).round();
let left = lerp(-MAX_BOUNDS * SIDE_SCALE, -MAX_BOUNDS * BACK_SCALE, lerp_factor).round();
let right = lerp(MAX_BOUNDS * SIDE_SCALE, MAX_BOUNDS * FRONT_SCALE, lerp_factor).round();

(front, back, left, right)
}
}

Expand All @@ -70,9 +81,9 @@ impl Camera for DirectionalShadowCamera {
}

fn generate_view_projection(&mut self, _window_size: Vector2<usize>) {
let bound_size = self.calculate_bounds();
let (front, back, left, right) = self.calculate_bounds();
self.view_matrix = Matrix4::look_to_lh(self.camera_position(), self.view_direction, LOOK_UP);
self.projection_matrix = orthographic_reverse_lh(-bound_size, bound_size, -bound_size, bound_size, NEAR_PLANE, FAR_PLANE);
self.projection_matrix = orthographic_reverse_lh(left, right, back, front, NEAR_PLANE, FAR_PLANE);
self.view_projection_matrix = self.projection_matrix * self.view_matrix;
}

Expand Down
2 changes: 1 addition & 1 deletion korangar/src/world/cameras/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub use self::start::StartCamera;
#[cfg(feature = "debug")]
use crate::interface::layout::{ScreenPosition, ScreenSize};

const MAXIMUM_CAMERA_DISTANCE: f32 = 600.0;
const MAXIMUM_CAMERA_DISTANCE: f32 = 500.0;
const MINIMUM_CAMERA_DISTANCE: f32 = 150.0;

/// The world space has a left-handed coordinate system where the Y axis is up.
Expand Down
8 changes: 2 additions & 6 deletions korangar/src/world/cameras/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const ZOOM_SPEED: f32 = 2.0;
const ROTATION_SPEED: f32 = 0.01;
const DEFAULT_DISTANCE: f32 = 400.0;
const DEFAULT_ANGLE: f32 = 180_f32.to_radians();
const CAMERA_PITCH: Deg<f32> = Deg(-50.0);
const VERTICAL_FOV: Deg<f32> = Deg(15.0);
const CAMERA_PITCH: Deg<f32> = Deg(-55.0);
const VERTICAL_FOV: Deg<f32> = Deg(15.5);
const THRESHOLD: f32 = 0.01;
const LOOK_UP: Vector3<f32> = Vector3::new(0.0, 1.0, 0.0);

Expand Down Expand Up @@ -49,10 +49,6 @@ impl PlayerCamera {
self.focus_point.z.set_desired(position.z);
}

pub fn get_zoom_scale(&self) -> f32 {
(self.camera_distance.get_current() - MINIMUM_CAMERA_DISTANCE) / (MAXIMUM_CAMERA_DISTANCE - MINIMUM_CAMERA_DISTANCE)
}

pub fn soft_zoom(&mut self, zoom_factor: f32) {
self.camera_distance
.move_desired_clamp(zoom_factor * ZOOM_SPEED, MINIMUM_CAMERA_DISTANCE, MAXIMUM_CAMERA_DISTANCE);
Expand Down
5 changes: 0 additions & 5 deletions korangar/src/world/cameras/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ impl StartCamera {
self.camera_position = self.focus_point + rotated_offset;
self.view_direction = -rotated_offset.normalize();
}

pub fn get_zoom_scale(&self) -> f32 {
// The start camera has a fixed zoom.
1.0
}
}

impl Camera for StartCamera {
Expand Down
5 changes: 5 additions & 0 deletions korangar_util/src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ pub fn multiply_matrix4_and_point3(matrix: &Matrix4<f32>, vector: Point3<f32>) -
Point3::from_vec((adjusted_vector / adjusted_vector.w).truncate())
}

/// Simple linear interpolation.
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t
}

#[cfg(test)]
mod tests {
use cgmath::{assert_relative_eq, EuclideanSpace, Matrix4, Point3};
Expand Down
Loading