Implement a single point light with blinn-phong lighting

This commit is contained in:
SeanOMik 2023-11-10 09:14:18 -05:00
parent 988fd6bf42
commit e95a45fd53
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
10 changed files with 191 additions and 17 deletions

10
Cargo.lock generated
View File

@ -1142,6 +1142,15 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.9"
@ -1290,6 +1299,7 @@ dependencies = [
"glam",
"image",
"instant",
"itertools",
"lyra-resource",
"petgraph",
"quote",

3
Cargo.toml Executable file → Normal file
View File

@ -37,4 +37,5 @@ aligned-vec = "0.5.0"
tracing-appender = "0.2.2"
stopwatch = "0.0.7"
petgraph = "0.6.4"
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
itertools = "0.11.0"

View File

@ -20,7 +20,7 @@ pub struct FreeFlyCamera {
impl Default for FreeFlyCamera {
fn default() -> Self {
Self {
speed: 3.0,
speed: 4.0,
look_speed: 0.09,
mouse_sensitivity: 0.4,
look_with_keys: false,

View File

@ -35,6 +35,23 @@ impl Projection {
}
}
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct CameraUniform {
pub view_proj: glam::Mat4,
// vec4 is used because of the uniforms 16 byte spacing requirement
pub view_pos: glam::Vec4,
}
impl Default for CameraUniform {
fn default() -> Self {
Self {
view_proj: glam::Mat4::IDENTITY,
view_pos: Default::default()
}
}
}
#[derive(Debug, Clone)]
pub struct RenderCamera {
view_proj: glam::Mat4,

13
src/render/light/mod.rs Normal file
View File

@ -0,0 +1,13 @@
pub mod point;
pub use point::*;
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct LightUniform {
pub position: glam::Vec3,
// Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here
pub(crate) _padding: u32,
pub color: glam::Vec3,
// Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here
pub(crate) _padding2: u32,
}

View File

@ -0,0 +1,6 @@
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct PointLight {
position: glam::Vec3,
color: glam::Vec3,
}

View File

@ -10,4 +10,5 @@ pub mod shader_loader;
pub mod material;
pub mod camera;
pub mod window;
pub mod transform_buffer_storage;
pub mod transform_buffer_storage;
pub mod light;

View File

@ -8,6 +8,7 @@ use edict::query::EpochOf;
use edict::{EntityId, Entities};
use glam::Vec3;
use instant::Instant;
use itertools::izip;
use tracing::{debug, warn};
use wgpu::{BindGroup, BindGroupLayout, Limits};
use wgpu::util::DeviceExt;
@ -16,10 +17,11 @@ use winit::window::Window;
use crate::ecs::components::camera::CameraComponent;
use crate::ecs::components::model::ModelComponent;
use crate::ecs::components::transform::TransformComponent;
use crate::math::Transform;
use crate::math::{Transform, self};
use super::camera::RenderCamera;
use super::camera::{RenderCamera, CameraUniform};
use super::desc_buf_lay::DescVertexBufferLayout;
use super::light::LightUniform;
use super::texture::RenderTexture;
use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers};
use super::vertex::Vertex;
@ -87,6 +89,11 @@ pub struct BasicRenderer {
texture_bind_group_layout: BindGroupLayout,
default_texture_bind_group: BindGroup,
depth_buffer_texture: RenderTexture,
point_light_uniform: LightUniform,
point_light_buffer: wgpu::Buffer,
point_light_bind_group_layout: BindGroupLayout,
point_light_bind_group: BindGroup,
}
impl BasicRenderer {
@ -236,10 +243,51 @@ impl BasicRenderer {
next_indices: TransformBufferIndices { buffer_index: 0, transform_index: 0 },
};
let point_light_uniform = LightUniform {
position: glam::Vec3::new(0.0, -2.0, -13.0),
_padding: 0,
color: glam::Vec3::new(1.0, 1.0, 1.0),
_padding2: 0,
};
let point_light_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Point Light Buffer"),
contents: bytemuck::cast_slice(&[glam::Mat4::IDENTITY]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}
);
let point_light_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
],
label: Some("point_light_bind_group_layout"),
});
let point_light_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &point_light_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: point_light_buffer.as_entire_binding(),
}
],
label: Some("point_light_bind_group"),
});
let camera_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[glam::Mat4::IDENTITY]),
contents: bytemuck::cast_slice(&[CameraUniform::default()]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}
);
@ -248,7 +296,7 @@ impl BasicRenderer {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
@ -323,13 +371,19 @@ impl BasicRenderer {
default_texture_bind_group: default_tex_bindgroup,
depth_buffer_texture: depth_texture,
entity_last_transforms: HashMap::new(),
point_light_uniform,
point_light_buffer,
point_light_bind_group_layout,
point_light_bind_group,
};
// create the default pipelines
let mut pipelines = HashMap::new();
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
vec![super::vertex::Vertex::desc(),],
vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout])));
vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout,
&s.point_light_bind_group_layout])));
s.render_pipelines = pipelines;
s
@ -376,12 +430,14 @@ impl BasicRenderer {
let positions = mesh.position().unwrap();
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
.unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]);
let normals = mesh.normals().unwrap();
assert!(positions.len() == tex_coords.len());
assert!(positions.len() == tex_coords.len() && positions.len() == normals.len());
let vertex_inputs: Vec<Vertex> = std::iter::zip(positions, tex_coords)
.map(|(v, t)| Vertex::new(*v, t))
.collect();
let mut vertex_inputs = vec![];
for (v, t, n) in izip!(positions.iter(), tex_coords.iter(), normals.iter()) {
vertex_inputs.push(Vertex::new(*v, *t, *n));
}
let vertex_buffer = self.device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
@ -588,10 +644,22 @@ impl Renderer for BasicRenderer {
if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
let view_proj = self.inuse_camera.update_view_projection(camera);
self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[*view_proj]));
let pos = camera.transform.translation;
let uniform = CameraUniform {
view_proj: *view_proj,
view_pos: glam::Vec4::new(pos.x, pos.y, pos.z, 0.0),
};
self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[uniform]));
} else {
warn!("Missing camera!");
}
{
let old_pos = self.point_light_uniform.position;
self.point_light_uniform.position = glam::Quat::from_axis_angle(glam::Vec3::Z,
math::Angle::Degrees(1.0).to_radians()) * old_pos;
self.queue.write_buffer(&self.point_light_buffer, 0, bytemuck::cast_slice(&[self.point_light_uniform]));
}
}
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
@ -650,6 +718,9 @@ impl Renderer for BasicRenderer {
// Bind camera
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
// bind light
render_pass.set_bind_group(3, &self.point_light_bind_group, &[]);
// if this mesh uses indices, use them to draw the mesh
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {

View File

@ -3,15 +3,24 @@
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) tex_coords: vec2<f32>,
@location(2) normal: vec3<f32>,
}
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) tex_coords: vec2<f32>,
@location(1) world_position: vec3<f32>,
@location(2) world_normal: vec3<f32>,
}
struct CameraUniform {
view_proj: mat4x4<f32>,
view_pos: vec4<f32>,
};
struct PointLight {
position: vec3<f32>,
color: vec3<f32>,
};
@group(1) @binding(0)
@ -20,13 +29,25 @@ var<uniform> u_model_transform: mat4x4<f32>;
@group(2) @binding(0)
var<uniform> camera: CameraUniform;
@group(3) @binding(0)
var<uniform> point_light: PointLight;
@vertex
fn vs_main(
model: VertexInput,
) -> VertexOutput {
var out: VertexOutput;
out.tex_coords = model.tex_coords;
out.clip_position = camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0);
out.world_normal = (u_model_transform * vec4<f32>(model.normal, 0.0)).xyz;
//out.world_normal = model.normal;
var world_position: vec4<f32> = u_model_transform * vec4<f32>(model.position, 1.0);
out.world_position = world_position.xyz;
return out;
}
@ -37,7 +58,35 @@ var t_diffuse: texture_2d<f32>;
@group(0)@binding(1)
var s_diffuse: sampler;
//@group(3) @binding(0)
//var<uniform> point_light: PointLight;
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return textureSample(t_diffuse, s_diffuse, in.tex_coords);
//return textureSample(t_diffuse, s_diffuse, in.tex_coords);
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
// We don't need (or want) much ambient light, so 0.1 is fine
let ambient_strength = 0.1;
let ambient_color = point_light.color * ambient_strength;
//// diffuse ////
let light_dir = normalize(point_light.position - in.world_position);
let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0);
let diffuse_color = point_light.color * diffuse_strength;
//// end of diffuse ////
//// specular ////
let view_dir = normalize(camera.view_pos.xyz - in.world_position);
let half_dir = normalize(view_dir + light_dir);
let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0);
let specular_color = specular_strength * point_light.color;
//// end of specular ////
let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz;
return vec4<f32>(result, object_color.a);
}

View File

@ -4,14 +4,15 @@ use super::desc_buf_lay::DescVertexBufferLayout;
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex {
pub position: glam::Vec3,
pub tex_coords: glam::Vec2
pub tex_coords: glam::Vec2,
pub normals: glam::Vec3,
//pub color: [f32; 3], // TODO: add color again
}
impl Vertex {
pub fn new(position: glam::Vec3, tex_coords: glam::Vec2) -> Self {
pub fn new(position: glam::Vec3, tex_coords: glam::Vec2, normals: glam::Vec3) -> Self {
Self {
position, tex_coords
position, tex_coords, normals
}
}
}
@ -31,6 +32,11 @@ impl DescVertexBufferLayout for Vertex {
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 1,
format: wgpu::VertexFormat::Float32x2, // Vec2
},
wgpu::VertexAttribute {
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
shader_location: 2,
format: wgpu::VertexFormat::Float32x3, // Vec3
}
]
}