From c78454ade80bc2a2e8ce1c74b288132c1c770aac Mon Sep 17 00:00:00 2001
From: Joseph Walton-Rivers <joseph@walton-rivers.uk>
Date: Sat, 9 Jul 2022 13:17:41 +0100
Subject: [PATCH] shaders based on opengl redbook

---
 demo/data/redbook/lighting_frag.glsl | 104 +++++++++++++++++++++++++++
 demo/data/redbook/lighting_vert.glsl |  26 +++++++
 demo/data/rollball.yml               |  14 ++--
 fggl/gfx/ogl/renderer.cpp            |   4 +-
 fggl/gfx/ogl/shader.cpp              |   3 +
 fggl/gfx/ogl4/models.cpp             |  84 ++++++++++++++++++----
 include/fggl/gfx/ogl/types.hpp       |   5 ++
 include/fggl/gfx/phong.hpp           |  36 +++++-----
 8 files changed, 237 insertions(+), 39 deletions(-)
 create mode 100644 demo/data/redbook/lighting_frag.glsl
 create mode 100644 demo/data/redbook/lighting_vert.glsl

diff --git a/demo/data/redbook/lighting_frag.glsl b/demo/data/redbook/lighting_frag.glsl
new file mode 100644
index 0000000..78242e7
--- /dev/null
+++ b/demo/data/redbook/lighting_frag.glsl
@@ -0,0 +1,104 @@
+/**
+ * OpenGL RedBook Shader.
+ * Examples 7.8, 7.9 and 7.10.
+ */
+#version 330 core
+
+struct LightProperties {
+    bool isEnabled;
+    bool isLocal;
+    bool isSpot;
+
+    vec3 ambient;
+    vec3 colour;
+    vec3 position;
+
+    vec3 halfVector;
+    vec3 coneDirection;
+    float spotCosCustoff;
+    float spotExponent;
+    float constantAttenuation;
+    float linearAttenuation;
+    float quadraticAttenuation;
+};
+
+// example 7.9
+struct MaterialProperties {
+    vec3 emission;
+    vec3 ambient;
+    vec3 diffuse;
+    vec3 specular;
+    float shininess;
+};
+
+const int MaxLights = 8;
+uniform LightProperties lights[MaxLights];
+
+const int MaxMaterials = 1;
+uniform MaterialProperties materials[MaxMaterials];
+
+uniform float Strength;
+uniform vec3 EyeDirection;
+
+in vec4 Position;
+in vec3 Normal;
+in vec4 Colour;
+flat in int MatIndex;
+
+out vec4 FragColour;
+
+void main() {
+    vec3 scatteredLight = vec3(0.0);
+    vec3 reflectedLight = vec3(0.0);
+
+    for(int light = 0; light < MaxLights; ++light) {
+        if ( !lights[light].isEnabled ) continue;
+
+        vec3 halfVector;
+        vec3 lightDirection = lights[light].position;
+        float attenuation = 1.0;
+
+        float specular = 0;
+        if ( lights[light].isLocal ) {
+            lightDirection = lightDirection - vec3(Position);
+            float lightDistance = length(lightDirection);
+            lightDirection = lightDirection / lightDistance;
+
+            attenuation = 1.0 /
+                            lights[light].constantAttenuation +
+                            lights[light].linearAttenuation * lightDistance +
+                            lights[light].quadraticAttenuation * lightDistance * lightDistance;
+
+            if ( lights[light].isSpot ) {
+                float spotCos = dot(lightDirection, -lights[light].coneDirection);
+                if ( spotCos < lights[light].spotCosCustoff )
+                    attenuation = 0.0;
+                else
+                    attenuation *= pow(spotCos, lights[light].spotExponent);
+            }
+
+            halfVector = normalize(lightDirection + EyeDirection);
+            specular = max(0.0, dot(Normal, halfVector));
+        } else {
+            //halfVector = lights[light].halfVector;
+            halfVector = reflect(-lightDirection, Normal);
+            specular = max(0.0, dot(EyeDirection, halfVector));
+        }
+
+        float diffuse = max(0.0, dot(Normal, lightDirection));
+
+
+        if (diffuse == 0.0)
+            specular = 0.0;
+        else
+            specular = pow(specular, materials[MatIndex].shininess) * Strength;
+
+        scatteredLight += lights[light].ambient * materials[MatIndex].ambient * attenuation +
+                          lights[light].colour * materials[MatIndex].diffuse * diffuse * attenuation;
+        reflectedLight += lights[light].colour * materials[MatIndex].specular *
+                            specular * attenuation;
+    }
+
+    vec3 rgb = min( materials[MatIndex].emission + Colour.rgb * scatteredLight + reflectedLight, vec3(1.0));
+    FragColour = vec4(rgb, 1.0);
+}
\ No newline at end of file
diff --git a/demo/data/redbook/lighting_vert.glsl b/demo/data/redbook/lighting_vert.glsl
new file mode 100644
index 0000000..435d901
--- /dev/null
+++ b/demo/data/redbook/lighting_vert.glsl
@@ -0,0 +1,26 @@
+/**
+ * OpenGL RedBook Shader.
+ * Example 7.8
+ */
+#version 330 core
+
+uniform mat4 MVPMatrix;
+uniform mat4 MVMatrix;
+uniform mat3 NormalMatrix;
+
+in vec4 VertexPosition;
+in vec3 VertexNormal;
+in vec4 VertexColour;
+
+out vec4 Position;
+out vec3 Normal;
+out vec4 Colour;
+out int matIndex;
+
+void main() {
+    Colour = vec4(1.0, 1.0, 1.0, 1.0f);
+    Normal = NormalMatrix * VertexNormal;
+    Position = MVMatrix * VertexPosition;
+    gl_Position = MVPMatrix * VertexPosition;
+    matIndex = 0;
+}
\ No newline at end of file
diff --git a/demo/data/rollball.yml b/demo/data/rollball.yml
index defd6c0..c1258bf 100644
--- a/demo/data/rollball.yml
+++ b/demo/data/rollball.yml
@@ -4,7 +4,7 @@ prefabs:
     components:
       Transform:
       StaticMesh:
-        pipeline: phong
+        pipeline: redbook/lighting
         shape:
           type: box
           scale: [1.0, 5.0, 41]
@@ -18,7 +18,7 @@ prefabs:
     components:
       Transform:
       StaticMesh:
-        pipeline: phong
+        pipeline: redbook/lighting
         shape:
           type: box
           scale: [39, 5, 1]
@@ -31,7 +31,7 @@ prefabs:
     components:
       Transform:
       StaticMesh:
-        pipeline: phong
+        pipeline: redbook/lighting
         shape:
           type: box # we don't (currently) support planes...
           scale: [39, 0.5, 39]
@@ -44,14 +44,14 @@ prefabs:
     components:
       Transform:
       StaticMesh:
-        pipeline: phong
+        pipeline: redbook/lighting
         shape:
           type: sphere
       gfx::material:
         ambient: [0.25, 0.25, 0.25]
         diffuse: [0.4, 0.4, 0.4]
         specular: [0.774597,0.774597,0.774597]
-        shininess: 0.6
+        shininess: 16
       phys::Body:
         shape:
           type: sphere
@@ -60,14 +60,14 @@ prefabs:
     components:
       Transform:
       StaticMesh:
-        pipeline: phong
+        pipeline: redbook/lighting
         shape:
           type: box
       gfx::material:
         ambient: [0.0215, 0.1754, 0.0215]
         diffuse: [1, 1, 1]
         specular: [0.0633, 0.727811, 0.633]
-        shininess: 0.6
+        shininess: 16
       phys::Body:
         type: kinematic
         shape:
diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp
index 45a42aa..f4fe812 100644
--- a/fggl/gfx/ogl/renderer.cpp
+++ b/fggl/gfx/ogl/renderer.cpp
@@ -169,8 +169,8 @@ namespace fggl::gfx {
 			m_canvasPipeline = m_cache->get(ogl4::FALLBACK_CANVAS_PIPELINE);
 		}
 
-		ShaderConfig shader3DConfig = ShaderFromName("phong");
-		m_cache->load(shader3DConfig);
+		m_cache->load(ShaderFromName("phong"));
+		m_cache->load(ShaderFromName("redbook/lighting"));
 
 		// rendering helpers
 		m_canvasRenderer = std::make_unique<ogl4::CanvasRenderer>(fonts);
