From 7c1e9b25fa4062a69ea00c20cdbcc745d1b82659 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 5 Jan 2023 19:45:36 +0000 Subject: Add rendering support for spot lights --- gfx/gl/sceneShader.cpp | 32 +++++++++++++++++++++-- gfx/gl/sceneShader.h | 16 ++++++++++++ gfx/gl/shaders/spotLight.fs | 34 ++++++++++++++++++++++++ gfx/gl/shaders/spotLight.gs | 63 +++++++++++++++++++++++++++++++++++++++++++++ gfx/gl/shaders/spotLight.vs | 23 +++++++++++++++++ test/test-render.cpp | 27 +++++++++++++++++++ 6 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 gfx/gl/shaders/spotLight.fs create mode 100644 gfx/gl/shaders/spotLight.gs create mode 100644 gfx/gl/shaders/spotLight.vs 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 #include #include +#include #include #include +#include #include #include #include +#include #include #include #include @@ -23,7 +26,8 @@ SceneShader::SceneShader() : void SceneShader::setViewProjection(const glm::mat4 & viewProjection) const { - for (const auto & prog : std::array {&basic, &water, &landmass, &absolute, &pointLight}) { + for (const auto & prog : + std::array {&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 {&basic, &water, &landmass, &absolute, &pointLight}) { + for (const auto & prog : + std::array {&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; + VertexArrayObject::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(); -- cgit v1.2.3