summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-11-12 22:38:52 +0100
committerHampusM <hampus@hampusmat.com>2023-11-12 22:57:19 +0100
commit8d821588cd4f51d4ae9c4ef52d45c0af0e1ce9e5 (patch)
treeed1b0dd30e801b9a70537b9806a445e4212978b3
parent67023d6a095f457a2579367d59d13c6c804e7108 (diff)
feat(engine): add basic flat lighting
-rw-r--r--engine/fragment-color.glsl21
-rw-r--r--engine/fragment-light.glsl7
-rw-r--r--engine/fragment-texture.glsl22
-rw-r--r--engine/light.glsl34
-rw-r--r--engine/src/color.rs5
-rw-r--r--engine/src/lib.rs46
-rw-r--r--engine/src/lighting.rs93
-rw-r--r--engine/src/math.rs17
-rw-r--r--engine/src/opengl/shader.rs36
-rw-r--r--engine/src/renderer/mod.rs65
-rw-r--r--engine/src/vector.rs22
-rw-r--r--engine/src/vertex.rs20
-rw-r--r--engine/vertex.glsl7
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;
}