diff --git a/fggl/gfx/ogl/shader.cpp b/fggl/gfx/ogl/shader.cpp
index a38aec9..21403f4 100644
--- a/fggl/gfx/ogl/shader.cpp
+++ b/fggl/gfx/ogl/shader.cpp
@@ -62,6 +62,9 @@ ShaderCache::ShaderCache(fggl::data::Storage* storage) : m_storage(storage), m_s
 	if ( !GLAD_GL_ARB_get_program_binary ) {
 		spdlog::warn("the graphics card doesn support shader caching, disabling");
 		m_binary = false;
+	} else {
+		// debug - disable shader cache
+		m_binary = false;
 	}
 
 	if ( GLAD_GL_ARB_shading_language_include ) {
diff --git a/fggl/gfx/ogl4/models.cpp b/fggl/gfx/ogl4/models.cpp
index ed30c5c..62b3e0d 100644
--- a/fggl/gfx/ogl4/models.cpp
+++ b/fggl/gfx/ogl4/models.cpp
@@ -75,7 +75,15 @@ namespace fggl::gfx::ogl4 {
 
 			auto* meshComp = world.get<data::StaticMesh>(renderable);
 			auto* modelComp = world.add<StaticModel>(renderable);
-			setupComponent(modelComp, m_phong, meshComp->mesh);
+
+			auto shader = m_phong;
+			try {
+				shader = std::make_shared<ogl::Shader>( m_shaders->get( meshComp->pipeline ) );
+			} catch ( std::out_of_range& e) {
+				debug::log(debug::Level::warning, "Could not find shader: {}", meshComp->pipeline);
+			}
+
+			setupComponent(modelComp, shader, meshComp->mesh);
 
 			// no active model, we need to resolve/load one.
 			spdlog::info("looks like {} needs a static mesh", renderable);
@@ -153,27 +161,79 @@ namespace fggl::gfx::ogl4 {
 					// new shader - need to re-send the view and projection matrices
 					shader = model->pipeline;
 					shader->use();
-					shader->setUniformMtx(shader->uniform("view"), viewMatrix);
-					shader->setUniformMtx(shader->uniform("projection"), projectionMatrix);
+					if ( shader->hasUniform("projection") ) {
+						shader->setUniformMtx(shader->uniform("view"), viewMatrix);
+						shader->setUniformMtx(shader->uniform("projection"), projectionMatrix);
+					}
 				}
 
 				// set model transform
 				auto* transform = world.get<math::Transform>(entity);
-				shader->setUniformMtx(shader->uniform("model"), transform->model());
+				if (shader->hasUniform("model")) {
+					shader->setUniformMtx(shader->uniform("model"), transform->model());
+				} else {
+					shader->setUniformMtx(shader->uniform("MVPMatrix"), projectionMatrix * viewMatrix * transform->model());
+					shader->setUniformMtx(shader->uniform("MVMatrix"), viewMatrix * transform->model());
+
+					auto normalMatrix = glm::mat3(glm::transpose(inverse(transform->model())));
+					shader->setUniformMtx(shader->uniform("NormalMatrix"), normalMatrix);
+				}
+
+				// setup lighting mode
+				if ( shader->hasUniform("lights[0].isEnabled") ) {
+					bool local = true;
+
+					shader->setUniformI(shader->uniform("lights[0].isEnabled"), 1);
+					shader->setUniformI(shader->uniform("lights[0].isLocal"), local);
+					shader->setUniformI(shader->uniform("lights[0].isSpot"), 0);
+
+					shader->setUniformF(shader->uniform("lights[0].constantAttenuation"), 5.0f);
+					shader->setUniformF(shader->uniform("lights[0].linearAttenuation"), 0.0f);
+					shader->setUniformF(shader->uniform("lights[0].quadraticAttenuation"), 0.0f);
+
+					shader->setUniformF(shader->uniform("Strength"), 0.6F);
+
+					if (!local) {
+						lightPos = glm::normalize(lightPos);
+						auto viewDir = glm::normalize(camTransform->origin() - transform->origin());
+						auto halfVector = glm::normalize(lightPos + viewDir);
+						shader->setUniformF(shader->uniform("lights[0].halfVector"), halfVector );
+						shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
+						shader->setUniformF(shader->uniform("lights[0].position"), lightPos);
+					} else {
+						auto camModelView = (viewMatrix * camTransform->model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
+						auto modelModelView = (viewMatrix * transform->model() * math::vec4(0.0f, 0.0f, 0.0f, 1.0f));
+						math::vec3 viewDir = glm::normalize(camModelView - modelModelView);
+						shader->setUniformF(shader->uniform("EyeDirection"), viewDir);
+
+						shader->setUniformF(shader->uniform("lights[0].position"), math::vec3(viewMatrix * math::vec4(lightPos, 1.0f)));
+					}
+
+					shader->setUniformF(shader->uniform("lights[0].ambient"), {0.0f, 0.5f, 0.0f});
+					shader->setUniformF(shader->uniform("lights[0].colour"), {0.5f, 0.5f, 0.5f});
+				}
 
 				// material detection with fallback
 				auto* material = &gfx::DEFAULT_MATERIAL;
 				if ( world.has<PhongMaterial>(entity) ) {
 					material = world.get<PhongMaterial>(entity);
 				}
-				shader->setUniformF(shader->uniform("material.ambient"), material->ambient);
-				shader->setUniformF(shader->uniform("material.diffuse"), material->diffuse);
-				shader->setUniformF(shader->uniform("material.specular"), material->specular);
-				shader->setUniformF(shader->uniform("material.shininess"), material->shininess);
-
-				auto lightPosIdx = shader->uniform("lightPos");
-				if ( lightPosIdx != -1 ) {
-					shader->setUniformF(lightPosIdx, lightPos);
+
+				if ( shader->hasUniform("material.ambient") ) {
+					shader->setUniformF(shader->uniform("material.ambient"), material->ambient);
+					shader->setUniformF(shader->uniform("material.diffuse"), material->diffuse);
+					shader->setUniformF(shader->uniform("material.specular"), material->specular);
+					shader->setUniformF(shader->uniform("material.shininess"), material->shininess);
+				} else {
+					shader->setUniformF(shader->uniform("materials[0].emission"), material->emission);
+					shader->setUniformF(shader->uniform("materials[0].ambient"), material->ambient);
+					shader->setUniformF(shader->uniform("materials[0].diffuse"), material->diffuse);
+					shader->setUniformF(shader->uniform("materials[0].specular"), material->specular);
+					shader->setUniformF(shader->uniform("materials[0].shininess"), material->shininess);
+				}
+
+				if ( shader->hasUniform("lightPos")) {
+					shader->setUniformF( shader->uniform("lightPos"), lightPos);
 				}
 
 				auto vao = model->vao;
diff --git a/include/fggl/gfx/ogl/types.hpp b/include/fggl/gfx/ogl/types.hpp
index 25ae694..73b1b44 100644
--- a/include/fggl/gfx/ogl/types.hpp
+++ b/include/fggl/gfx/ogl/types.hpp
@@ -59,6 +59,11 @@ namespace fggl::gfx::ogl {
 				glUseProgram( m_obj );
 			}
 
+			inline bool hasUniform(const std::string_view& name) const {
+				auto location = glGetUniformLocation( m_obj, name.data() );
+				return location != -1;
+			}
+
 			inline Location uniform(const std::string_view& name) const {
 				auto location = glGetUniformLocation( m_obj, name.data() );
 				if ( location == -1 ) {
diff --git a/include/fggl/gfx/phong.hpp b/include/fggl/gfx/phong.hpp
index 5c10908..e62773e 100644
--- a/include/fggl/gfx/phong.hpp
+++ b/include/fggl/gfx/phong.hpp
@@ -25,47 +25,47 @@ namespace fggl::gfx {
 
 	struct PhongMaterial {
 		constexpr static const char* name = "gfx::material";
+		math::vec3 emission;
 		math::vec3 ambient;
 		math::vec3 diffuse;
 		math::vec3 specular;
 		float shininess;
 	};
 
+	constexpr math::vec3 DEFAULT_EMISSION {0.0F, 0.0F, 0.0F};
 	constexpr math::vec3 DEFAULT_AMBIENT { 0.05F, 0.05F, 0.05F};
 	constexpr math::vec3 DEFAULT_DIFFUSE { 0.5F, 0.5F, 0.5F};
 	constexpr math::vec3 DEFAULT_SPECULAR { 0.7F, 0.7F, 0.7F};
-	constexpr float DEFAULT_SHININESS = .078125F;
+	//constexpr float DEFAULT_SHININESS = .078125F;
+	constexpr float DEFAULT_SHININESS = 16.0F;
 
 	constexpr PhongMaterial DEFAULT_MATERIAL {
+		DEFAULT_EMISSION,
 		DEFAULT_AMBIENT,
 		DEFAULT_DIFFUSE,
 		DEFAULT_SPECULAR,
 		DEFAULT_SHININESS
 	};
 
-
-	enum class LightType {
-			Directional,
-			Point,
-			Spot
-	};
-
 	struct Light {
 		constexpr static const char* name = "gfx::light";
-		LightType type;
-		math::vec3 position;
+		bool enabled;
+		bool local;
+		bool spot;
 
-		// colours
 		math::vec3 ambient;
-		math::vec3 diffuse;
-		math::vec3 specular;
+		math::vec3 colour;
+		math::vec3 position;
+		math::vec3 halfVector;
+		math::vec3 coneDirection;
 
-		// distance data
-		math::vec3 falloffs; // (constant, linear, quadratic)
-		float cutOff;
-		float outerCutOff;
+		float spotCosCutoff;
+		float spotExponent;
+		float constantAttenuation;
+		float linearAttenuation;
+		float quadraticAttenuation;
 	};
 
-} // namesapce fggl::gfx
+} // namespace fggl::gfx
 
 #endif //FGGL_GFX_PHONG_HPP
-- 
GitLab