Skip to content

Commit

Permalink
Clean camera and raymarching
Browse files Browse the repository at this point in the history
  • Loading branch information
Azkellas committed Sep 29, 2023
1 parent f5e5f87 commit 8e198a5
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 65 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ wgpu = { version = "0.17.0", default-features = false }
egui = { git = "https://github.com/emilk/egui.git", rev = "b896d641c578c04603adf07a9350b8bfc3c7ed9f" }
pollster = "0.3.0"
log = "0.4.19"
winit = "0.28.6"


[dependencies]
wgpu.workspace = true
egui.workspace = true
pollster.workspace = true
log.workspace = true
winit.workspace = true
lib = { path = "./lib" }
hot-lib-reloader = { version = "^0.6", optional = true }

winit = "0.28.6"
env_logger = "0.10.0"
wasm-bindgen = "0.2.87"

Expand Down
1 change: 1 addition & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
winit.workspace = true
wgpu.workspace = true
egui.workspace = true
pollster.workspace = true
Expand Down
71 changes: 71 additions & 0 deletions lib/src/camera_control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use winit::event::MouseButton;

use crate::mouse_input::MouseState;

// Naive look-at camera.
// This version removes the use of quaternion to avoid adding a dependency.
// To avoid having to do linear algebra ourselves, most computations are done in the shader.
// This is sub-optimal. Improving this is left as an exercise to the reader.
#[derive(Debug)]
pub struct CameraLookAt {
// Object the camera is looking at.
pub center: [f32; 3],
// Angle around the object on the horizontal plane, in radians.
pub angle: f32,
// Height between -1 and 1, 0 is flat, 1 is zenith, -1 is nadir
pub height: f32,
// Distance from center
pub distance: f32,
}

impl Default for CameraLookAt {
fn default() -> Self {
// See object in 0,0,0 from the front top left
CameraLookAt {
center: [0.0, 0.0, 0.0],
angle: 2.0 * std::f32::consts::FRAC_PI_3,
height: 0.3,
distance: f32::sqrt(72.0),
}
}
}

impl CameraLookAt {
/// Pan the camera with middle mouse click, zoom with scroll wheel, orbit with right mouse click.
pub fn update(&mut self, mouse_state: &MouseState, window_size: [f32; 2]) {
// change input mapping for orbit and panning here
let orbit_button = MouseButton::Right;
let translation_button = MouseButton::Middle;

if mouse_state.position_delta[0] != 0.0 || mouse_state.position_delta[1] != 0.0 {
if mouse_state.pressed(orbit_button) {
let delta_x =
mouse_state.position_delta[0] / window_size[0] * std::f32::consts::PI * 2.0;
let delta_y = mouse_state.position_delta[1] / window_size[1] * std::f32::consts::PI;
self.angle += delta_x;
self.height += delta_y;
self.height = self
.height
.max(-std::f32::consts::FRAC_PI_2 + 0.001)
.min(std::f32::consts::FRAC_PI_2 - 0.001);
}

if mouse_state.pressed(translation_button) {
let dir = [self.angle.cos(), self.angle.sin()];
let translation_dir = [-dir[1], dir[0]];
let translation_weight =
mouse_state.position_delta[0] / window_size[0] * self.distance;

self.center[0] += translation_dir[0] * translation_weight;
self.center[2] += translation_dir[1] * translation_weight;
self.center[1] += mouse_state.position_delta[1] / window_size[1] * self.distance;
}
}

if mouse_state.scroll_delta != 0.0 {
self.distance -= mouse_state.scroll_delta * self.distance * 0.2;
// Don't allow zoom to reach 0 or 1e6 to avoid getting stuck / in float precision issue realm.
self.distance = self.distance.max(0.05).min(1e6);
}
}
}
21 changes: 19 additions & 2 deletions lib/src/demo_raymarching.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use wgpu::util::DeviceExt;

use crate::camera_control::CameraLookAt;
use crate::frame_rate::FrameRate;
use crate::program::{Program, ProgramError};
use crate::shader_builder::ShaderBuilder;
Expand Down Expand Up @@ -53,6 +54,7 @@ pub struct DemoRaymarchingProgram {
elapsed: f32, // elapsed take the speed into consideration
frame_rate: FrameRate,
size: [f32; 2],
camera: CameraLookAt,
}

impl Program for DemoRaymarchingProgram {
Expand All @@ -72,6 +74,7 @@ impl Program for DemoRaymarchingProgram {
elapsed: 0.0,
frame_rate: FrameRate::new(200),
size: [0.0, 0.0],
camera: CameraLookAt::default(),
})
}

Expand Down Expand Up @@ -115,7 +118,17 @@ impl Program for DemoRaymarchingProgram {
queue.write_buffer(
&self.render_pass.uniform_buf,
0,
bytemuck::cast_slice(&[self.elapsed, self.size[0], self.size[1], 0.0]),
bytemuck::cast_slice(&[
self.elapsed,
self.size[0],
self.size[1],
self.camera.angle,
self.camera.center[0],
self.camera.center[1],
self.camera.center[2],
self.camera.height,
self.camera.distance,
]),
);
}

Expand Down Expand Up @@ -158,6 +171,10 @@ impl Program for DemoRaymarchingProgram {
ui.separator();
ui.label(std::format!("framerate: {:.0}fps", self.frame_rate.get()));
}

