Spotlight feature

master
Wynd 2024-11-07 01:12:53 +02:00
parent 79c10f6d95
commit 9ea1ab3b5b
6 changed files with 219 additions and 45 deletions

View File

@ -1,4 +1,7 @@
use winit::event::{MouseButton, MouseScrollDelta, WindowEvent}; use winit::{
event::{ElementState, MouseButton, MouseScrollDelta, WindowEvent},
keyboard::{Key, NamedKey},
};
use crate::camera::Camera; use crate::camera::Camera;
@ -12,9 +15,10 @@ pub struct CameraController {
cursor_start: (f32, f32), cursor_start: (f32, f32),
movement: (f32, f32), movement: (f32, f32),
is_moving: bool, is_moving: bool,
is_ctrl_pressed: bool,
movement_total: (f32, f32), movement_total: (f32, f32),
zoom_total: f32, pub zoom_total: f32,
} }
impl CameraController { impl CameraController {
@ -27,6 +31,7 @@ impl CameraController {
cursor_start: (0.0, 0.0), cursor_start: (0.0, 0.0),
movement: (0.0, 0.0), movement: (0.0, 0.0),
is_moving: false, is_moving: false,
is_ctrl_pressed: false,
movement_total: (0.0, 0.0), movement_total: (0.0, 0.0),
zoom_total: 1.0, zoom_total: 1.0,
@ -80,20 +85,33 @@ impl CameraController {
} }
WindowEvent::MouseWheel { delta, .. } => match delta { WindowEvent::MouseWheel { delta, .. } => match delta {
MouseScrollDelta::LineDelta(_, y) => { MouseScrollDelta::LineDelta(_, y) => {
if !self.is_ctrl_pressed {
self.zoom_total += y; self.zoom_total += y;
if self.zoom_total >= 0.0 && self.zoom_total <= MAX_ZOOM_LEVEL { if self.zoom_total >= 0.0 && self.zoom_total <= MAX_ZOOM_LEVEL {
self.zoom = *y; self.zoom = *y;
} }
self.zoom_total = self.zoom_total.clamp(0.0, MAX_ZOOM_LEVEL); self.zoom_total = self.zoom_total.clamp(0.0, MAX_ZOOM_LEVEL);
}
true true
} }
_ => false, _ => false,
}, },
WindowEvent::KeyboardInput { event, .. } => {
self.is_ctrl_pressed = false;
if event.state == ElementState::Pressed {
let key = &event.logical_key;
if let Key::Named(NamedKey::Control) = key {
self.is_ctrl_pressed = true;
}
}
true
}
_ => false, _ => false,
} }
} }
pub fn update_camera(&mut self, camera: &mut Camera) { pub fn update(&mut self, camera: &mut Camera) {
use cgmath::InnerSpace; use cgmath::InnerSpace;
let forward = camera.target - camera.eye; let forward = camera.target - camera.eye;

View File

@ -4,6 +4,7 @@ use winit::event_loop::{ControlFlow, EventLoop};
pub mod app; pub mod app;
pub mod camera; pub mod camera;
pub mod controller; pub mod controller;
pub mod spotlight;
pub mod state; pub mod state;
pub mod texture; pub mod texture;
pub mod vertex; pub mod vertex;

View File

@ -29,12 +29,27 @@ fn vs_main(
// Fragment shader // Fragment shader
struct SpotlightUniform {
cursor_position: vec2<f32>,
size: f32,
zoom_level: f32,
strength: f32
}
@group(0) @binding(0) @group(0) @binding(0)
var t_diffuse: texture_2d<f32>; var t_diffuse: texture_2d<f32>;
@group(0) @binding(1) @group(0) @binding(1)
var s_diffuse: sampler; var s_diffuse: sampler;
@group(2) @binding(0)
var<uniform> spotlight: SpotlightUniform;
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return textureSample(t_diffuse, s_diffuse, in.tex_coords); var cursor4 = vec4<f32>(spotlight.cursor_position, 0.0, 1.0);
var texColor = textureSample(t_diffuse, s_diffuse, in.tex_coords);
var shadowColor = vec4<f32>(0.0, 0.0, 0.0, 1.0);
var dist_check = length(cursor4 - in.position) < (spotlight.size * spotlight.zoom_level);
var spotlightColor = select(spotlight.strength, 0.0, dist_check);
var finalColor = mix(texColor, shadowColor, spotlightColor);
return finalColor;
} }

124
src/spotlight.rs 100644
View File

@ -0,0 +1,124 @@
use wgpu::util::DeviceExt;
use winit::{
event::{ElementState, MouseScrollDelta, WindowEvent},
keyboard::{Key, NamedKey},
};
use crate::controller::CameraController;
pub struct Spotlight {
cursor_position: (f32, f32),
size: f32,
is_ctrl_pressed: bool,
}
pub struct SpotlightInfo {
pub uniform: SpotlightUniform,
pub buffer: wgpu::Buffer,
pub layout: wgpu::BindGroupLayout,
pub bind_group: wgpu::BindGroup,
}
impl Spotlight {
pub fn new(device: &wgpu::Device) -> (Self, SpotlightInfo) {
let spotlight = Self {
cursor_position: (960.0, 540.0),
size: 50.0,
is_ctrl_pressed: false,
};
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
label: Some("spotlight_bind_group_layout"),
});
let uniform = SpotlightUniform {
cursor_position: [0.0, 0.0],
size: 50.0,
zoom_level: 1.0,
strength: 0.0,
padding: 0.0,
};
let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Spotlight Buffer"),
contents: bytemuck::cast_slice(&[uniform]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
label: Some("spotlight_bind_group"),
});
let info = SpotlightInfo {
layout,
bind_group,
buffer,
uniform,
};
(spotlight, info)
}
pub fn process_events(&mut self, event: &WindowEvent) {
match event {
WindowEvent::CursorMoved { position, .. } => {
self.cursor_position = (position.x as f32, position.y as f32);
}
WindowEvent::KeyboardInput { event, .. } => {
self.is_ctrl_pressed = false;
if event.state == ElementState::Pressed {
let key = &event.logical_key;
if let Key::Named(NamedKey::Control) = key {
self.is_ctrl_pressed = true;
}
}
}
WindowEvent::MouseWheel { delta, .. } => {
if let MouseScrollDelta::LineDelta(_, y) = delta {
if self.is_ctrl_pressed {
self.size += y * 5.0;
}
}
}
_ => {}
}
}
pub fn update(&mut self) {}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct SpotlightUniform {
cursor_position: [f32; 2],
size: f32,
zoom_level: f32,
strength: f32,
padding: f32,
}
impl SpotlightUniform {
pub fn update(&mut self, spotlight: &Spotlight, controller: &CameraController) {
self.cursor_position = [spotlight.cursor_position.0, spotlight.cursor_position.1];
self.size = spotlight.size;
self.zoom_level = controller.zoom_total.max(1.0);
self.strength = if spotlight.is_ctrl_pressed { 0.9 } else { 0.0 };
}
}

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use pollster::FutureExt; use pollster::FutureExt;
use wgpu::util::DeviceExt; use wgpu::{util::DeviceExt, DynamicOffset};
use winit::{ use winit::{
event::{ElementState, WindowEvent}, event::{ElementState, WindowEvent},
keyboard::Key, keyboard::Key,
@ -11,6 +11,7 @@ use winit::{
use crate::{ use crate::{
camera::{Camera, CameraInfo}, camera::{Camera, CameraInfo},
controller::CameraController, controller::CameraController,
spotlight::{Spotlight, SpotlightInfo},
texture::Texture, texture::Texture,
vertex::Vertex, vertex::Vertex,
}; };
@ -54,9 +55,11 @@ pub struct State<'a> {
render_pipeline: wgpu::RenderPipeline, render_pipeline: wgpu::RenderPipeline,
vertex_buffer: wgpu::Buffer, vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer, index_buffer: wgpu::Buffer,
bind_group: wgpu::BindGroup, texture_bind_group: wgpu::BindGroup,
pub camera: Camera, pub camera: Camera,
pub camera_info: CameraInfo, pub camera_info: CameraInfo,
pub spotlight: Spotlight,
pub spotlight_info: SpotlightInfo,
pub zoom: f32, pub zoom: f32,
pub camera_controller: CameraController, pub camera_controller: CameraController,
} }
@ -117,6 +120,8 @@ impl<'a> State<'a> {
let (camera, camera_info) = Camera::new(&config, &device); let (camera, camera_info) = Camera::new(&config, &device);
let camera_controller = CameraController::new(ZOOM_SPEED, MOVE_SPEED); let camera_controller = CameraController::new(ZOOM_SPEED, MOVE_SPEED);
let (spotlight, spotlight_info) = Spotlight::new(&device);
let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl")); let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
let texture = Texture::new(image, &device, &queue); let texture = Texture::new(image, &device, &queue);
@ -124,7 +129,7 @@ impl<'a> State<'a> {
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[&texture.layout, &camera_info.layout], bind_group_layouts: &[&texture.layout, &camera_info.layout, &spotlight_info.layout],
push_constant_ranges: &[], push_constant_ranges: &[],
}); });
@ -192,9 +197,11 @@ impl<'a> State<'a> {
render_pipeline, render_pipeline,
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
bind_group: texture.group, texture_bind_group: texture.bind_group,
camera, camera,
camera_info, camera_info,
spotlight,
spotlight_info,
camera_controller, camera_controller,
zoom: 1.0, zoom: 1.0,
} }
@ -212,6 +219,7 @@ impl<'a> State<'a> {
pub fn input(&mut self, event: &winit::event::WindowEvent) -> bool { pub fn input(&mut self, event: &winit::event::WindowEvent) -> bool {
self.camera_controller.process_events(self.wsize, event); self.camera_controller.process_events(self.wsize, event);
self.spotlight.process_events(event);
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
event: key_event, .. event: key_event, ..
@ -232,13 +240,24 @@ impl<'a> State<'a> {
} }
pub fn update(&mut self) { pub fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update(&mut self.camera);
self.camera_info.uniform.update_view_proj(&self.camera); self.camera_info.uniform.update_view_proj(&self.camera);
self.queue.write_buffer( self.queue.write_buffer(
&self.camera_info.buffer, &self.camera_info.buffer,
0, 0,
bytemuck::cast_slice(&[self.camera_info.uniform]), bytemuck::cast_slice(&[self.camera_info.uniform]),
); );
self.spotlight.update();
self.spotlight_info
.uniform
.update(&self.spotlight, &self.camera_controller);
self.queue.write_buffer(
&self.spotlight_info.buffer,
0,
bytemuck::cast_slice(&[self.spotlight_info.uniform]),
);
} }
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> { pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
@ -274,8 +293,9 @@ impl<'a> State<'a> {
}); });
render_pass.set_pipeline(&self.render_pipeline); render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_bind_group(0, &self.bind_group, &[]); render_pass.set_bind_group(0, &self.texture_bind_group, &[]);
render_pass.set_bind_group(1, &self.camera_info.bind_group, &[]); render_pass.set_bind_group(1, &self.camera_info.bind_group, &[]);
render_pass.set_bind_group(2, &self.spotlight_info.bind_group, &[]);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);

View File

@ -2,7 +2,7 @@ use image::GenericImageView;
pub struct Texture { pub struct Texture {
pub layout: wgpu::BindGroupLayout, pub layout: wgpu::BindGroupLayout,
pub group: wgpu::BindGroup, pub bind_group: wgpu::BindGroup,
} }
impl Texture { impl Texture {
@ -53,8 +53,7 @@ impl Texture {
..Default::default() ..Default::default()
}); });
let texture_bind_group_layout = let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -77,7 +76,7 @@ impl Texture {
}); });
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
@ -91,9 +90,6 @@ impl Texture {
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
}); });
Self { Self { layout, bind_group }
layout: texture_bind_group_layout,
group: bind_group,
}
} }
} }