diff options
-rw-r--r-- | engine/fragment-color.glsl | 21 | ||||
-rw-r--r-- | engine/fragment-light.glsl | 7 | ||||
-rw-r--r-- | engine/fragment-texture.glsl | 22 | ||||
-rw-r--r-- | engine/light.glsl | 34 | ||||
-rw-r--r-- | engine/src/color.rs | 5 | ||||
-rw-r--r-- | engine/src/lib.rs | 46 | ||||
-rw-r--r-- | engine/src/lighting.rs | 93 | ||||
-rw-r--r-- | engine/src/math.rs | 17 | ||||
-rw-r--r-- | engine/src/opengl/shader.rs | 36 | ||||
-rw-r--r-- | engine/src/renderer/mod.rs | 65 | ||||
-rw-r--r-- | engine/src/vector.rs | 22 | ||||
-rw-r--r-- | engine/src/vertex.rs | 20 | ||||
-rw-r--r-- | engine/vertex.glsl | 7 |
13 files changed, 390 insertions, 5 deletions
diff --git a/engine/fragment-color.glsl b/engine/fragment-color.glsl index 8b209c4..fcf8d28 100644 --- a/engine/fragment-color.glsl +++ b/engine/fragment-color.glsl @@ -1,9 +1,28 @@ #version 330 core + +#preinclude "light.glsl" + out vec4 FragColor; in vec3 in_frag_color; +in vec3 in_frag_pos; +in vec3 in_normal; + +uniform vec3 view_pos; void main() { - FragColor = vec4(in_frag_color, 1.0); + vec3 ambient_light = ambient_light_strength * light_color; + + vec3 light_direction = normalize(light_pos - in_frag_pos); + vec3 norm = normalize(in_normal); + + vec3 diffuse_light = calc_diffuse_light(light_direction, norm); + + vec3 specular_light = + calc_specular_light(light_direction, norm, view_pos, in_frag_pos); + + FragColor = + vec4((ambient_light + diffuse_light + specular_light) * in_frag_color, 1.0); } + diff --git a/engine/fragment-light.glsl b/engine/fragment-light.glsl new file mode 100644 index 0000000..0d46bea --- /dev/null +++ b/engine/fragment-light.glsl @@ -0,0 +1,7 @@ +#version 330 core +out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); +} diff --git a/engine/fragment-texture.glsl b/engine/fragment-texture.glsl index a4d378b..2948a2e 100644 --- a/engine/fragment-texture.glsl +++ b/engine/fragment-texture.glsl @@ -1,11 +1,31 @@ #version 330 core + +#preinclude "light.glsl" + out vec4 FragColor; in vec2 in_texture_coords; +in vec3 in_frag_pos; +in vec3 in_normal; + +uniform vec3 view_pos; + uniform sampler2D input_texture; void main() { - FragColor = texture(input_texture, in_texture_coords); + vec3 ambient_light = ambient_light_strength * light_color; + + vec3 light_direction = normalize(light_pos - in_frag_pos); + vec3 norm = normalize(in_normal); + + vec3 diffuse_light = calc_diffuse_light(light_direction, norm); + + vec3 specular_light = + calc_specular_light(light_direction, norm, view_pos, in_frag_pos); + + vec4 light = vec4((ambient_light + diffuse_light + specular_light), 1.0); + + FragColor = light * texture(input_texture, in_texture_coords); } diff --git a/engine/light.glsl b/engine/light.glsl new file mode 100644 index 0000000..3f1c601 --- /dev/null +++ b/engine/light.glsl @@ -0,0 +1,34 @@ +#version 330 core + +uniform vec3 light_color; +uniform vec3 light_pos; + +uniform float ambient_light_strength; +uniform float specular_light_strength; + +uniform uint specular_shininess; + +vec3 calc_diffuse_light(in vec3 light_dir, in vec3 norm) +{ + float diff = max(dot(norm, light_dir), 0.0); + + return diff * light_color; +} + +vec3 calc_specular_light( + in vec3 light_dir, + in vec3 norm, + in vec3 view_pos, + in vec3 frag_pos +) +{ + vec3 view_direction = normalize(view_pos - frag_pos); + + vec3 reflect_direction = reflect(-light_dir, norm); + + float spec = + pow(max(dot(view_direction, reflect_direction), 0.0), specular_shininess); + + return specular_light_strength * spec * light_color; +} + diff --git a/engine/src/color.rs b/engine/src/color.rs index 1e87cb9..2fd2cd3 100644 --- a/engine/src/color.rs +++ b/engine/src/color.rs @@ -6,3 +6,8 @@ pub struct Color<Value> pub green: Value, pub blue: Value, } + +impl Color<f32> +{ + pub const WHITE_F32: Self = Self { red: 1.0, green: 1.0, blue: 1.0 }; +} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 0eca914..e6568a1 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -7,6 +7,7 @@ use glfw::window::KeyState; use glfw::{Window, WindowBuilder}; use crate::camera::Camera; +use crate::lighting::{LightSettings, LightSource}; use crate::object::{Id as ObjectId, Object}; use crate::renderer::Renderer; use crate::vector::Vec2; @@ -20,6 +21,8 @@ mod transform; pub mod camera; pub mod color; +pub mod lighting; +pub mod math; pub mod object; pub mod texture; pub mod vector; @@ -33,6 +36,8 @@ pub struct Engine { /// Objects have to be dropped before window. Otherwise, UB. objects: BTreeMap<ObjectId, Object>, + light_source: Option<LightSource>, + light_settings: Option<LightSettings>, window: Window, renderer: Renderer, delta_time: Duration, @@ -70,6 +75,8 @@ impl Engine window, renderer, objects: BTreeMap::new(), + light_source: None, + light_settings: None, delta_time: Duration::ZERO, }) } @@ -85,6 +92,23 @@ impl Engine .extend(objects.into_iter().map(|object| (object.id(), object))); } + pub fn set_light_source(&mut self, light_source: LightSource) + { + self.light_source = Some(light_source); + } + + #[must_use] + pub fn light_source(&self) -> Option<&LightSource> + { + self.light_source.as_ref() + } + + #[must_use] + pub fn light_source_mut(&mut self) -> Option<&mut LightSource> + { + self.light_source.as_mut() + } + #[must_use] pub fn get_object_by_id(&self, id: ObjectId) -> Option<&Object> { @@ -105,6 +129,8 @@ impl Engine { let mut prev_frame_start: Option<Instant> = None; + let default_light_settings = LightSettings::default(); + while !self.window.should_close() { self.update_delta_time(&mut prev_frame_start); @@ -112,7 +138,14 @@ impl Engine let window_size = self.window.size().map_err(Error::GetWindowSizeFailed)?; - self.renderer.render(self.objects.values(), &window_size); + self.renderer.render( + self.objects.values(), + self.light_source.as_ref(), + self.light_settings + .as_ref() + .unwrap_or(&default_light_settings), + &window_size, + ); self.window .swap_buffers() @@ -171,6 +204,17 @@ impl Engine Ok(Vec2 { x: pos.x, y: pos.y }) } + pub fn set_light_settings(&mut self, light_settings: LightSettings) + { + self.light_settings = Some(light_settings); + } + + #[must_use] + pub fn light_settings(&self) -> Option<&LightSettings> + { + self.light_settings.as_ref() + } + fn update_delta_time(&mut self, prev_frame_start: &mut Option<Instant>) { let frame_start_time = Instant::now(); diff --git a/engine/src/lighting.rs b/engine/src/lighting.rs new file mode 100644 index 0000000..e7fb901 --- /dev/null +++ b/engine/src/lighting.rs @@ -0,0 +1,93 @@ +use crate::color::Color; +use crate::vector::Vec3; + +#[derive(Debug, Clone)] +pub struct LightSettings +{ + pub ambient_light_strength: f32, + pub specular_light_strength: f32, + pub specular_shininess: u32, +} + +impl Default for LightSettings +{ + fn default() -> Self + { + Self { + ambient_light_strength: 1.0, + specular_light_strength: 0.0, + specular_shininess: 32, + } + } +} + +#[derive(Debug, Clone)] +pub struct LightSource +{ + position: Vec3<f32>, + color: Color<f32>, +} + +impl LightSource +{ + #[must_use] + pub fn position(&self) -> &Vec3<f32> + { + &self.position + } + + #[must_use] + pub fn color(&self) -> &Color<f32> + { + &self.color + } + + pub fn translate(&mut self, translation: Vec3<f32>) + { + self.position += translation; + } +} + +#[derive(Debug, Clone, Default)] +pub struct LightSourceBuilder +{ + position: Vec3<f32>, + color: Color<f32>, +} + +impl LightSourceBuilder +{ + #[must_use] + pub fn new() -> Self + { + Self { + position: Vec3::default(), + color: Color::WHITE_F32, + } + } + + #[must_use] + pub fn position(mut self, position: Vec3<f32>) -> Self + { + self.position = position; + + self + } + + #[must_use] + pub fn color(mut self, color: Color<f32>) -> Self + { + self.color = color; + + self + } + + #[must_use] + pub fn build(&self) -> LightSource + { + LightSource { + position: self.position.clone(), + color: self.color.clone(), + } + } +} diff --git a/engine/src/math.rs b/engine/src/math.rs new file mode 100644 index 0000000..bda3b59 --- /dev/null +++ b/engine/src/math.rs @@ -0,0 +1,17 @@ +//! Miscellaneous math functions. + +use crate::vector::Vec3; + +/// Calculates the surface normal of a triangle. +#[must_use] +pub fn calc_triangle_surface_normal( + egde_a: &Vec3<f32>, + edge_b: &Vec3<f32>, + edge_c: &Vec3<f32>, +) -> Vec3<f32> +{ + let v1 = edge_b.clone() - egde_a.clone(); + let v2 = edge_c.clone() - egde_a.clone(); + + v1.cross(&v2) +} diff --git a/engine/src/opengl/shader.rs b/engine/src/opengl/shader.rs index 3dc308e..4bf56aa 100644 --- a/engine/src/opengl/shader.rs +++ b/engine/src/opengl/shader.rs @@ -3,6 +3,7 @@ use std::ptr::null_mut; use crate::matrix::Matrix; use crate::opengl::currently_bound::CurrentlyBound; +use crate::vector::Vec3; #[derive(Debug)] pub struct Shader @@ -179,6 +180,41 @@ impl Program } } + pub fn set_uniform_vec_3fv( + &self, + _: &CurrentlyBound<Self>, + name: &CStr, + vec: &Vec3<f32>, + ) + { + let uniform_location = + unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) }; + + unsafe { + gl::Uniform3fv(uniform_location, 1, vec.as_ptr()); + } + } + + pub fn set_uniform_1fv(&self, _: &CurrentlyBound<Self>, name: &CStr, num: f32) + { + let uniform_location = + unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) }; + + unsafe { + gl::Uniform1fv(uniform_location, 1, &num); + } + } + + pub fn set_uniform_1uiv(&self, _: &CurrentlyBound<Self>, name: &CStr, num: u32) + { + let uniform_location = + unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) }; + + unsafe { + gl::Uniform1uiv(uniform_location, 1, &num); + } + } + fn get_info_log(&self) -> String { let mut buf = vec![gl::types::GLchar::default(); 512]; diff --git a/engine/src/renderer/mod.rs b/engine/src/renderer/mod.rs index 7d5e104..d46612f 100644 --- a/engine/src/renderer/mod.rs +++ b/engine/src/renderer/mod.rs @@ -5,6 +5,8 @@ use cstr::cstr; use glfw::WindowSize; use crate::camera::Camera; +use crate::color::Color; +use crate::lighting::{LightSettings, LightSource}; use crate::object::Object; use crate::opengl::buffer::{ ArrayKind as ArrayBufferKind, @@ -19,7 +21,7 @@ use crate::opengl::shader::Program as ShaderProgram; use crate::opengl::vertex_array::{PrimitiveKind, VertexArray}; use crate::opengl::{clear_buffers, enable, BufferClearMask, Capability}; use crate::projection::new_perspective; -use crate::vector::Vec2; +use crate::vector::{Vec2, Vec3}; use crate::vertex::Vertex; #[derive(Debug)] @@ -65,6 +67,8 @@ impl Renderer pub fn render<'obj>( &self, objects: impl IntoIterator<Item = &'obj Object>, + light_source: Option<&LightSource>, + light_settings: &LightSettings, window_size: &WindowSize, ) { @@ -81,6 +85,14 @@ impl Renderer &shader_program_curr_bound, ); + apply_light( + obj, + light_source, + &self.camera, + &shader_program_curr_bound, + light_settings, + ); + if let Some(texture) = obj.texture() { texture.inner().bind(|_| { Self::draw_object(obj); @@ -245,6 +257,57 @@ fn apply_transformation_matrices( ); } +fn apply_light( + obj: &Object, + light_source: Option<&LightSource>, + camera: &Camera, + shader_program_curr_bound: &CurrentlyBound<ShaderProgram>, + light_settings: &LightSettings, +) +{ + obj.renderable().shader_program.set_uniform_vec_3fv( + shader_program_curr_bound, + cstr!("light_color"), + &light_source + .map_or(Color::WHITE_F32, |light_source| { + light_source.color().clone() + }) + .into(), + ); + + obj.renderable().shader_program.set_uniform_vec_3fv( + shader_program_curr_bound, + cstr!("light_pos"), + &light_source.map_or_else(Vec3::default, |light_source| { + light_source.position().clone() + }), + ); + + obj.renderable().shader_program.set_uniform_1fv( + shader_program_curr_bound, + cstr!("ambient_light_strength"), + light_settings.ambient_light_strength, + ); + + obj.renderable().shader_program.set_uniform_1fv( + shader_program_curr_bound, + cstr!("specular_light_strength"), + light_settings.specular_light_strength, + ); + + obj.renderable().shader_program.set_uniform_1uiv( + shader_program_curr_bound, + cstr!("specular_shininess"), + light_settings.specular_shininess, + ); + + obj.renderable().shader_program.set_uniform_vec_3fv( + shader_program_curr_bound, + cstr!("view_pos"), + camera.position(), + ); +} + #[cfg(feature = "debug")] #[tracing::instrument(skip_all)] fn opengl_debug_message_cb( diff --git a/engine/src/vector.rs b/engine/src/vector.rs index 00d6a6f..a5ef911 100644 --- a/engine/src/vector.rs +++ b/engine/src/vector.rs @@ -1,5 +1,7 @@ use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign}; +use crate::color::Color; + #[derive(Debug, Default, Clone)] pub struct Vec2<Value> { @@ -64,6 +66,14 @@ impl Vec3<f32> } } +impl<Value> Vec3<Value> +{ + pub fn as_ptr(&self) -> *const Value + { + &self.x + } +} + impl<Value> Sub for Vec3<Value> where Value: Sub<Value, Output = Value>, @@ -215,3 +225,15 @@ where self.z -= rhs.z; } } + +impl<Value> From<Color<Value>> for Vec3<Value> +{ + fn from(color: Color<Value>) -> Self + { + Self { + x: color.red, + y: color.green, + z: color.blue, + } + } +} diff --git a/engine/src/vertex.rs b/engine/src/vertex.rs index 9df646f..bb0e890 100644 --- a/engine/src/vertex.rs +++ b/engine/src/vertex.rs @@ -10,6 +10,7 @@ pub struct Vertex pos: Vec3<f32>, color: Color<f32>, texture_coords: Vec2<f32>, + normal: Vec3<f32>, } #[derive(Debug, Default)] @@ -18,6 +19,7 @@ pub struct Builder pos: Option<Vec3<f32>>, color: Option<Color<f32>>, texture_coords: Vec2<f32>, + normal: Option<Vec3<f32>>, } impl Builder @@ -53,15 +55,25 @@ impl Builder } #[must_use] + pub fn normal(mut self, normal: Vec3<f32>) -> Self + { + self.normal = Some(normal); + + self + } + + #[must_use] pub fn build(self) -> Option<Vertex> { let pos = self.pos?; - let color = self.color?; + let color = self.color.unwrap_or_default(); + let normal = self.normal.unwrap_or_default(); Some(Vertex { pos, color, texture_coords: self.texture_coords, + normal, }) } } @@ -89,6 +101,12 @@ impl Vertex component_cnt: AttributeComponentCnt::Two, component_size: size_of::<f32>(), }, + Attribute { + index: 3, + component_type: AttributeComponentType::Float, + component_cnt: AttributeComponentCnt::Three, + component_size: size_of::<f32>(), + }, ] } } diff --git a/engine/vertex.glsl b/engine/vertex.glsl index 61782c2..3279036 100644 --- a/engine/vertex.glsl +++ b/engine/vertex.glsl @@ -2,9 +2,12 @@ layout (location = 0) in vec3 pos; layout (location = 1) in vec3 color; layout (location = 2) in vec2 texture_coords; +layout (location = 3) in vec3 normal; out vec3 in_frag_color; +out vec3 in_frag_pos; out vec2 in_texture_coords; +out vec3 in_normal; uniform mat4 model; uniform mat4 view; @@ -15,5 +18,9 @@ void main() gl_Position = projection * view * model * vec4(pos, 1.0); in_frag_color = color; + in_frag_pos = vec3(model * vec4(pos, 1.0)); in_texture_coords = texture_coords; + + // TODO: Do this using CPU for performance increase + in_normal = mat3(transpose(inverse(model))) * normal; } |