fn get_camera(&mut self) -> Option<&mut crate::camera_control::CameraLookAt> {
Some(&mut self.camera)
}
}

impl DemoRaymarchingProgram {
Expand Down Expand Up @@ -214,7 +231,7 @@ impl DemoRaymarchingProgram {
// create uniform buffer.
let uniforms = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[0.0, 0.0, 0.0, 0.0]),
contents: bytemuck::cast_slice(&[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});

Expand Down
9 changes: 9 additions & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ mod demo_boids;
mod demo_polygon;
mod demo_raymarching;

pub mod camera_control;
mod frame_rate;
pub mod mouse_input;
pub mod program;
pub mod reload_flags;
mod shader_builder;
Expand Down Expand Up @@ -111,3 +113,10 @@ pub fn program_required_downlevel_capabilities() -> wgpu::DownlevelCapabilities
pub fn program_required_limits() -> wgpu::Limits {
CurrentProgram::required_limits()
}

#[no_mangle]
pub fn get_program_camera(
program: &mut CurrentProgram,
) -> Option<&mut crate::camera_control::CameraLookAt> {
program.get_camera()
}
60 changes: 60 additions & 0 deletions lib/src/mouse_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use winit::event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent};

// Naive mouse state.
#[derive(Debug, Default)]
pub struct MouseState {
pub position: [f32; 2],
pub position_delta: [f32; 2],
pub left: bool,
pub right: bool,
pub middle: bool,
pub scroll_delta: f32,
}

impl MouseState {
pub fn pressed(&self, button: MouseButton) -> bool {
match button {
MouseButton::Left => self.left,
MouseButton::Right => self.right,
MouseButton::Middle => self.middle,
_ => false,
}
}

// Called on Event::RedrawRequested since it's the start of a new frame.
pub fn clear_deltas(&mut self) {
self.position_delta = [0.0, 0.0];
self.scroll_delta = 0.0;
}

// Called on relevant window events.
pub fn on_window_event(&mut self, window_event: WindowEvent) {
self.position_delta = [0.0, 0.0];
self.scroll_delta = 0.0;
match window_event {
WindowEvent::MouseInput { button, state, .. } => match button {
MouseButton::Left => self.left = state == ElementState::Pressed,
MouseButton::Right => self.right = state == ElementState::Pressed,
MouseButton::Middle => self.middle = state == ElementState::Pressed,
_ => (),
},
WindowEvent::CursorMoved { position, .. } => {
self.position_delta = [
position.x as f32 - self.position[0],
position.y as f32 - self.position[1],
];
self.position = [position.x as f32, position.y as f32];
}
WindowEvent::MouseWheel { delta, .. } => {
log::info!("delta: {:?}", delta);
match delta {
// native mode: line delta should be 1 or -1
MouseScrollDelta::LineDelta(_, y) => self.scroll_delta = y,
// wasm: pixel delta is around 100 * display_scale
MouseScrollDelta::PixelDelta(pos) => self.scroll_delta = pos.y as f32 / 100.0,
}
}
_ => (),
}
}
}
4 changes: 4 additions & 0 deletions lib/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,8 @@ pub trait Program: Sized {
// These downlevel limits will allow the code to run on all possible hardware
wgpu::Limits::downlevel_webgl2_defaults()
}

fn get_camera(&mut self) -> Option<&mut crate::camera_control::CameraLookAt> {
None
}
}
18 changes: 18 additions & 0 deletions shaders/demo_raymarching/camera.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
fn get_camera_ray(x: f32, y: f32) -> vec3<f32> {
let fov = 60.0;
let xy: vec2<f32> = vec2(x, y) - vec2(uniforms.width, uniforms.height) / 2.0;
let z: f32 = uniforms.height / tan(radians(fov) / 2.0);
return normalize(vec3(xy.x, -xy.y, -z));
}

fn get_view_matrix(eye: vec3<f32>, center: vec3<f32>, up: vec3<f32>) -> mat4x4<f32> {
let f = normalize(center - eye);
let s = normalize(cross(f, up));
let u = cross(s, f);
return mat4x4(
vec4(s, 0.0),
vec4(u, 0.0),
vec4(-f, 0.0),
vec4(-dot(eye, s), -dot(eye, u), dot(eye, f), 1.0)
);
}
5 changes: 4 additions & 1 deletion shaders/demo_raymarching/common.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ struct Uniforms {
elapsed: f32,
width: f32,
height: f32,
_padding: f32, // padding to 16 bytes, required for WebGL.
camera_angle: f32,
camera_center: vec3<f32>,
camera_height: f32,
camera_distance: f32,
};

struct VertexInput {
Expand Down
1 change: 0 additions & 1 deletion shaders/demo_raymarching/draw_2d.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
fn sdf_circle(pos: vec2<f32>, origin: vec2<f32>, radius: f32) -> f32 {
return length(pos - origin) - radius;
}

fn sdf_square(pos: vec2<f32>, origin: vec2<f32>, size: f32, rounding: f32) -> f32 {
let d = abs(pos - origin) - vec2<f32>(size - rounding, size - rounding);
return length(max(d, vec2<f32>(0.0, 0.0))) + min(max(d.x, d.y), 0.0) - rounding;
Expand Down
Loading

0 comments on commit 8e198a5

Please sign in to comment.