Added directional light from sun
This commit is contained in:
parent
6c0cfbefce
commit
5a444c3e07
@ -1,5 +1,5 @@
|
||||
use cgmath::Rotation3;
|
||||
use solar_engine::{Application, Body, Key, KeyState, Simulator};
|
||||
use cgmath::{Rotation3, Vector3};
|
||||
use solar_engine::{Application, Body, Key, KeyState, Light, LightType, Simulator};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::thread;
|
||||
|
||||
@ -47,14 +47,34 @@ pub async fn run() {
|
||||
let sim = simulator_clone.read().unwrap();
|
||||
let bodies = &sim.bodies;
|
||||
|
||||
let sun_pos = Vector3::new(
|
||||
(bodies[0].position[0] / 1.496e11) as f32,
|
||||
(bodies[0].position[1] / 1.496e11) as f32,
|
||||
0.0,
|
||||
);
|
||||
|
||||
let light_offset = Vector3::new(0.0, 0.0, 0.1);
|
||||
|
||||
state.light_manager.clear();
|
||||
state.light_manager.add_light(Light {
|
||||
position: sun_pos + light_offset,
|
||||
direction: Vector3::new(0.0, 0.0, 0.0),
|
||||
color: Vector3::from([1.0, 1.0, 0.8]),
|
||||
intensity: 5.0,
|
||||
range: 0.0,
|
||||
inner_cutoff: 0.0,
|
||||
light_type: LightType::Directional,
|
||||
outer_cutoff: 0.0,
|
||||
});
|
||||
|
||||
let instances = bodies
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, b)| {
|
||||
solar_engine::RenderInstance {
|
||||
position: cgmath::Vector3::new(
|
||||
(b.position[0] / 1.496e11) as f32,
|
||||
(b.position[1] / 1.496e11) as f32,
|
||||
position: Vector3::new(
|
||||
((b.position[0] / 1.496e11) as f32) - sun_pos.x,
|
||||
((b.position[1] / 1.496e11) as f32) - sun_pos.y,
|
||||
0.0,
|
||||
),
|
||||
rotation: cgmath::Quaternion::from_angle_z(cgmath::Deg(0.0)),
|
||||
|
||||
@ -5,6 +5,7 @@ mod render;
|
||||
mod application;
|
||||
mod input;
|
||||
mod camera;
|
||||
mod light;
|
||||
|
||||
pub use body::Body;
|
||||
|
||||
@ -21,4 +22,7 @@ pub use state::State;
|
||||
pub use input::Key;
|
||||
pub use input::map_winit_key;
|
||||
pub use input::InputEvent;
|
||||
pub use input::KeyState;
|
||||
pub use input::KeyState;
|
||||
|
||||
pub use light::Light;
|
||||
pub use light::LightType;
|
||||
118
solar_engine/src/light.rs
Normal file
118
solar_engine/src/light.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use cgmath::Vector3;
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum LightType {
|
||||
Directional = 0,
|
||||
Point = 1,
|
||||
Spot = 2
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Pod, Zeroable, Debug)]
|
||||
pub struct GpuLight {
|
||||
pub position: [f32; 3],
|
||||
pub light_type: u32,
|
||||
pub color: [f32; 3],
|
||||
pub intensity: f32,
|
||||
pub direction: [f32; 3],
|
||||
pub range: f32,
|
||||
pub inner_cutoff: f32,
|
||||
pub outer_cutoff: f32,
|
||||
}
|
||||
|
||||
pub struct Light {
|
||||
pub light_type: LightType,
|
||||
pub position: Vector3<f32>,
|
||||
pub direction: Vector3<f32>,
|
||||
pub color: Vector3<f32>,
|
||||
pub intensity: f32,
|
||||
pub range: f32,
|
||||
pub inner_cutoff: f32,
|
||||
pub outer_cutoff: f32,
|
||||
}
|
||||
|
||||
impl Light {
|
||||
pub fn to_gpu(&self) -> GpuLight {
|
||||
GpuLight {
|
||||
position: self.position.into(),
|
||||
light_type: self.light_type as u32,
|
||||
color: self.color.into(),
|
||||
intensity: self.intensity,
|
||||
direction: self.direction.into(),
|
||||
range: self.range,
|
||||
inner_cutoff: self.inner_cutoff,
|
||||
outer_cutoff: self.outer_cutoff,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LightManager {
|
||||
pub lights: Vec<Light>,
|
||||
pub buffer: wgpu::Buffer,
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
pub layout: wgpu::BindGroupLayout,
|
||||
}
|
||||
|
||||
impl LightManager {
|
||||
pub fn new(device: &wgpu::Device, max_lights: usize) -> Self {
|
||||
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,
|
||||
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,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
});
|
||||
|
||||
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(),
|
||||
}],
|
||||
});
|
||||
|
||||
Self {
|
||||
lights: Vec::new(),
|
||||
buffer,
|
||||
bind_group,
|
||||
layout,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_light(&mut self, light: Light) {
|
||||
self.lights.push(light);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
pub fn bind_group(&self) -> &wgpu::BindGroup {
|
||||
&self.bind_group
|
||||
}
|
||||
|
||||
pub fn layout(&self) -> &wgpu::BindGroupLayout {
|
||||
&self.layout
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.lights.clear();
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,6 @@ pub struct Globals {
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Shape {
|
||||
Polygon,
|
||||
Circle,
|
||||
Sphere,
|
||||
}
|
||||
@ -74,11 +73,12 @@ impl SampleCount {
|
||||
pub struct Vertex {
|
||||
pub(crate) position: [f32; 3],
|
||||
pub(crate) color: [f32; 3],
|
||||
pub(crate) normal: [f32; 3],
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
const ATTRIBS: [wgpu::VertexAttribute; 2] =
|
||||
wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3];
|
||||
const ATTRIBS: [wgpu::VertexAttribute; 3] =
|
||||
wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Float32x3];
|
||||
|
||||
pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||
wgpu::VertexBufferLayout {
|
||||
@ -90,14 +90,22 @@ impl Vertex {
|
||||
}
|
||||
|
||||
pub fn create_circle_vertices(segment_count: usize, radius: f32, color: [f32; 3]) -> (Vec<Vertex>, Vec<u16>) {
|
||||
let mut vertices = vec![Vertex { position: [0.0, 0.0, 0.0], color }];
|
||||
let mut vertices = vec![Vertex {
|
||||
position: [0.0, 0.0, 0.0],
|
||||
color,
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
}];
|
||||
let mut indices = vec![];
|
||||
|
||||
for i in 0..=segment_count {
|
||||
let theta = (i as f32) / (segment_count as f32) * std::f32::consts::TAU;
|
||||
let x = radius * theta.cos();
|
||||
let y = radius * theta.sin();
|
||||
vertices.push(Vertex { position: [x, y, 0.0], color });
|
||||
vertices.push(Vertex {
|
||||
position: [x, y, 0.0],
|
||||
color,
|
||||
normal: [0.0, 0.0, 1.0],
|
||||
});
|
||||
}
|
||||
|
||||
for i in 1..=segment_count {
|
||||
@ -123,9 +131,12 @@ pub fn create_sphere_vertices(stacks: usize, slices: usize, radius: f32, color:
|
||||
let x = r * theta.cos();
|
||||
let z = r * theta.sin();
|
||||
|
||||
let normal = [x, y, z];
|
||||
|
||||
vertices.push(Vertex {
|
||||
position: [x * radius, y * radius, z * radius],
|
||||
color,
|
||||
normal,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) color: vec3<f32>,
|
||||
@location(2) normal: vec3<f32>,
|
||||
};
|
||||
|
||||
struct InstanceInput {
|
||||
@ -13,7 +14,9 @@ struct InstanceInput {
|
||||
|
||||
struct VSOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) color: vec3<f32>,
|
||||
@location(0) frag_color: vec3<f32>,
|
||||
@location(1) world_pos: vec3<f32>,
|
||||
@location(2) normal: vec3<f32>,
|
||||
};
|
||||
|
||||
struct Globals {
|
||||
@ -23,21 +26,52 @@ struct Globals {
|
||||
@group(0) @binding(0)
|
||||
var<uniform> globals: Globals;
|
||||
|
||||
struct GpuLight {
|
||||
position: vec3<f32>,
|
||||
color: vec3<f32>,
|
||||
intensity: f32,
|
||||
};
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> lights: array<GpuLight, 10>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VSOutput {
|
||||
var out: VSOutput;
|
||||
|
||||
let model = mat4x4<f32>(
|
||||
instance.model_row0,
|
||||
instance.model_row1,
|
||||
instance.model_row2,
|
||||
instance.model_row3
|
||||
);
|
||||
out.position = globals.view_proj * model * vec4<f32>(vertex.position, 1.0);
|
||||
out.color = instance.color;
|
||||
|
||||
let world_position = (model * vec4<f32>(vertex.position, 1.0)).xyz;
|
||||
let normal_matrix = mat3x3<f32>(
|
||||
instance.model_row0.xyz,
|
||||
instance.model_row1.xyz,
|
||||
instance.model_row2.xyz
|
||||
);
|
||||
|
||||
out.position = globals.view_proj * vec4<f32>(world_position, 1.0);
|
||||
out.frag_color = instance.color * vertex.color;
|
||||
out.world_pos = world_position;
|
||||
out.normal = normalize(normal_matrix * vertex.normal);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(input: VSOutput) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(input.color, 1.0);
|
||||
var lighting: vec3<f32> = 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);
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ use wgpu::{Adapter, Device, Instance, PresentMode, Queue, Surface, SurfaceCapabi
|
||||
use winit::dpi::PhysicalSize;
|
||||
use winit::window::{Window};
|
||||
use crate::camera::Camera;
|
||||
use crate::light::{GpuLight, LightManager};
|
||||
use crate::render::{create_circle_vertices, create_sphere_vertices, Geometry, Globals, InstanceRaw, RenderInstance, SampleCount, Shape, Vertex};
|
||||
|
||||
pub struct State<'a> {
|
||||
@ -32,6 +33,8 @@ pub struct State<'a> {
|
||||
|
||||
camera: Camera,
|
||||
depth_texture: wgpu::TextureView,
|
||||
|
||||
pub light_manager: LightManager,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
@ -83,7 +86,9 @@ impl<'a> State<'a> {
|
||||
label: Some("Global Bind Group"),
|
||||
});
|
||||
|
||||
let render_pipeline = Self::create_render_pipeline(&device, &config, sample_count.0, &global_bind_group_layout);
|
||||
let light_manager = LightManager::new(&device, 10);
|
||||
|
||||
let render_pipeline = Self::create_render_pipeline(&device, &config, sample_count.0, &global_bind_group_layout, &light_manager);
|
||||
let geometries = Self::create_geometries(&device);
|
||||
|
||||
let instances = vec![];
|
||||
@ -112,9 +117,15 @@ impl<'a> State<'a> {
|
||||
instance_buffer,
|
||||
camera,
|
||||
depth_texture,
|
||||
light_manager
|
||||
}
|
||||
}
|
||||
|
||||
fn update_lights(&mut self) {
|
||||
let light_data: Vec<GpuLight> = self.light_manager.lights.iter().map(|l| l.to_gpu()).collect();
|
||||
self.queue.write_buffer(&self.light_manager.buffer, 0, bytemuck::cast_slice(&light_data));
|
||||
}
|
||||
|
||||
fn create_depth_texture(device: &Device, width: u32, height: u32, sample_count: u32) -> wgpu::TextureView {
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("Depth Texture"),
|
||||
@ -136,18 +147,6 @@ impl<'a> State<'a> {
|
||||
fn create_geometries(device: &Device) -> HashMap<Shape, Geometry> {
|
||||
let mut geometries = HashMap::new();
|
||||
|
||||
let polygon_vertices = vec![
|
||||
Vertex { position: [-0.0868241, 0.49240386, 0.0], color: [0.5, 0.0, 0.5] },
|
||||
Vertex { position: [-0.49513406, 0.06958647, 0.0], color: [0.5, 0.0, 0.5] },
|
||||
Vertex { position: [-0.21918549, -0.44939706, 0.0], color: [0.5, 0.0, 0.5] },
|
||||
Vertex { position: [0.35966998, -0.3473291, 0.0], color: [0.5, 0.0, 0.5] },
|
||||
Vertex { position: [0.44147372, 0.2347359, 0.0], color: [0.5, 0.0, 0.5] },
|
||||
];
|
||||
let polygon_indices = vec![0, 1, 4, 1, 2, 4, 2, 3, 4];
|
||||
|
||||
let polygon_geometry = Self::create_geometry(device, &polygon_vertices, &polygon_indices);
|
||||
geometries.insert(Shape::Polygon, polygon_geometry);
|
||||
|
||||
let (circle_vertices, circle_indices) = create_circle_vertices(512, 0.5, [0.5, 0.5, 0.5]);
|
||||
let circle_geometry = Self::create_geometry(device, &circle_vertices, &circle_indices);
|
||||
geometries.insert(Shape::Circle, circle_geometry);
|
||||
@ -207,7 +206,7 @@ impl<'a> State<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn create_render_pipeline(device: &Device, config: &SurfaceConfiguration, sample_count: u32, global_bind_group_layout: &wgpu::BindGroupLayout) -> wgpu::RenderPipeline {
|
||||
fn create_render_pipeline(device: &Device, config: &SurfaceConfiguration, sample_count: u32, global_bind_group_layout: &wgpu::BindGroupLayout, light_manager: &LightManager) -> wgpu::RenderPipeline {
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
|
||||
@ -216,7 +215,7 @@ impl<'a> State<'a> {
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Render Pipeline Layout"),
|
||||
bind_group_layouts: &[&global_bind_group_layout],
|
||||
bind_group_layouts: &[&global_bind_group_layout, &light_manager.layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
@ -393,6 +392,10 @@ impl<'a> State<'a> {
|
||||
|
||||
render_pass.set_pipeline(&self.render_pipeline);
|
||||
render_pass.set_bind_group(0, &self.global_bind_group, &[]);
|
||||
|
||||
// Update the light manager buffer
|
||||
self.light_manager.update_gpu(&self.queue);
|
||||
render_pass.set_bind_group(1, &self.light_manager.bind_group, &[]);
|
||||
|
||||
for shape in self.geometries.keys().copied() {
|
||||
let geometry = &self.geometries[&shape];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user