summaryrefslogtreecommitdiff
path: root/engine/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-11-23 21:32:03 +0100
committerHampusM <hampus@hampusmat.com>2024-11-23 21:32:03 +0100
commitdaf280048d82eed42d44423ce50ae9e809ba91a9 (patch)
treefdd1580b3c50f2f7cebc5175b38777a7117c904f /engine/src
parent999264a46f9a545771c6710b0893ca32cf6e7ee3 (diff)
refactor(engine): make shaders a renderer detail
Diffstat (limited to 'engine/src')
-rw-r--r--engine/src/lib.rs1
-rw-r--r--engine/src/opengl/shader.rs17
-rw-r--r--engine/src/renderer/opengl.rs99
-rw-r--r--engine/src/renderer/opengl/glsl/fragment.glsl73
-rw-r--r--engine/src/renderer/opengl/glsl/light.glsl133
-rw-r--r--engine/src/renderer/opengl/glsl/vertex.glsl24
-rw-r--r--engine/src/renderer/opengl/glsl/vertex_data.glsl11
-rw-r--r--engine/src/shader.rs188
8 files changed, 317 insertions, 229 deletions
diff --git a/engine/src/lib.rs b/engine/src/lib.rs
index 555e1ed..07b2f8d 100644
--- a/engine/src/lib.rs
+++ b/engine/src/lib.rs
@@ -37,7 +37,6 @@ pub mod mesh;
pub mod performance;
pub mod projection;
pub mod renderer;
-pub mod shader;
pub mod texture;
pub mod transform;
pub mod vertex;
diff --git a/engine/src/opengl/shader.rs b/engine/src/opengl/shader.rs
index 070897e..36dc1a4 100644
--- a/engine/src/opengl/shader.rs
+++ b/engine/src/opengl/shader.rs
@@ -2,7 +2,6 @@ use std::ffi::CStr;
use std::ptr::null_mut;
use crate::matrix::Matrix;
-use crate::shader::Kind;
use crate::vector::Vec3;
#[derive(Debug)]
@@ -20,7 +19,7 @@ impl Shader
Self { shader }
}
- pub fn set_source(&self, source: &str) -> Result<(), Error>
+ pub fn set_source(&mut self, source: &str) -> Result<(), Error>
{
if !source.is_ascii() {
return Err(Error::SourceNotAscii);
@@ -39,7 +38,7 @@ impl Shader
Ok(())
}
- pub fn compile(&self) -> Result<(), Error>
+ pub fn compile(&mut self) -> Result<(), Error>
{
unsafe {
gl::CompileShader(self.shader);
@@ -90,6 +89,14 @@ impl Drop for Shader
}
}
+/// Shader kind.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Kind
+{
+ Vertex,
+ Fragment,
+}
+
impl Kind
{
fn into_gl(self) -> gl::types::GLenum
@@ -117,14 +124,14 @@ impl Program
Self { program }
}
- pub fn attach(&self, shader: &Shader)
+ pub fn attach(&mut self, shader: &Shader)
{
unsafe {
gl::AttachShader(self.program, shader.shader);
}
}
- pub fn link(&self) -> Result<(), Error>
+ pub fn link(&mut self) -> Result<(), Error>
{
unsafe {
gl::LinkProgram(self.program);
diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs
index 6e6347b..deb26a8 100644
--- a/engine/src/renderer/opengl.rs
+++ b/engine/src/renderer/opengl.rs
@@ -2,7 +2,9 @@
use std::collections::HashMap;
use std::ffi::{c_void, CString};
+use std::io::{Error as IoError, ErrorKind as IoErrorKind};
use std::ops::Deref;
+use std::path::Path;
use std::process::abort;
use ecs::actions::Actions;
@@ -24,8 +26,13 @@ use crate::mesh::Mesh;
use crate::opengl::buffer::{Buffer, Usage as BufferUsage};
#[cfg(feature = "debug")]
use crate::opengl::debug::{MessageSeverity, MessageSource, MessageType};
+use crate::opengl::glsl::{
+ preprocess as glsl_preprocess,
+ PreprocessingError as GlslPreprocessingError,
+};
use crate::opengl::shader::{
Error as GlShaderError,
+ Kind as ShaderKind,
Program as GlShaderProgram,
Shader as GlShader,
};
@@ -41,7 +48,6 @@ use crate::opengl::vertex_array::{
};
use crate::opengl::{clear_buffers, enable, BufferClearMask, Capability};
use crate::projection::{new_perspective_matrix, Projection};
-use crate::shader::Program as ShaderProgram;
use crate::texture::{Id as TextureId, Texture};
use crate::transform::{Position, Scale};
use crate::util::NeverDrop;
@@ -51,7 +57,6 @@ use crate::window::Window;
type RenderableEntity = (
Mesh,
- ShaderProgram,
Material,
Option<MaterialFlags>,
Option<Position>,
@@ -137,34 +142,24 @@ fn render(
let directional_lights = directional_lights.iter().collect::<Vec<_>>();
let GlobalGlObjects {
- shader_programs: gl_shader_programs,
+ shader_program,
textures: gl_textures,
} = &mut *gl_objects;
+ let shader_program =
+ shader_program.get_or_insert_with(|| create_default_shader_program().unwrap());
+
clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH);
for (
entity_index,
- (
- mesh,
- shader_program,
- material,
- material_flags,
- position,
- scale,
- draw_flags,
- gl_objects,
- ),
+ (mesh, material, material_flags, position, scale, draw_flags, gl_objects),
) in query.iter().enumerate()
{
let material_flags = material_flags
.map(|material_flags| material_flags.clone())
.unwrap_or_default();
- let shader_program = gl_shader_programs
- .entry(shader_program.u64_hash())
- .or_insert_with(|| create_gl_shader_program(&shader_program).unwrap());
-
let new_gl_objects;
let gl_objects = if let Some(gl_objects) = gl_objects.as_deref() {
@@ -248,7 +243,7 @@ fn render(
#[derive(Debug, Default, Component)]
struct GlobalGlObjects
{
- shader_programs: HashMap<u64, GlShaderProgram>,
+ shader_program: Option<GlShaderProgram>,
textures: HashMap<TextureId, GlTexture>,
}
@@ -300,34 +295,68 @@ fn create_gl_texture(texture: &Texture) -> GlTexture
gl_texture
}
-fn create_gl_shader_program(
- shader_program: &ShaderProgram,
-) -> Result<GlShaderProgram, GlShaderError>
+const VERTEX_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex.glsl");
+const FRAGMENT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/fragment.glsl");
+
+const VERTEX_DATA_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex_data.glsl");
+const LIGHT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/light.glsl");
+
+fn create_default_shader_program() -> Result<GlShaderProgram, CreateShaderError>
{
- let gl_shaders = shader_program
- .shaders()
- .iter()
- .map(|shader| {
- let gl_shader = GlShader::new(shader.kind());
+ let mut vertex_shader = GlShader::new(ShaderKind::Vertex);
- gl_shader.set_source(shader.source())?;
- gl_shader.compile()?;
+ vertex_shader.set_source(&*glsl_preprocess(
+ VERTEX_GLSL_SHADER_SRC,
+ &get_glsl_shader_content,
+ )?)?;
- Ok(gl_shader)
- })
- .collect::<Result<Vec<_>, _>>()?;
+ vertex_shader.compile()?;
- let gl_shader_program = GlShaderProgram::new();
+ let mut fragment_shader = GlShader::new(ShaderKind::Fragment);
- for gl_shader in &gl_shaders {
- gl_shader_program.attach(gl_shader);
- }
+ fragment_shader.set_source(&*glsl_preprocess(
+ FRAGMENT_GLSL_SHADER_SRC,
+ &get_glsl_shader_content,
+ )?)?;
+
+ fragment_shader.compile()?;
+
+ let mut gl_shader_program = GlShaderProgram::new();
+
+ gl_shader_program.attach(&vertex_shader);
+ gl_shader_program.attach(&fragment_shader);
gl_shader_program.link()?;
Ok(gl_shader_program)
}
+#[derive(Debug, thiserror::Error)]
+enum CreateShaderError
+{
+ #[error(transparent)]
+ ShaderError(#[from] GlShaderError),
+
+ #[error(transparent)]
+ PreprocessingError(#[from] GlslPreprocessingError),
+}
+
+fn get_glsl_shader_content(path: &Path) -> Result<Vec<u8>, std::io::Error>
+{
+ if path == Path::new("vertex_data.glsl") {
+ return Ok(VERTEX_DATA_GLSL_SHADER_SRC.as_bytes().to_vec());
+ }
+
+ if path == Path::new("light.glsl") {
+ return Ok(LIGHT_GLSL_SHADER_SRC.as_bytes().to_vec());
+ }
+
+ Err(IoError::new(
+ IoErrorKind::NotFound,
+ format!("Content for shader file {} not found", path.display()),
+ ))
+}
+
#[derive(Debug, Component)]
struct GlObjects
{
diff --git a/engine/src/renderer/opengl/glsl/fragment.glsl b/engine/src/renderer/opengl/glsl/fragment.glsl
new file mode 100644
index 0000000..5bf5ff2
--- /dev/null
+++ b/engine/src/renderer/opengl/glsl/fragment.glsl
@@ -0,0 +1,73 @@
+#version 330 core
+
+#preinclude "light.glsl"
+#preinclude "vertex_data.glsl"
+
+#define MAX_LIGHT_CNT 64
+
+out vec4 FragColor;
+
+in VertexData vertex_data;
+
+uniform vec3 view_pos;
+uniform sampler2D input_texture;
+uniform Material material;
+
+uniform PointLight point_lights[MAX_LIGHT_CNT];
+uniform int point_light_cnt;
+
+uniform DirectionalLight directional_lights[MAX_LIGHT_CNT];
+uniform int directional_light_cnt;
+
+void main()
+{
+ vec3 ambient_light = calc_ambient_light(material, vertex_data.texture_coords);
+
+ vec3 directional_light_sum = vec3(0.0, 0.0, 0.0);
+
+ for (int dl_index = 0; dl_index < directional_light_cnt; dl_index++) {
+ CalculatedLight calculated_dir_light;
+
+ calc_light(
+ // Negated since we want the light to point from the light direction
+ normalize(-directional_lights[dl_index].direction),
+ directional_lights[dl_index].phong,
+ vertex_data,
+ view_pos,
+ material,
+ calculated_dir_light
+ );
+
+ directional_light_sum +=
+ calculated_dir_light.diffuse + calculated_dir_light.specular;
+ }
+
+ vec3 point_light_sum = vec3(0.0, 0.0, 0.0);
+
+ for (int pl_index = 0; pl_index < point_light_cnt; pl_index++) {
+ vec3 light_direction =
+ normalize(point_lights[pl_index].position - vertex_data.world_space_pos);
+
+ CalculatedLight calculated_point_light;
+
+ calc_light(
+ light_direction,
+ point_lights[pl_index].phong,
+ vertex_data,
+ view_pos,
+ material,
+ calculated_point_light
+ );
+
+ float attenuation =
+ calc_attenuation(point_lights[pl_index], vertex_data.world_space_pos);
+
+ calculated_point_light.diffuse *= attenuation;
+ calculated_point_light.specular *= attenuation;
+
+ point_light_sum +=
+ calculated_point_light.diffuse + calculated_point_light.specular;
+ }
+
+ FragColor = vec4((ambient_light + directional_light_sum + point_light_sum), 1.0);
+}
diff --git a/engine/src/renderer/opengl/glsl/light.glsl b/engine/src/renderer/opengl/glsl/light.glsl
new file mode 100644
index 0000000..1bc23a4
--- /dev/null
+++ b/engine/src/renderer/opengl/glsl/light.glsl
@@ -0,0 +1,133 @@
+#version 330 core
+
+#ifndef LIGHT_GLSL
+#define LIGHT_GLSL
+
+#preinclude "vertex_data.glsl"
+
+struct Material
+{
+ vec3 ambient;
+ vec3 diffuse;
+ vec3 specular;
+ sampler2D ambient_map;
+ sampler2D diffuse_map;
+ sampler2D specular_map;
+ float shininess;
+};
+
+struct LightPhong
+{
+ vec3 diffuse;
+ vec3 specular;
+};
+
+struct AttenuationProperties
+{
+ float constant;
+ float linear;
+ float quadratic;
+};
+
+struct PointLight
+{
+ LightPhong phong;
+ vec3 position;
+ AttenuationProperties attenuation_props;
+};
+
+struct DirectionalLight
+{
+ LightPhong phong;
+ vec3 direction;
+};
+
+struct CalculatedLight
+{
+ vec3 diffuse;
+ vec3 specular;
+};
+
+vec3 calc_ambient_light(in Material material, in vec2 texture_coords)
+{
+ return vec3(texture(material.ambient_map, texture_coords)) * material.ambient;
+}
+
+vec3 calc_diffuse_light(
+ in Material material,
+ in LightPhong light_phong,
+ in vec3 light_dir,
+ in vec3 norm,
+ in vec2 texture_coords
+)
+{
+ float diff = max(dot(norm, light_dir), 0.0);
+
+ return light_phong.diffuse * (
+ diff * (vec3(texture(material.diffuse_map, texture_coords)) * material.diffuse)
+ );
+}
+
+vec3 calc_specular_light(
+ in Material material,
+ in LightPhong light_phong,
+ in vec3 light_dir,
+ in vec3 norm,
+ in vec3 view_pos,
+ in vec3 frag_pos,
+ in vec2 texture_coords
+)
+{
+ 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), material.shininess);
+
+ return light_phong.specular * (
+ spec * (vec3(texture(material.specular_map, texture_coords)) * material.specular)
+ );
+}
+
+float calc_attenuation(in PointLight point_light, in vec3 position)
+{
+ float light_distance = length(point_light.position - position);
+
+ return 1.0 / (point_light.attenuation_props.constant
+ + point_light.attenuation_props.linear
+ * light_distance + point_light.attenuation_props.quadratic
+ * pow(light_distance, 2));
+}
+
+void calc_light(
+ in vec3 light_direction,
+ in LightPhong light_phong,
+ in VertexData vertex_data,
+ in vec3 view_pos,
+ in Material material,
+ out CalculatedLight calculated_light
+)
+{
+ vec3 norm = normalize(vertex_data.world_space_normal);
+
+ calculated_light.diffuse = calc_diffuse_light(
+ material,
+ light_phong,
+ light_direction,
+ norm,
+ vertex_data.texture_coords
+ );
+
+ calculated_light.specular = calc_specular_light(
+ material,
+ light_phong,
+ light_direction,
+ norm,
+ view_pos,
+ vertex_data.world_space_pos,
+ vertex_data.texture_coords
+ );
+}
+
+#endif
diff --git a/engine/src/renderer/opengl/glsl/vertex.glsl b/engine/src/renderer/opengl/glsl/vertex.glsl
new file mode 100644
index 0000000..b57caa6
--- /dev/null
+++ b/engine/src/renderer/opengl/glsl/vertex.glsl
@@ -0,0 +1,24 @@
+#version 330 core
+
+#preinclude "vertex_data.glsl"
+
+layout (location = 0) in vec3 pos;
+layout (location = 1) in vec2 texture_coords;
+layout (location = 2) in vec3 normal;
+
+out VertexData vertex_data;
+
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 projection;
+
+void main()
+{
+ gl_Position = projection * view * model * vec4(pos, 1.0);
+
+ vertex_data.world_space_pos = vec3(model * vec4(pos, 1.0));
+ vertex_data.texture_coords = texture_coords;
+
+ // TODO: Do this using CPU for performance increase
+ vertex_data.world_space_normal = mat3(transpose(inverse(model))) * normal;
+}
diff --git a/engine/src/renderer/opengl/glsl/vertex_data.glsl b/engine/src/renderer/opengl/glsl/vertex_data.glsl
new file mode 100644
index 0000000..486d445
--- /dev/null
+++ b/engine/src/renderer/opengl/glsl/vertex_data.glsl
@@ -0,0 +1,11 @@
+#ifndef VERTEX_DATA_GLSL
+#define VERTEX_DATA_GLSL
+
+struct VertexData
+{
+ vec2 texture_coords;
+ vec3 world_space_pos;
+ vec3 world_space_normal;
+};
+
+#endif
diff --git a/engine/src/shader.rs b/engine/src/shader.rs
deleted file mode 100644
index b43d538..0000000
--- a/engine/src/shader.rs
+++ /dev/null
@@ -1,188 +0,0 @@
-use std::collections::hash_map::DefaultHasher;
-use std::fs::read_to_string;
-use std::hash::{Hash, Hasher};
-use std::path::{Path, PathBuf};
-
-use ecs::Component;
-
-use crate::opengl::glsl::{
- preprocess as glsl_preprocess,
- PreprocessingError as GlslPreprocessingError,
-};
-
-const VERTEX_SHADER_FILE: &str = "vertex.glsl";
-const FRAGMENT_SHADER_FILE: &str = "fragment.glsl";
-
-const SHADER_DIR: &str = "engine";
-
-/// Shader program
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Component)]
-pub struct Program
-{
- shaders: Vec<Shader>,
-}
-
-impl Program
-{
- /// Creates a new shader program with the default shaders.
- ///
- /// # Errors
- /// Returns `Err` if:
- /// - Reading a default shader file Fails
- /// - Preprocessing a shader fails.
- pub fn new() -> Result<Self, Error>
- {
- let mut program = Self { shaders: Vec::new() };
-
- program.push_shader(
- Shader::read_shader_file(
- Kind::Vertex,
- &Path::new(SHADER_DIR).join(VERTEX_SHADER_FILE),
- )?
- .preprocess()?,
- );
-
- program.push_shader(
- Shader::read_shader_file(
- Kind::Fragment,
- &Path::new(SHADER_DIR).join(FRAGMENT_SHADER_FILE),
- )?
- .preprocess()?,
- );
-
- Ok(program)
- }
-
- pub fn push_shader(&mut self, shader: Shader)
- {
- self.shaders.push(shader);
- }
-
- pub fn append_shaders(&mut self, shaders: impl IntoIterator<Item = Shader>)
- {
- self.shaders.extend(shaders);
- }
-
- #[must_use]
- pub fn shaders(&self) -> &[Shader]
- {
- &self.shaders
- }
-
- pub(crate) fn u64_hash(&self) -> u64
- {
- let mut hasher = DefaultHasher::new();
-
- self.hash(&mut hasher);
-
- hasher.finish()
- }
-}
-
-#[derive(Debug, Clone, Hash, PartialEq, Eq)]
-pub struct Shader
-{
- kind: Kind,
- source: String,
- file: PathBuf,
-}
-
-impl Shader
-{
- /// Reads a shader from the specified source file.
- ///
- /// # Errors
- /// Will return `Err` if:
- /// - Reading the file fails
- /// - The shader source is not ASCII
- pub fn read_shader_file(kind: Kind, shader_file: &Path) -> Result<Self, Error>
- {
- let source = read_to_string(shader_file).map_err(|err| Error::ReadFailed {
- source: err,
- shader_file: shader_file.to_path_buf(),
- })?;
-
- if !source.is_ascii() {
- return Err(Error::SourceNotAscii);
- }
-
- Ok(Self {
- kind,
- source,
- file: shader_file.to_path_buf(),
- })
- }
-
- /// Preprocesses the shaders.
- ///
- /// # Errors
- /// Returns `Err` if preprocessing fails.
- pub fn preprocess(self) -> Result<Self, Error>
- {
- let parent_dir = self
- .file
- .parent()
- .ok_or(Error::SourcePathHasNoParent)?
- .to_path_buf();
-
- let source_preprocessed =
- glsl_preprocess(self.source, &|path| std::fs::read(parent_dir.join(path)))
- .map_err(|err| Error::PreprocessFailed {
- source: err,
- shader_file: self.file.clone(),
- })?;
-
- Ok(Self {
- kind: self.kind,
- source: source_preprocessed.into_owned(),
- file: self.file.clone(),
- })
- }
-
- #[must_use]
- pub fn kind(&self) -> Kind
- {
- self.kind
- }
-
- #[must_use]
- pub fn source(&self) -> &str
- {
- &self.source
- }
-}
-
-/// Shader kind.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum Kind
-{
- Vertex,
- Fragment,
-}
-
-/// Shader error
-#[derive(Debug, thiserror::Error)]
-pub enum Error
-{
- #[error("Failed to read shader {}", shader_file.display())]
- ReadFailed
- {
- #[source]
- source: std::io::Error,
- shader_file: PathBuf,
- },
-
- #[error("Shader source is not ASCII")]
- SourceNotAscii,
-
- #[error("Failed to preprocess shader {}", shader_file.display())]
- PreprocessFailed
- {
- #[source]
- source: GlslPreprocessingError,
- shader_file: PathBuf,
- },
-
- #[error("Shader source path has no parent")]
- SourcePathHasNoParent,
-}