summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2023-01-05 19:45:36 +0000
committerDan Goodliffe <dan@randomdan.homeip.net>2023-01-05 19:45:36 +0000
commit7c1e9b25fa4062a69ea00c20cdbcc745d1b82659 (patch)
tree03a200a82211016a70420930ee88d2afd01f60a5
parentInclude uniform name in required uniform does not exist error message (diff)
downloadilt-7c1e9b25fa4062a69ea00c20cdbcc745d1b82659.tar.bz2
ilt-7c1e9b25fa4062a69ea00c20cdbcc745d1b82659.tar.xz
ilt-7c1e9b25fa4062a69ea00c20cdbcc745d1b82659.zip
Add rendering support for spot lights
-rw-r--r--gfx/gl/sceneShader.cpp32
-rw-r--r--gfx/gl/sceneShader.h16
-rw-r--r--gfx/gl/shaders/spotLight.fs34
-rw-r--r--gfx/gl/shaders/spotLight.gs63
-rw-r--r--gfx/gl/shaders/spotLight.vs23
-rw-r--r--test/test-render.cpp27
6 files changed, 193 insertions, 2 deletions
diff --git a/gfx/gl/sceneShader.cpp b/gfx/gl/sceneShader.cpp
index d0b26c8..981e7df 100644
--- a/gfx/gl/sceneShader.cpp
+++ b/gfx/gl/sceneShader.cpp
@@ -3,11 +3,14 @@
#include <gfx/gl/shaders/fs-basicShader.h>
#include <gfx/gl/shaders/fs-landmassShader.h>
#include <gfx/gl/shaders/fs-pointLight.h>
+#include <gfx/gl/shaders/fs-spotLight.h>
#include <gfx/gl/shaders/fs-waterShader.h>
#include <gfx/gl/shaders/gs-pointLight.h>
+#include <gfx/gl/shaders/gs-spotLight.h>
#include <gfx/gl/shaders/vs-basicShader.h>
#include <gfx/gl/shaders/vs-landmassShader.h>
#include <gfx/gl/shaders/vs-pointLight.h>
+#include <gfx/gl/shaders/vs-spotLight.h>
#include <gfx/gl/shaders/vs-waterShader.h>
#include <gfx/gl/vertexArrayObject.hpp>
#include <glm/gtc/type_ptr.hpp>
@@ -23,7 +26,8 @@ SceneShader::SceneShader() :
void
SceneShader::setViewProjection(const glm::mat4 & viewProjection) const
{
- for (const auto & prog : std::array<const SceneProgram *, 5> {&basic, &water, &landmass, &absolute, &pointLight}) {
+ for (const auto & prog :
+ std::array<const SceneProgram *, 6> {&basic, &water, &landmass, &absolute, &pointLight, &spotLight}) {
prog->setViewProjection(viewProjection);
}
}
@@ -31,7 +35,8 @@ SceneShader::setViewProjection(const glm::mat4 & viewProjection) const
void
SceneShader::setViewPort(const glm::ivec4 & viewPort) const
{
- for (const auto & prog : std::array<const SceneProgram *, 5> {&basic, &water, &landmass, &absolute, &pointLight}) {
+ for (const auto & prog :
+ std::array<const SceneProgram *, 6> {&basic, &water, &landmass, &absolute, &pointLight, &spotLight}) {
prog->setViewPort(viewPort);
}
}
@@ -95,3 +100,26 @@ SceneShader::PointLightShader::add(const glm::vec3 & position, const glm::vec3 &
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec3), glm::value_ptr(position));
glDrawArrays(GL_POINTS, 0, 1);
}
+
+SceneShader::SpotLightShader::SpotLightShader() :
+ SceneProgram {spotLight_vs, spotLight_gs, spotLight_fs}, colourLoc {*this, "colour"}, kqLoc {*this, "kq"},
+ arcLoc {*this, "arc"}
+{
+ using v3pair = std::pair<glm::vec3, glm::vec3>;
+ VertexArrayObject<v3pair>::configure<&v3pair::first, &v3pair::second>(va, b);
+}
+
+void
+SceneShader::SpotLightShader::add(const glm::vec3 & position, const glm::vec3 & direction, const glm::vec3 & colour,
+ const float kq, const float arc) const
+{
+ Program::use();
+ glBindVertexArray(va);
+ glBindBuffer(GL_ARRAY_BUFFER, b);
+ glUniform3fv(colourLoc, 1, glm::value_ptr(colour));
+ glUniform1f(kqLoc, kq);
+ glUniform1f(arcLoc, arc);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(glm::vec3), glm::value_ptr(position));
+ glBufferSubData(GL_ARRAY_BUFFER, sizeof(glm::vec3), sizeof(glm::vec3), glm::value_ptr(direction));
+ glDrawArrays(GL_POINTS, 0, 1);
+}
diff --git a/gfx/gl/sceneShader.h b/gfx/gl/sceneShader.h
index 9db6ebf..ed1bb79 100644
--- a/gfx/gl/sceneShader.h
+++ b/gfx/gl/sceneShader.h
@@ -61,6 +61,21 @@ class SceneShader {
glBuffer b;
};
+ class SpotLightShader : public SceneProgram {
+ public:
+ SpotLightShader();
+
+ void add(const glm::vec3 & position, const glm::vec3 & direction, const glm::vec3 & colour, const float kq,
+ const float arc) const;
+
+ private:
+ UniformLocation colourLoc;
+ UniformLocation kqLoc;
+ UniformLocation arcLoc;
+ glVertexArray va;
+ glBuffer b;
+ };
+
public:
SceneShader();
@@ -68,6 +83,7 @@ public:
WaterProgram water;
AbsolutePosProgram landmass, absolute;
PointLightShader pointLight;
+ SpotLightShader spotLight;
void setViewProjection(const glm::mat4 & viewProjection) const;
void setViewPort(const glm::ivec4 & viewPort) const;
diff --git a/gfx/gl/shaders/spotLight.fs b/gfx/gl/shaders/spotLight.fs
new file mode 100644
index 0000000..add86fd
--- /dev/null
+++ b/gfx/gl/shaders/spotLight.fs
@@ -0,0 +1,34 @@
+#version 330 core
+#extension GL_ARB_shading_language_420pack : enable
+
+out vec3 FragColor;
+
+layout(binding = 0) uniform sampler2D gPosition;
+layout(binding = 1) uniform sampler2D gNormal;
+uniform ivec4 viewPort;
+uniform vec3 colour;
+uniform float kq;
+in vec4 geo_centre;
+in vec4 geo_direction;
+
+void
+main()
+{
+ const vec2 texCoord = gl_FragCoord.xy / viewPort.zw;
+ const vec3 position = texture(gPosition, texCoord).xyz;
+ const vec3 lightv = position - geo_centre.xyz;
+ const float lightDist = length(lightv);
+ if (lightDist > geo_centre.w) {
+ discard;
+ }
+ const vec3 lightDirection = normalize(lightv);
+ if (dot(lightDirection, geo_direction.xyz) < geo_direction.w) {
+ discard;
+ }
+ const vec3 normal = texture(gNormal, texCoord).xyz;
+ const float normalDot = dot(-lightDirection, normal);
+ if (normalDot < 0) {
+ discard;
+ }
+ FragColor = (colour * normalDot) / (1 + (kq * pow(lightDist, 2)));
+}
diff --git a/gfx/gl/shaders/spotLight.gs b/gfx/gl/shaders/spotLight.gs
new file mode 100644
index 0000000..ad65675
--- /dev/null
+++ b/gfx/gl/shaders/spotLight.gs
@@ -0,0 +1,63 @@
+#version 330 core
+#extension GL_ARB_enhanced_layouts : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+const vec3[] pyramid = vec3[]( // four-sided
+ vec3(0, 0, 0), // Apex
+ vec3(-1, 1, 1), // Back-left
+ vec3(-1, -1, 1), // Front-left
+ vec3(1, 1, 1), // Back-right
+ vec3(1, -1, 1) // Front-right
+);
+uniform mat4 viewProjection;
+uniform float arc;
+
+in vec3 position[];
+in vec3 direction[];
+in float size[];
+in float cosarc[];
+layout(points) in;
+
+layout(triangle_strip, max_vertices = 8) out;
+out vec4 geo_centre;
+out vec4 geo_direction;
+
+vec3
+perp(vec3 a)
+{
+ return normalize(a.x != 0 ? vec3((a.y + a.z) / -a.x, 1, 1)
+ : a.y != 0 ? vec3(1, (a.x + a.z) / -a.y, 1)
+ : vec3(1, 1, (a.x + a.y) / -a.z));
+}
+
+void
+doVertex(vec4 ndcpos)
+{
+ gl_Position = ndcpos;
+ geo_centre = vec4(position[0], size[0]);
+ geo_direction = vec4(direction[0], cosarc[0]);
+ EmitVertex();
+}
+
+void
+main()
+{
+ const float base = size[0] * tan(arc / 2);
+ const vec3 offx = perp(direction[0]);
+ const vec3 offy = cross(direction[0], offx);
+ vec4 out_py[pyramid.length()];
+ for (int i = 0; i < pyramid.length(); ++i) {
+ const vec3 p = pyramid[i];
+ const vec3 edge = (offx * base * p.x) + (offy * base * p.y) + (direction[0] * size[0] * p.z);
+ out_py[i] = viewProjection * (gl_in[0].gl_Position + vec4(edge, 1));
+ }
+ doVertex(out_py[3]);
+ doVertex(out_py[0]);
+ doVertex(out_py[1]);
+ doVertex(out_py[2]);
+ doVertex(out_py[3]);
+ doVertex(out_py[4]);
+ doVertex(out_py[0]);
+ doVertex(out_py[2]);
+ EndPrimitive();
+}
diff --git a/gfx/gl/shaders/spotLight.vs b/gfx/gl/shaders/spotLight.vs
new file mode 100644
index 0000000..e648553
--- /dev/null
+++ b/gfx/gl/shaders/spotLight.vs
@@ -0,0 +1,23 @@
+#version 330 core
+
+layout(location = 0) in vec3 v_position;
+layout(location = 1) in vec3 v_direction;
+
+uniform vec3 colour;
+uniform float kq;
+uniform float arc;
+
+out vec3 position;
+out vec3 direction;
+out float size;
+out float cosarc;
+
+void
+main()
+{
+ position = v_position;
+ direction = normalize(v_direction);
+ size = (8 * sqrt(max(max(colour.r, colour.g), colour.b))) / sqrt(kq);
+ cosarc = cos(arc / 2);
+ gl_Position = vec4(position, 0);
+}
diff --git a/test/test-render.cpp b/test/test-render.cpp
index 76b8e6a..1cad732 100644
--- a/test/test-render.cpp
+++ b/test/test-render.cpp
@@ -104,4 +104,31 @@ BOOST_AUTO_TEST_CASE(pointlight)
Texture::save(outImage, size, "/tmp/pointlight.tga");
}
+BOOST_AUTO_TEST_CASE(spotlight)
+{
+ SceneRenderer ss {size, output};
+ ss.camera.setView({-10, -10, 60}, glm::normalize(glm::vec3 {1, 1, -0.5F}));
+ class PointLightScene : public TestScene {
+ public:
+ void
+ environment(const SceneShader &, const SceneRenderer & r) const override
+ {
+ r.setAmbientLight({0.2F, 0.2F, 0.2F});
+ r.setDirectionalLight({0.2F, 0.2F, 0.2F}, west + down, *this);
+ }
+ void
+ lights(const SceneShader & shader) const override
+ {
+ shader.spotLight.add({50, 50, 15}, down, {1.0, 1.0, 1.0}, 0.01, 1);
+ shader.spotLight.add({51, 59.5, 1}, north, {1.0, 1.0, 1.0}, 0.001, .5);
+ shader.spotLight.add({53, 59.5, 1}, north, {1.0, 1.0, 1.0}, 0.001, .5);
+ shader.spotLight.add({60, 50, 3}, north + east, {1.0, 1.0, 1.0}, 0.0001, .7);
+ }
+ };
+ PointLightScene scene;
+ ss.render(scene);
+ glDisable(GL_DEBUG_OUTPUT);
+ Texture::save(outImage, size, "/tmp/spotlight.tga");
+}
+
BOOST_AUTO_TEST_SUITE_END();