diff --git a/install/gl/shadowmap_fp.glsl b/install/gl/shadowmap_fp.glsl new file mode 100644 index 0000000000..f674ae29eb --- /dev/null +++ b/install/gl/shadowmap_fp.glsl @@ -0,0 +1,28 @@ +#version 120 + +uniform sampler2D u_Diffuse; +uniform float u_AlphaTest; +uniform mat4 u_ObjectTransform; + +// The final diffuse texture coordinate at this vertex, calculated in the vertex shader +varying vec2 var_TexDiffuse; + +void main() +{ + if (u_AlphaTest < 0) + { + gl_FragColor.a = 1.0; + gl_FragColor.rgb = vec3(1.0, 1.0, 0.0); + } + else + { + vec4 tex = texture2D(u_Diffuse, var_TexDiffuse); + + if (tex.a <= u_AlphaTest) + { + discard; + } + + gl_FragColor = tex; + } +} diff --git a/install/gl/shadowmap_vs.glsl b/install/gl/shadowmap_vs.glsl new file mode 100644 index 0000000000..86a0c1fe84 --- /dev/null +++ b/install/gl/shadowmap_vs.glsl @@ -0,0 +1,23 @@ +#version 140 + +in vec4 attr_Position; // bound to attribute 0 in source, in object space +in vec4 attr_TexCoord; // bound to attribute 8 in source + +uniform mat4 u_ObjectTransform; // object transform (object2world) + +// The two top-rows of the diffuse stage texture transformation matrix +uniform vec4 u_DiffuseTextureMatrix[2]; + +// The final diffuse texture coordinate at this vertex +varying vec2 var_TexDiffuse; + +void main() +{ + // Apply the supplied object transform to the incoming vertex + // transform vertex position into homogenous clip-space + gl_Position = u_ModelViewProjection * u_ObjectTransform * attr_Position; + + // Apply the stage texture transform to the incoming tex coord, component wise + var_TexDiffuse.x = dot(u_DiffuseTextureMatrix[0], attr_TexCoord); + var_TexDiffuse.y = dot(u_DiffuseTextureMatrix[1], attr_TexCoord); +} diff --git a/libs/render/Rectangle.h b/libs/render/Rectangle.h new file mode 100644 index 0000000000..a02ebbf78a --- /dev/null +++ b/libs/render/Rectangle.h @@ -0,0 +1,15 @@ +#pragma once + +namespace render +{ + +// Defines a rectangular area within a larger one +struct Rectangle +{ + int x; + int y; + int width; + int height; +}; + +} diff --git a/radiantcore/CMakeLists.txt b/radiantcore/CMakeLists.txt index ee35613526..87771f036f 100644 --- a/radiantcore/CMakeLists.txt +++ b/radiantcore/CMakeLists.txt @@ -217,6 +217,7 @@ add_library(radiantcore MODULE rendersystem/backend/glprogram/GLSLBumpProgram.cpp rendersystem/backend/glprogram/GLSLCubeMapProgram.cpp rendersystem/backend/glprogram/GLSLDepthFillAlphaProgram.cpp + rendersystem/backend/glprogram/ShadowMapProgram.cpp rendersystem/backend/BuiltInShader.cpp rendersystem/backend/ColourShader.cpp rendersystem/backend/LightInteractions.cpp diff --git a/radiantcore/rendersystem/backend/FrameBuffer.h b/radiantcore/rendersystem/backend/FrameBuffer.h index 91776919f5..bf3dde467d 100644 --- a/radiantcore/rendersystem/backend/FrameBuffer.h +++ b/radiantcore/rendersystem/backend/FrameBuffer.h @@ -11,11 +11,13 @@ class FrameBuffer private: GLuint _fbo; std::size_t _width; + std::size_t _height; GLuint _textureNumber; FrameBuffer() : _fbo(0), _width(0), + _height(0), _textureNumber(0) {} @@ -33,6 +35,16 @@ class FrameBuffer _fbo = 0; } + std::size_t getWidth() const + { + return _width; + } + + std::size_t getHeight() const + { + return _height; + } + void bind() { glBindFramebuffer(GL_FRAMEBUFFER, _fbo); @@ -62,7 +74,11 @@ class FrameBuffer static_cast(size), static_cast(size), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); + // Attach the texture to the FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, buffer->_textureNumber, 0); + buffer->_width = size; + buffer->_height = size; return buffer; } diff --git a/radiantcore/rendersystem/backend/GLProgramFactory.cpp b/radiantcore/rendersystem/backend/GLProgramFactory.cpp index 239cd6d5e0..f09267b10a 100644 --- a/radiantcore/rendersystem/backend/GLProgramFactory.cpp +++ b/radiantcore/rendersystem/backend/GLProgramFactory.cpp @@ -5,6 +5,7 @@ #include "glprogram/GLSLCubeMapProgram.h" #include "glprogram/GLSLBumpProgram.h" #include "glprogram/GenericVFPProgram.h" +#include "glprogram/ShadowMapProgram.h" #include "itextstream.h" #include "iregistry.h" @@ -27,6 +28,7 @@ GLProgramFactory::GLProgramFactory() _builtInPrograms[ShaderProgram::DepthFillAlpha] = std::make_shared(); _builtInPrograms[ShaderProgram::Interaction] = std::make_shared(); _builtInPrograms[ShaderProgram::CubeMap] = std::make_shared(); + _builtInPrograms[ShaderProgram::ShadowMap] = std::make_shared(); } GLProgram* GLProgramFactory::getBuiltInProgram(ShaderProgram builtInProgram) diff --git a/radiantcore/rendersystem/backend/GLProgramFactory.h b/radiantcore/rendersystem/backend/GLProgramFactory.h index dd96e5e9bb..a4144230d8 100644 --- a/radiantcore/rendersystem/backend/GLProgramFactory.h +++ b/radiantcore/rendersystem/backend/GLProgramFactory.h @@ -18,6 +18,7 @@ enum class ShaderProgram DepthFillAlpha, Interaction, CubeMap, + ShadowMap, }; /** diff --git a/radiantcore/rendersystem/backend/LightInteractions.cpp b/radiantcore/rendersystem/backend/LightInteractions.cpp index 727f1a144f..814e8cfdb4 100644 --- a/radiantcore/rendersystem/backend/LightInteractions.cpp +++ b/radiantcore/rendersystem/backend/LightInteractions.cpp @@ -3,6 +3,7 @@ #include "OpenGLShader.h" #include "ObjectRenderer.h" #include "glprogram/GLSLDepthFillAlphaProgram.h" +#include "glprogram/ShadowMapProgram.h" namespace render { @@ -155,9 +156,91 @@ void LightInteractions::fillDepthBuffer(OpenGLState& state, GLSLDepthFillAlphaPr } } -void LightInteractions::drawShadowMap(OpenGLState& state) +void LightInteractions::drawShadowMap(OpenGLState& state, const Rectangle& rectangle, ShadowMapProgram& program) { - + // Enable GL state and save to state + glDepthMask(true); + state.setRenderFlag(RENDER_DEPTHWRITE); + + glDepthFunc(GL_LEQUAL); + state.setDepthFunc(GL_LEQUAL); + + // Set up the viewport to write to a specific area within the shadow map texture + glViewport(rectangle.x, rectangle.y, 6 * rectangle.width, rectangle.width); + glClear(GL_DEPTH_BUFFER_BIT); + + std::vector untransformedObjects; + untransformedObjects.reserve(1000); + + // No alpha test for now + program.setAlphaTest(-1); + + // Render all the objects that have a depth filling stage + for (const auto& [entity, objectsByShader] : _objectsByEntity) + { + for (const auto& [shader, objects] : objectsByShader) + { + auto depthFillPass = shader->getDepthFillPass(); + + if (!depthFillPass) continue; + +#if 0 + const auto& material = shader->getMaterial(); + assert(material); + + auto coverage = material->getCoverage(); + + // Skip translucent materials + if (coverage == Material::MC_TRANSLUCENT) continue; + + if (coverage == Material::MC_PERFORATED) + { + // Evaluate the shader stages of this material + depthFillPass->evaluateShaderStages(renderTime, entity); + + // Apply the alpha test value, it might be affected by time and entity parms + program.setAlphaTest(depthFillPass->getAlphaTestValue()); + + // If there's a diffuse stage, apply the correct texture + OpenGLState::SetTextureState(state.texture0, depthFillPass->state().texture0, GL_TEXTURE0, GL_TEXTURE_2D); + + // Set evaluated stage texture transformation matrix to the GLSL uniform + program.setDiffuseTextureTransform(depthFillPass->getDiffuseTextureTransform()); + } + else + { + // No alpha test on this material, pass -1 to deactivate texture sampling + // in the GLSL program + program.setAlphaTest(-1); + } +#endif + + for (const auto& object : objects) + { + // We submit all objects with an identity matrix in a single multi draw call + if (!object.get().isOriented()) + { + untransformedObjects.push_back(object.get().getStorageLocation()); + continue; + } + + program.setObjectTransform(object.get().getObjectTransform()); + + ObjectRenderer::SubmitGeometry(object.get().getStorageLocation(), GL_TRIANGLES, _store); + ++_depthDrawCalls; + } + + if (!untransformedObjects.empty()) + { + program.setObjectTransform(Matrix4::getIdentity()); + + ObjectRenderer::SubmitGeometry(untransformedObjects, GL_TRIANGLES, _store); + ++_depthDrawCalls; + + untransformedObjects.clear(); + } + } + } } void LightInteractions::drawInteractions(OpenGLState& state, GLSLBumpProgram& program, diff --git a/radiantcore/rendersystem/backend/LightInteractions.h b/radiantcore/rendersystem/backend/LightInteractions.h index c71a12c946..ba936f2213 100644 --- a/radiantcore/rendersystem/backend/LightInteractions.h +++ b/radiantcore/rendersystem/backend/LightInteractions.h @@ -7,6 +7,7 @@ #include "isurfacerenderer.h" #include "irenderableobject.h" #include "irenderview.h" +#include "render/Rectangle.h" namespace render { @@ -15,6 +16,7 @@ class OpenGLState; class OpenGLShader; class GLSLDepthFillAlphaProgram; class GLSLBumpProgram; +class ShadowMapProgram; /** * Defines interactions between a light and one or more entity renderables @@ -91,7 +93,7 @@ class LightInteractions void fillDepthBuffer(OpenGLState& state, GLSLDepthFillAlphaProgram& program, const IRenderView& view, std::size_t renderTime, std::vector& untransformedObjectsWithoutAlphaTest); - void drawShadowMap(OpenGLState& state); + void drawShadowMap(OpenGLState& state, const Rectangle& rectangle, ShadowMapProgram& program); void drawInteractions(OpenGLState& state, GLSLBumpProgram& program, const IRenderView& view, std::size_t renderTime); }; diff --git a/radiantcore/rendersystem/backend/LightingModeRenderer.cpp b/radiantcore/rendersystem/backend/LightingModeRenderer.cpp index b5bed67412..f7843326f2 100644 --- a/radiantcore/rendersystem/backend/LightingModeRenderer.cpp +++ b/radiantcore/rendersystem/backend/LightingModeRenderer.cpp @@ -1,5 +1,6 @@ #include "LightingModeRenderer.h" +#include "GLProgramFactory.h" #include "LightingModeRenderResult.h" #include "LightInteractions.h" #include "OpenGLShaderPass.h" @@ -20,6 +21,22 @@ IRenderResult::Ptr LightingModeRenderer::render(RenderStateFlags globalFlagsMask if (!_shadowMapFbo) { _shadowMapFbo = FrameBuffer::CreateShadowMapBuffer(); + // Define the shadow atlas + _shadowMapAtlas.resize(4); + + for (int i = 0; i < 6; ++i) + { + _shadowMapAtlas[i].x = 0; + _shadowMapAtlas[i].y = static_cast((_shadowMapFbo->getHeight() / 6) * i); + _shadowMapAtlas[i].width = static_cast(_shadowMapFbo->getWidth() / 6); + _shadowMapAtlas[i].height = static_cast(_shadowMapFbo->getHeight() / 6); + } + } + + if (!_shadowMapProgram) + { + _shadowMapProgram = dynamic_cast(_programFactory.getBuiltInProgram(ShaderProgram::ShadowMap)); + assert(_shadowMapProgram); } // Construct default OpenGL state @@ -70,7 +87,7 @@ IRenderResult::Ptr LightingModeRenderer::render(RenderStateFlags globalFlagsMask { if (!interactionList.castsShadows()) continue; - interactionList.drawShadowMap(current); + interactionList.drawShadowMap(current, _shadowMapAtlas[0], *_shadowMapProgram); result->shadowDrawCalls += interactionList.getShadowMapDrawCalls(); break; } diff --git a/radiantcore/rendersystem/backend/LightingModeRenderer.h b/radiantcore/rendersystem/backend/LightingModeRenderer.h index 6aa5487659..31ebe8c964 100644 --- a/radiantcore/rendersystem/backend/LightingModeRenderer.h +++ b/radiantcore/rendersystem/backend/LightingModeRenderer.h @@ -5,6 +5,8 @@ #include "SceneRenderer.h" #include "igeometrystore.h" #include "FrameBuffer.h" +#include "render/Rectangle.h" +#include "glprogram/ShadowMapProgram.h" namespace render { @@ -29,6 +31,8 @@ class LightingModeRenderer final : std::vector _untransformedObjectsWithoutAlphaTest; FrameBuffer::Ptr _shadowMapFbo; + std::vector _shadowMapAtlas; + ShadowMapProgram* _shadowMapProgram; public: LightingModeRenderer(GLProgramFactory& programFactory, @@ -38,11 +42,21 @@ class LightingModeRenderer final : _programFactory(programFactory), _geometryStore(store), _lights(lights), - _entities(entities) + _entities(entities), + _shadowMapProgram(nullptr) { _untransformedObjectsWithoutAlphaTest.reserve(10000); } + ~LightingModeRenderer() + { + if (_shadowMapProgram) + { + _shadowMapProgram->destroy(); + _shadowMapProgram = nullptr; + } + } + IRenderResult::Ptr render(RenderStateFlags globalFlagsMask, const IRenderView& view, std::size_t time) override; private: diff --git a/radiantcore/rendersystem/backend/glprogram/ShadowMapProgram.cpp b/radiantcore/rendersystem/backend/glprogram/ShadowMapProgram.cpp new file mode 100644 index 0000000000..5482e511e2 --- /dev/null +++ b/radiantcore/rendersystem/backend/glprogram/ShadowMapProgram.cpp @@ -0,0 +1,79 @@ +#include "ShadowMapProgram.h" + +#include "GLProgramAttributes.h" +#include "../GLProgramFactory.h" +#include "debugging/gl.h" + +#include "itextstream.h" + +namespace render +{ + +namespace +{ + constexpr const char* const SHADOWMAP_VP_FILENAME = "shadowmap_vs.glsl"; + constexpr const char* const SHADOWMAP_FP_FILENAME = "shadowmap_fp.glsl"; +} + +void ShadowMapProgram::create() +{ + // Create the program object + rMessage() << "[renderer] Creating GLSL shadowmap program" << std::endl; + + _programObj = GLProgramFactory::createGLSLProgram( + SHADOWMAP_VP_FILENAME, SHADOWMAP_FP_FILENAME + ); + + glBindAttribLocation(_programObj, GLProgramAttribute::Position, "attr_Position"); + glBindAttribLocation(_programObj, GLProgramAttribute::TexCoord, "attr_TexCoord"); + + glLinkProgram(_programObj); + + debug::assertNoGlErrors(); + + _locAlphaTest = glGetUniformLocation(_programObj, "u_AlphaTest"); + _locObjectTransform = glGetUniformLocation(_programObj, "u_ObjectTransform"); + _locDiffuseTextureMatrix = glGetUniformLocation(_programObj, "u_DiffuseTextureMatrix"); + + glUseProgram(_programObj); + debug::assertNoGlErrors(); + + auto samplerLoc = glGetUniformLocation(_programObj, "u_Diffuse"); + glUniform1i(samplerLoc, 0); + + debug::assertNoGlErrors(); +} + +void ShadowMapProgram::enable() +{ + GLSLProgramBase::enable(); + + glEnableVertexAttribArray(GLProgramAttribute::Position); + glEnableVertexAttribArray(GLProgramAttribute::TexCoord); +} + +void ShadowMapProgram::disable() +{ + GLSLProgramBase::disable(); + + glDisableVertexAttribArray(GLProgramAttribute::Position); + glDisableVertexAttribArray(GLProgramAttribute::TexCoord); +} + +void ShadowMapProgram::setAlphaTest(float alphaTest) +{ + glUniform1f(_locAlphaTest, alphaTest); +} + +void ShadowMapProgram::setObjectTransform(const Matrix4& transform) +{ + loadMatrixUniform(_locObjectTransform, transform); +} + +void ShadowMapProgram::setDiffuseTextureTransform(const Matrix4& transform) +{ + loadTextureMatrixUniform(_locDiffuseTextureMatrix, transform); +} + +} + diff --git a/radiantcore/rendersystem/backend/glprogram/ShadowMapProgram.h b/radiantcore/rendersystem/backend/glprogram/ShadowMapProgram.h new file mode 100644 index 0000000000..c447341f14 --- /dev/null +++ b/radiantcore/rendersystem/backend/glprogram/ShadowMapProgram.h @@ -0,0 +1,27 @@ +#pragma once + +#include "GLSLProgramBase.h" + +namespace render +{ + +class ShadowMapProgram : + public GLSLProgramBase +{ +private: + GLint _locAlphaTest; + GLint _locObjectTransform; + GLint _locDiffuseTextureMatrix; + +public: + void create() override; + void enable() override; + void disable() override; + + void setObjectTransform(const Matrix4& transform); + + void setDiffuseTextureTransform(const Matrix4& transform); + void setAlphaTest(float alphaTest); +}; + +} diff --git a/tools/msvc/DarkRadiantCore.vcxproj b/tools/msvc/DarkRadiantCore.vcxproj index 35f4c7f746..cc61e492cc 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj +++ b/tools/msvc/DarkRadiantCore.vcxproj @@ -634,6 +634,7 @@ + @@ -1005,6 +1006,7 @@ + diff --git a/tools/msvc/DarkRadiantCore.vcxproj.filters b/tools/msvc/DarkRadiantCore.vcxproj.filters index 1c76f07a54..a58a8d919e 100644 --- a/tools/msvc/DarkRadiantCore.vcxproj.filters +++ b/tools/msvc/DarkRadiantCore.vcxproj.filters @@ -1144,6 +1144,9 @@ src\eclass + + src\rendersystem\backend\glprogram + @@ -2361,5 +2364,8 @@ src\rendersystem\backend + + src\rendersystem\backend\glprogram + \ No newline at end of file diff --git a/tools/msvc/libs.vcxproj b/tools/msvc/libs.vcxproj index 1539c459a9..d1c59e2511 100644 --- a/tools/msvc/libs.vcxproj +++ b/tools/msvc/libs.vcxproj @@ -218,6 +218,7 @@ + diff --git a/tools/msvc/libs.vcxproj.filters b/tools/msvc/libs.vcxproj.filters index 19f10fbea2..8688ff7869 100644 --- a/tools/msvc/libs.vcxproj.filters +++ b/tools/msvc/libs.vcxproj.filters @@ -377,6 +377,9 @@ render + + render +