Implemented better light rendering and made the sun a point light

This commit is contained in:
Verox001 2025-05-07 14:41:37 +02:00
parent 2b73b0ddce
commit b9729874e2
3 changed files with 107 additions and 33 deletions

View File

@ -49,9 +49,7 @@ pub async fn run() {
let sim = simulator_clone.read().unwrap(); let sim = simulator_clone.read().unwrap();
let bodies = &sim.bodies; let bodies = &sim.bodies;
let sun_pos = (bodies[0].position / 1.496e11); let sun_pos = bodies[0].position / 1.496e11;
// let light_offset = Vector3::new(0.0, 0.0, 0.1);
state.light_manager.clear(); state.light_manager.clear();
state.light_manager.add_light(Light { state.light_manager.add_light(Light {
@ -59,9 +57,9 @@ pub async fn run() {
direction: Vector3::new(0.0, 0.0, 0.0), direction: Vector3::new(0.0, 0.0, 0.0),
color: Vector3::from([1.0, 1.0, 0.8]), color: Vector3::from([1.0, 1.0, 0.8]),
intensity: 5.0, intensity: 5.0,
range: 0.0, range: 1.0,
inner_cutoff: 0.0, inner_cutoff: 0.0,
light_type: LightType::Directional, light_type: LightType::Point,
outer_cutoff: 0.0, outer_cutoff: 0.0,
}); });

View File

@ -52,7 +52,14 @@ pub struct LightManager {
pub lights: Vec<Light>, pub lights: Vec<Light>,
pub buffer: wgpu::Buffer, pub buffer: wgpu::Buffer,
pub bind_group: wgpu::BindGroup, pub bind_group: wgpu::BindGroup,
pub layout: wgpu::BindGroupLayout, pub count_buffer: wgpu::Buffer,
pub layout: wgpu::BindGroupLayout
}
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable, Debug)]
pub struct LightCount {
pub count: u32,
} }
impl LightManager { impl LightManager {
@ -60,31 +67,58 @@ impl LightManager {
let buffer = device.create_buffer(&wgpu::BufferDescriptor { let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Light Buffer"), label: Some("Light Buffer"),
size: (max_lights * size_of::<GpuLight>()) as wgpu::BufferAddress, size: (max_lights * size_of::<GpuLight>()) as wgpu::BufferAddress,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,
}); });
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Light Bind Group Layout"), label: Some("Light Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry { entries: &[
binding: 0, // Binding 0: Storage buffer for lights
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, wgpu::BindGroupLayoutEntry {
ty: wgpu::BindingType::Buffer { binding: 0,
ty: wgpu::BufferBindingType::Uniform, visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
has_dynamic_offset: false, ty: wgpu::BindingType::Buffer {
min_binding_size: None, ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}, },
count: None, // Binding 1: Uniform buffer for light count
}], wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let count_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Light Count Buffer"),
size: size_of::<LightCount>() as wgpu::BufferAddress,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
}); });
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Light Bind Group"), label: Some("Light Bind Group"),
layout: &layout, layout: &layout,
entries: &[wgpu::BindGroupEntry { entries: &[
binding: 0, wgpu::BindGroupEntry {
resource: buffer.as_entire_binding(), binding: 0,
}], resource: buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: count_buffer.as_entire_binding(),
},
],
}); });
Self { Self {
@ -92,6 +126,7 @@ impl LightManager {
buffer, buffer,
bind_group, bind_group,
layout, layout,
count_buffer,
} }
} }
@ -102,6 +137,11 @@ impl LightManager {
pub fn update_gpu(&self, queue: &wgpu::Queue) { pub fn update_gpu(&self, queue: &wgpu::Queue) {
let data: Vec<GpuLight> = self.lights.iter().map(|l| l.to_gpu()).collect(); let data: Vec<GpuLight> = self.lights.iter().map(|l| l.to_gpu()).collect();
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&data)); queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&data));
let count = LightCount {
count: self.lights.len() as u32,
};
queue.write_buffer(&self.count_buffer, 0, bytemuck::bytes_of(&count));
} }
pub fn bind_group(&self) -> &wgpu::BindGroup { pub fn bind_group(&self) -> &wgpu::BindGroup {

View File

@ -21,6 +21,13 @@ struct VSOutput {
@location(3) flags: u32, @location(3) flags: u32,
}; };
struct LightCount {
count: u32,
};
@group(1) @binding(1)
var<uniform> light_count: LightCount;
struct Globals { struct Globals {
view_proj: mat4x4<f32>, view_proj: mat4x4<f32>,
} }
@ -30,12 +37,17 @@ var<uniform> globals: Globals;
struct GpuLight { struct GpuLight {
position: vec3<f32>, position: vec3<f32>,
light_type: u32,
color: vec3<f32>, color: vec3<f32>,
intensity: f32, intensity: f32,
direction: vec3<f32>,
range: f32,
inner_cutoff: f32,
outer_cutoff: f32,
}; };
@group(1) @binding(0) @group(1) @binding(0)
var<uniform> lights: array<GpuLight, 10>; var<storage, read> all_lights: array<GpuLight>;
@vertex @vertex
fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VSOutput { fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VSOutput {
@ -66,22 +78,46 @@ fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VSOutput {
@fragment @fragment
fn fs_main(input: VSOutput) -> @location(0) vec4<f32> { fn fs_main(input: VSOutput) -> @location(0) vec4<f32> {
var lighting: vec3<f32>; var lighting: vec3<f32> = vec3<f32>(0.0);
let always_lit = (input.flags & 0x1u) != 0u; let always_lit = (input.flags & 0x1u) != 0u;
if (always_lit) { if (always_lit) {
return vec4<f32>(input.frag_color * 2.0, 1.0); return vec4<f32>(input.frag_color * 2.0, 1.0);
} else {
lighting = vec3<f32>(0.0);
for (var i = 0u; i < 10u; i = i + 1u) {
let light = lights[i];
let light_dir = normalize(light.position - input.world_pos);
let diff = max(dot(input.normal, light_dir), 0.0);
lighting += light.color * light.intensity * diff;
}
} }
let final_color = input.frag_color * lighting; for (var i = 0u; i < light_count.count; i = i + 1u) {
return vec4<f32>(final_color, 1.0); let light = all_lights[i];
var light_contrib: vec3<f32> = vec3<f32>(0.0);
let light_dir = normalize(light.position - input.world_pos);
let diff = max(dot(input.normal, light_dir), 0.0);
switch light.light_type {
case 0u: { // Directional
light_contrib = light.color * light.intensity * diff;
}
case 1u: { // Point
let dist = distance(light.position, input.world_pos);
if (dist < light.range) {
let attenuation = 1.0 / (dist * dist);
light_contrib = light.color * light.intensity * diff * attenuation;
}
}
case 2u: { // Spot
let spot_dir = normalize(-light.direction);
let angle = dot(spot_dir, light_dir);
if (angle > light.outer_cutoff) {
let intensity = clamp((angle - light.outer_cutoff) / (light.inner_cutoff - light.outer_cutoff), 0.0, 1.0);
let dist = distance(light.position, input.world_pos);
let attenuation = 1.0 / (dist * dist);
light_contrib = light.color * light.intensity * diff * attenuation * intensity;
}
}
default: {}
}
lighting += light_contrib;
}
return vec4<f32>(input.frag_color * lighting, 1.0);
} }