From 0af4622525e04134c72adc3b9661db7dc73977cf Mon Sep 17 00:00:00 2001 From: reo Date: Sun, 13 Jul 2025 20:03:43 +0300 Subject: [PATCH] Add texture support to the renderer - Refactor to use the new model::Model struct - Implement blinn-phong shading - Add texture support for shaders --- resources/shaders/gl_solid_color.frag | 11 ----- resources/shaders/gl_solid_color.vert | 12 ----- resources/shaders/gl_textured.frag | 34 ++++++++++++++ resources/shaders/gl_textured.vert | 23 ++++++++++ src/render.rs | 66 +++++++++++++++++++++------ 5 files changed, 109 insertions(+), 37 deletions(-) delete mode 100644 resources/shaders/gl_solid_color.frag delete mode 100644 resources/shaders/gl_solid_color.vert create mode 100644 resources/shaders/gl_textured.frag create mode 100644 resources/shaders/gl_textured.vert diff --git a/resources/shaders/gl_solid_color.frag b/resources/shaders/gl_solid_color.frag deleted file mode 100644 index c3da69f..0000000 --- a/resources/shaders/gl_solid_color.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 330 core -in vec3 v_normal; -out vec4 color; -uniform vec3 light_dir; - -void main() { - float brightness = dot(normalize(v_normal), normalize(light_dir)); - vec3 dark_color = vec3(0.6, 0.0, 0.0); - vec3 regular_color = vec3(1.0, 0.0, 0.0); - color = vec4(mix(dark_color, regular_color, brightness), 1.0); -} diff --git a/resources/shaders/gl_solid_color.vert b/resources/shaders/gl_solid_color.vert deleted file mode 100644 index cc65b9f..0000000 --- a/resources/shaders/gl_solid_color.vert +++ /dev/null @@ -1,12 +0,0 @@ -#version 330 core -in vec3 position; -in vec3 normal; -uniform mat4 model; -uniform mat4 view; -uniform mat4 projection; -out vec3 v_normal; -void main() { - mat4 modelview = view * model; - v_normal = transpose(inverse(mat3(modelview))) * normal; - gl_Position = projection * modelview * vec4(position, 1.0); -} diff --git a/resources/shaders/gl_textured.frag b/resources/shaders/gl_textured.frag new file mode 100644 index 0000000..e0da565 --- /dev/null +++ b/resources/shaders/gl_textured.frag @@ -0,0 +1,34 @@ +#version 330 core + +in vec3 v_normal; +in vec2 v_tex; +in vec3 v_position; + +out vec4 frag_color; + +uniform vec3 u_light; +uniform sampler2D tex; +uniform vec3 color; // base colour factor (acts as solid colour when no texture) + +void main() { + // Combine base texture (or constant white) with colour factor supplied by CPU. + vec3 base_col = texture(tex, v_tex).rgb * color; + + vec3 ambient_color = base_col * 0.2; + vec3 diffuse_color = base_col * 0.6; + vec3 specular_color = vec3(1.0); + + // u_light is the direction **from the light towards the fragment**. + float diffuse = max(dot(normalize(v_normal), normalize(u_light)), 0.0); + + vec3 camera_dir = normalize(-v_position); + vec3 half_dir = normalize(normalize(u_light) + camera_dir); + float specular = pow(max(dot(half_dir, normalize(v_normal)), 0.0), 16.0); + + vec3 result = ambient_color + diffuse * diffuse_color + specular * specular_color; + + // Convert from linear to sRGB for display (approximate γ-correction) + result = pow(result, vec3(1.0 / 2.2)); + + frag_color = vec4(result, 1.0); +} \ No newline at end of file diff --git a/resources/shaders/gl_textured.vert b/resources/shaders/gl_textured.vert new file mode 100644 index 0000000..269c9d3 --- /dev/null +++ b/resources/shaders/gl_textured.vert @@ -0,0 +1,23 @@ +#version 330 core + +in vec3 position; +in vec3 normal; +in vec2 tex_coords; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; +uniform vec2 uv_offset; +uniform vec2 uv_scale; + +out vec3 v_normal; +out vec2 v_tex; +out vec3 v_position; + +void main() { + mat4 modelview = view * model; + v_normal = transpose(inverse(mat3(modelview))) * normal; + v_tex = tex_coords * uv_scale + uv_offset; + v_position = (modelview * vec4(position, 1.0)).xyz; + gl_Position = projection * modelview * vec4(position, 1.0); +} \ No newline at end of file diff --git a/src/render.rs b/src/render.rs index eca2710..80972a5 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,7 +1,9 @@ use crate::camera::Camera; -use crate::ecs::{MeshHandle, Transform}; -use crate::model::Mesh; +use crate::ecs::{ModelHandle, Transform}; +use crate::model::{Model}; +use glium::texture::{RawImage2d, SrgbTexture2d}; use glium::{uniform, Program, Surface}; +use glium::uniforms::{MinifySamplerFilter, MagnifySamplerFilter, SamplerWrapFunction}; use glam::Vec3; use hecs::World; use glium::glutin::surface::WindowSurface; @@ -9,16 +11,25 @@ use glium::glutin::surface::WindowSurface; pub struct GliumRenderer { display: glium::Display, program: Program, - pub meshes: Vec, + white_tex: SrgbTexture2d, + + pub models: Vec, + params: glium::DrawParameters<'static>, } impl GliumRenderer { pub fn new(display: glium::Display) -> anyhow::Result { - const VERT: &str = include_str!("../resources/shaders/gl_solid_color.vert"); - const FRAG: &str = include_str!("../resources/shaders/gl_solid_color.frag"); + const VERT_SRC: &str = include_str!("../resources/shaders/gl_textured.vert"); + const FRAG_SRC: &str = include_str!("../resources/shaders/gl_textured.frag"); - let program = Program::from_source(&display, VERT, FRAG, None)?; + let program = Program::from_source(&display, VERT_SRC, FRAG_SRC, None)?; + + let white_tex = { + let data = vec![255u8, 255u8, 255u8, 255u8]; + let raw = RawImage2d::from_raw_rgba(data, (1, 1)); + SrgbTexture2d::new(&display, raw)? + }; let params = glium::DrawParameters { depth: glium::Depth { @@ -29,7 +40,13 @@ impl GliumRenderer { .. Default::default() }; - Ok(Self { display, program, meshes: Vec::new(), params }) + Ok(Self { + display, + program, + white_tex, + models: Vec::new(), + params, + }) } fn draw_scene(&self, world: &World, target: &mut S) { @@ -41,20 +58,41 @@ impl GliumRenderer { } }; - let light_dir: Vec3 = Vec3::new(-1.0, -1.0, -1.0).normalize(); + // Direction from the light source (0,+Y) towards the scene. + let light_dir: Vec3 = Vec3::new(0.0, -1.0, 0.0).normalize(); + + for (_, (tr, mh)) in world.query::<(&Transform, &ModelHandle)>().iter() { + let model = &self.models[mh.0]; + let mesh = &model.mesh; + let mat = &model.material; + + let tex_ref: &SrgbTexture2d = mat.base_color.as_ref().unwrap_or(&self.white_tex); + + let mut sampler = tex_ref.sampled(); + sampler = sampler.wrap_function(SamplerWrapFunction::Repeat); + sampler = sampler.minify_filter(MinifySamplerFilter::Linear); + sampler = sampler.magnify_filter(MagnifySamplerFilter::Linear); + + let c = mat.base_color_factor; - for (_, (tr, mh)) in world.query::<(&Transform, &MeshHandle)>().iter() { - let mesh = &self.meshes[mh.0]; let uniforms = uniform! { model: tr.matrix().to_cols_array_2d(), view: cam.view().to_cols_array_2d(), projection: cam.projection().to_cols_array_2d(), - light_dir: [light_dir.x, light_dir.y, light_dir.z], + u_light: [light_dir.x, light_dir.y, light_dir.z], + tex: sampler, + color: [c[0], c[1], c[2]], + uv_offset: [mat.uv_offset.x, mat.uv_offset.y], + uv_scale: [mat.uv_scale.x, mat.uv_scale.y], }; - target - .draw(&mesh.vbuf, &mesh.ibuf, &self.program, &uniforms, &self.params) - .unwrap(); + target.draw( + &mesh.vbuf, + &mesh.ibuf, + &self.program, + &uniforms, + &self.params, + ).unwrap(); } }