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

View File

@ -52,7 +52,14 @@ pub struct LightManager {
pub lights: Vec<Light>,
pub buffer: wgpu::Buffer,
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 {
@ -60,31 +67,58 @@ impl LightManager {
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Light Buffer"),
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,
});
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Light Bind Group Layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
entries: &[
// Binding 0: Storage buffer for lights
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
ty: wgpu::BindingType::Buffer {
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 {
label: Some("Light Bind Group"),
layout: &layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: count_buffer.as_entire_binding(),
},
],
});
Self {
@ -92,6 +126,7 @@ impl LightManager {
buffer,
bind_group,
layout,
count_buffer,
}
}
@ -102,6 +137,11 @@ impl LightManager {
pub fn update_gpu(&self, queue: &wgpu::Queue) {
let data: Vec<GpuLight> = self.lights.iter().map(|l| l.to_gpu()).collect();
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 {

View File

@ -21,6 +21,13 @@ struct VSOutput {
@location(3) flags: u32,
};
struct LightCount {
count: u32,
};
@group(1) @binding(1)
var<uniform> light_count: LightCount;
struct Globals {
view_proj: mat4x4<f32>,
}
@ -30,12 +37,17 @@ var<uniform> globals: Globals;
struct GpuLight {
position: vec3<f32>,
light_type: u32,
color: vec3<f32>,
intensity: f32,
direction: vec3<f32>,
range: f32,
inner_cutoff: f32,
outer_cutoff: f32,
};
@group(1) @binding(0)
var<uniform> lights: array<GpuLight, 10>;
var<storage, read> all_lights: array<GpuLight>;
@vertex
fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VSOutput {
@ -66,22 +78,46 @@ fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VSOutput {
@fragment
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;
if (always_lit) {
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;
return vec4<f32>(final_color, 1.0);
for (var i = 0u; i < light_count.count; i = i + 1u) {
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);
}