From 57502d2f545a3be2cb80c40037e7de6faa1266ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kapu=C5=9Bci=C5=84ski?= Date: Wed, 1 Dec 2021 17:05:20 +0100 Subject: [PATCH] Added object renderer (WIP) Rewritten shadow rendering logic Split some shaders into smaller parts Shadow renderer now has its own framebuffer object --- src/CMakeLists.txt | 2 + src/graphics/core/device.h | 3 + src/graphics/core/renderers.h | 91 ++++- src/graphics/engine/engine.cpp | 361 ++++++++++-------- src/graphics/engine/engine.h | 10 + src/graphics/opengl/gl33device.cpp | 8 + src/graphics/opengl/gl33device.h | 4 + src/graphics/opengl/gl33objectrenderer.cpp | 334 ++++++++++++++++ src/graphics/opengl/gl33objectrenderer.h | 134 +++++++ src/graphics/opengl/gl33renderers.cpp | 138 +++++-- src/graphics/opengl/gl33renderers.h | 24 +- src/graphics/opengl/glframebuffer.cpp | 7 +- src/graphics/opengl/glutil.cpp | 89 +++++ src/graphics/opengl/glutil.h | 7 + .../opengl/shaders/gl33/object_fs.glsl | 138 +++++++ .../opengl/shaders/gl33/object_vs.glsl | 56 +++ .../opengl/shaders/gl33/preamble.glsl | 21 + src/graphics/opengl/shaders/gl33/shadow.glsl | 74 ++++ .../opengl/shaders/gl33/terrain_fs.glsl | 11 +- .../opengl/shaders/gl33/terrain_vs.glsl | 7 +- 20 files changed, 1305 insertions(+), 214 deletions(-) create mode 100644 src/graphics/opengl/gl33objectrenderer.cpp create mode 100644 src/graphics/opengl/gl33objectrenderer.h create mode 100644 src/graphics/opengl/shaders/gl33/object_fs.glsl create mode 100644 src/graphics/opengl/shaders/gl33/object_vs.glsl create mode 100644 src/graphics/opengl/shaders/gl33/preamble.glsl create mode 100644 src/graphics/opengl/shaders/gl33/shadow.glsl diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fd5eebd6..f3559b6e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -147,6 +147,8 @@ add_library(colobotbase STATIC graphics/opengl/gl33device.h graphics/opengl/gl33renderers.cpp graphics/opengl/gl33renderers.h + graphics/opengl/gl33objectrenderer.cpp + graphics/opengl/gl33objectrenderer.h graphics/opengl/glframebuffer.cpp graphics/opengl/glframebuffer.h graphics/opengl/glutil.cpp diff --git a/src/graphics/core/device.h b/src/graphics/core/device.h index a311aa7b..27f4f876 100644 --- a/src/graphics/core/device.h +++ b/src/graphics/core/device.h @@ -53,6 +53,7 @@ class CFramebuffer; class CUIRenderer; class CTerrainRenderer; class CShadowRenderer; +class CObjectRenderer; struct FramebufferParams; struct Light; struct Material; @@ -455,6 +456,8 @@ public: virtual CUIRenderer* GetUIRenderer() = 0; //! Returns terrain renderer virtual CTerrainRenderer* GetTerrainRenderer() = 0; + //! Returns object renderer + virtual CObjectRenderer* GetObjectRenderer() = 0; //! Returns shadow renderer virtual CShadowRenderer* GetShadowRenderer() = 0; diff --git a/src/graphics/core/renderers.h b/src/graphics/core/renderers.h index bc9200d2..40924c44 100644 --- a/src/graphics/core/renderers.h +++ b/src/graphics/core/renderers.h @@ -36,6 +36,21 @@ class CVertexBuffer; enum PrimitiveType; struct Texture; +enum class TransparencyMode +{ + NONE, + ALPHA, + BLACK, + WHITE, +}; + +struct ShadowParam +{ + glm::mat4 matrix; + glm::vec2 uv_offset; + glm::vec2 uv_scale; +}; + /** * \class CRenderer * \brief Common abstract interface for renderers @@ -46,11 +61,11 @@ public: virtual ~CRenderer() { } //! Flush buffered content - virtual void Flush() = 0; + virtual void Flush() {} }; /** - * \class CRenderer + * \class CUIRenderer * \brief Abstract interface for UI renderers */ class CUIRenderer : public CRenderer @@ -69,6 +84,10 @@ public: virtual void DrawPrimitive(PrimitiveType type, int count, const Vertex2D* vertices) = 0; }; +/** + * \class CTerrainRenderer + * \brief Abstract interface for terrain renderers + */ class CTerrainRenderer : public CRenderer { public: @@ -84,8 +103,6 @@ public: virtual void SetViewMatrix(const glm::mat4& matrix) = 0; //! Sets model matrix virtual void SetModelMatrix(const glm::mat4& matrix) = 0; - //! Sets shadow matrix - virtual void SetShadowMatrix(const glm::mat4& matrix) = 0; //! Sets primary texture, setting texture 0 means using white texture virtual void SetPrimaryTexture(const Texture& texture) = 0; @@ -96,6 +113,8 @@ public: //! Sets light parameters virtual void SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color) = 0; + //! Sets shadow parameters + virtual void SetShadowParams(int count, const ShadowParam* params) = 0; //! Sets fog parameters virtual void SetFog(float min, float max, const glm::vec3& color) = 0; @@ -104,6 +123,65 @@ public: virtual void DrawObject(const glm::mat4& matrix, const CVertexBuffer* buffer) = 0; }; +/** + * \class CObjectRenderer + * \brief Abstract interface for object renderers + */ +class CObjectRenderer : public CRenderer +{ +public: + virtual ~CObjectRenderer() { } + + virtual void Begin() = 0; + + virtual void End() = 0; + + //! Sets projection matrix + virtual void SetProjectionMatrix(const glm::mat4& matrix) = 0; + //! Sets view matrix + virtual void SetViewMatrix(const glm::mat4& matrix) = 0; + //! Sets model matrix + virtual void SetModelMatrix(const glm::mat4& matrix) = 0; + + //! Sets color + virtual void SetColor(const glm::vec4& color) = 0; + + //! Sets primary texture + virtual void SetPrimaryTexture(const Texture& texture) = 0; + //! Sets secondary texture + virtual void SetSecondaryTexture(const Texture& texture) = 0; + //! Sets shadow map + virtual void SetShadowMap(const Texture& texture) = 0; + + //! Enables lighting + virtual void SetLighting(bool enabled) = 0; + //! Sets light parameters + virtual void SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color) = 0; + //! Sets shadow parameters + virtual void SetShadowParams(int count, const ShadowParam* params) = 0; + + //! Sets fog parameters + virtual void SetFog(float min, float max, const glm::vec3& color) = 0; + //! Sets alpha scissor + virtual void SetAlphaScissor(float alpha) = 0; + + //! Sets cull mode + virtual void SetCullMode(bool enabled) = 0; + //! Sets transparency mode + virtual void SetTransparency(TransparencyMode mode) = 0; + + virtual void SetPrimaryTextureEnabled(bool enabled) = 0; + //! Sets amount of dirt (second texture) to apply + virtual void SetDirty(float amount) = 0; + + //! Draws an object + virtual void DrawObject(const CVertexBuffer* buffer) = 0; +}; + +/** + * \class CShadowRenderer + * \brief Abstract interface for shadow renderers + */ class CShadowRenderer : public CRenderer { public: @@ -123,6 +201,11 @@ public: //! Sets texture virtual void SetTexture(const Texture& texture) = 0; + //! Sets shadow map + virtual void SetShadowMap(const Texture& texture) = 0; + //! Sets shadow region + virtual void SetShadowRegion(const glm::vec2& offset, const glm::vec2& scale) = 0; + //! Draws terrain object virtual void DrawObject(const CVertexBuffer* buffer, bool transparent) = 0; }; diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index 93133d75..dd9ae2be 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -3359,9 +3359,9 @@ void CEngine::Draw3DScene() // Display the objects - m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true); - m_device->SetRenderState(RENDER_STATE_LIGHTING, true); - m_device->SetRenderState(RENDER_STATE_FOG, true); + //m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true); + //m_device->SetRenderState(RENDER_STATE_LIGHTING, true); + //m_device->SetRenderState(RENDER_STATE_FOG, true); float fogStart = m_deepView[m_rankView] * m_fogStart[m_rankView] * m_clippingDistance; float fogEnd = m_deepView[m_rankView] * m_clippingDistance; @@ -3378,20 +3378,28 @@ void CEngine::Draw3DScene() // Draw terrain - m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN); + //m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN); UseShadowMapping(true); SetState(0); + Gfx::ShadowParam shadowParams[4]; + for (int i = 0; i < m_shadowRegions; i++) + { + shadowParams[i].matrix = m_shadowParams[i].transform; + shadowParams[i].uv_offset = m_shadowParams[i].offset; + shadowParams[i].uv_scale = m_shadowParams[i].scale; + } + auto terrainRenderer = m_device->GetTerrainRenderer(); terrainRenderer->Begin(); terrainRenderer->SetProjectionMatrix(m_matProj); terrainRenderer->SetViewMatrix(m_matView); - terrainRenderer->SetShadowMatrix(m_shadowTextureMat); terrainRenderer->SetShadowMap(m_shadowMap); terrainRenderer->SetLight(glm::vec4(1.0, 1.0, -1.0, 0.0), 1.0f, glm::vec3(1.0)); + terrainRenderer->SetShadowParams(m_shadowRegions, shadowParams); Color fogColor = m_fogColor[m_rankView]; @@ -3459,6 +3467,20 @@ void CEngine::Draw3DScene() CProfiler::StartPerformanceCounter(PCNT_RENDER_OBJECTS); + auto objectRenderer = m_device->GetObjectRenderer(); + objectRenderer->Begin(); + + objectRenderer->SetProjectionMatrix(m_matProj); + objectRenderer->SetViewMatrix(m_matView); + objectRenderer->SetShadowMap(m_shadowMap); + objectRenderer->SetLighting(true); + objectRenderer->SetLight(glm::vec4(1.0, 1.0, -1.0, 0.0), 1.0f, glm::vec3(1.0)); + objectRenderer->SetTransparency(TransparencyMode::NONE); + + objectRenderer->SetFog(fogStart, fogEnd, { fogColor.r, fogColor.g, fogColor.b }); + objectRenderer->SetAlphaScissor(0.5f); + objectRenderer->SetShadowParams(m_shadowRegions, shadowParams); + bool transparent = false; for (int objRank = 0; objRank < static_cast(m_objects.size()); objRank++) @@ -3472,7 +3494,6 @@ void CEngine::Draw3DScene() if (! m_objects[objRank].drawWorld) continue; - m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform); auto combinedMatrix = Math::MultiplyMatrices(projectionViewMatrix, m_objects[objRank].transform); if (! IsVisible(combinedMatrix, objRank)) @@ -3488,14 +3509,16 @@ void CEngine::Draw3DScene() if (! p1.used) continue; + objectRenderer->SetModelMatrix(m_objects[objRank].transform); + m_lightMan->UpdateDeviceLights(m_objects[objRank].type); for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; - SetTexture(p2.tex1, 0); - SetTexture(p2.tex2, 1); + objectRenderer->SetPrimaryTexture(p2.tex1); + objectRenderer->SetSecondaryTexture(p2.tex2); for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { @@ -3507,16 +3530,32 @@ void CEngine::Draw3DScene() continue; } - SetMaterial(p3.material); - SetState(p3.state); + if ((p3.state & ENG_RSTATE_TCOLOR_BLACK) + || (p3.state & ENG_RSTATE_TCOLOR_WHITE) + || (p3.state & ENG_RSTATE_TCOLOR_ALPHA)) + objectRenderer->SetPrimaryTextureEnabled(false); + else + objectRenderer->SetPrimaryTextureEnabled(true); - DrawObject(p3); + float dirty = ((p3.state & ENG_RSTATE_DUAL_BLACK) && m_dirty) ? 1.0 : 0.0; + objectRenderer->SetDirty(dirty); + auto color = p3.material.diffuse; + objectRenderer->SetColor({ color.r, color.g, color.b, 1.0 }); + objectRenderer->SetCullMode((p3.state& ENG_RSTATE_2FACE) == 0); + objectRenderer->DrawObject(p3.buffer); } } } + objectRenderer->End(); + UseShadowMapping(false); + objectRenderer->Begin(); + objectRenderer->SetLighting(false); + objectRenderer->SetTransparency(TransparencyMode::ALPHA); + objectRenderer->SetAlphaScissor(0.0f); + // Draw transparent objects if (transparent) @@ -3535,7 +3574,6 @@ void CEngine::Draw3DScene() if (! m_objects[objRank].drawWorld) continue; - m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform); auto combinedMatrix = Math::MultiplyMatrices(projectionViewMatrix, m_objects[objRank].transform); if (! IsVisible(combinedMatrix, objRank)) @@ -3551,14 +3589,14 @@ void CEngine::Draw3DScene() if (! p1.used) continue; - m_lightMan->UpdateDeviceLights(m_objects[objRank].type); + objectRenderer->SetModelMatrix(m_objects[objRank].transform); for (int l2 = 0; l2 < static_cast( p1.next.size() ); l2++) { EngineBaseObjTexTier& p2 = p1.next[l2]; - SetTexture(p2.tex1, 0); - SetTexture(p2.tex2, 1); + objectRenderer->SetPrimaryTexture(p2.tex1); + objectRenderer->SetSecondaryTexture(p2.tex2); for (int l3 = 0; l3 < static_cast( p2.next.size() ); l3++) { @@ -3567,15 +3605,19 @@ void CEngine::Draw3DScene() if (m_objects[objRank].transparency == 0.0f) continue; - SetMaterial(p3.material); - SetState(tState, tColor); - - DrawObject(p3); + float dirty = (p3.state & ENG_RSTATE_DUAL_BLACK) && m_dirty ? 1.0 : 0.0; + objectRenderer->SetDirty(dirty); + auto color = p3.material.diffuse; + objectRenderer->SetColor({ color.r, color.g, color.b, 0.5f });// -m_objects[objRank].transparency }); + objectRenderer->SetCullMode((p3.state& ENG_RSTATE_2FACE) == 0); + objectRenderer->DrawObject(p3.buffer); } } } } + objectRenderer->End(); + CProfiler::StopPerformanceCounter(PCNT_RENDER_OBJECTS); m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN); @@ -3904,62 +3946,51 @@ void CEngine::RenderShadowMap() CProfiler::StartPerformanceCounter(PCNT_RENDER_SHADOW_MAP); + m_shadowRegions = 4; + + m_shadowParams[0].range = 32.0; + m_shadowParams[0].offset = { 0.0, 0.0 }; + m_shadowParams[0].scale = { 0.5, 0.5 }; + + m_shadowParams[1].range = 96.0; + m_shadowParams[1].offset = { 0.5, 0.0 }; + m_shadowParams[1].scale = { 0.5, 0.5 }; + + m_shadowParams[2].range = 256.0; + m_shadowParams[2].offset = { 0.0, 0.5 }; + m_shadowParams[2].scale = { 0.5, 0.5 }; + + m_shadowParams[3].range = 1024.0; + m_shadowParams[3].offset = { 0.5, 0.5 }; + m_shadowParams[3].scale = { 0.5, 0.5 }; + // If no shadow map texture exists, create it if (m_shadowMap.id == 0) { - int width = 256, height = 256; - int depth; + FramebufferParams params; + params.width = m_offscreenShadowRenderingResolution; + params.height = m_offscreenShadowRenderingResolution; + params.depth = 32; + params.colorAttachment = FramebufferParams::AttachmentType::None; + params.depthAttachment = FramebufferParams::AttachmentType::Texture; - if (m_offscreenShadowRendering) + CFramebuffer* framebuffer = m_device->CreateFramebuffer("shadow", params); + if (framebuffer == nullptr) { - width = height = m_offscreenShadowRenderingResolution; - - FramebufferParams params; - params.width = params.height = width; - params.depth = depth = 32; - params.colorAttachment = FramebufferParams::AttachmentType::None; - params.depthAttachment = FramebufferParams::AttachmentType::Texture; - - CFramebuffer *framebuffer = m_device->CreateFramebuffer("shadow", params); - if (framebuffer == nullptr) - { - GetLogger()->Error("Could not create shadow mapping framebuffer, disabling dynamic shadows\n"); - m_shadowMapping = false; - m_offscreenShadowRendering = false; - m_qualityShadows = false; - CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP); - return; - } - - m_shadowMap.id = framebuffer->GetDepthTexture(); - m_shadowMap.size = Math::IntPoint(width, height); - } - else - { - int min = Math::Min(m_size.x, m_size.y); - - for (int i = 0; i < 16; i++) - { - if (min < (1 << i)) break; - - width = height = 1 << i; - } - - depth = m_app->GetInstance().GetVideoConfig().depthSize; - - m_shadowMap = m_device->CreateDepthTexture(width, height, depth); + GetLogger()->Error("Could not create shadow mapping framebuffer, disabling dynamic shadows\n"); + m_shadowMapping = false; + m_offscreenShadowRendering = false; + m_qualityShadows = false; + CProfiler::StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP); + return; } - GetLogger()->Info("Created shadow map texture: %dx%d, depth %d\n", width, height, depth); - } + m_shadowMap.id = framebuffer->GetDepthTexture(); + m_shadowMap.size = Math::IntPoint(params.width, params.height); - if (m_offscreenShadowRendering) - { - m_device->GetFramebuffer("shadow")->Bind(); + GetLogger()->Info("Created shadow map texture: %dx%d, depth %d\n", params.width, params.height, params.depth); } - m_device->Clear(); - // change state to rendering shadow maps m_device->SetColorMask(false, false, false, false); m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true); @@ -3974,111 +4005,117 @@ void CEngine::RenderShadowMap() m_device->SetDepthBias(2.0f, 8.0f); m_device->SetViewport(0, 0, m_shadowMap.size.x, m_shadowMap.size.y); - // recompute matrices - Math::Vector worldUp(0.0f, 1.0f, 0.0f); - Math::Vector lightDir = Math::Vector(1.0f, 2.0f, -1.0f); - Math::Vector dir = m_lookatPt - m_eyePt; - dir.y = 0.0f; - dir.Normalize(); - - float dist = m_shadowRange; - float depth = 200.0f; - - if (dist < 0.5f) - { - float scale = log(m_shadowMap.size.x) / log(2.0f) - 6.5f; - dist = 75.0f * scale; - } - - Math::Vector pos = m_lookatPt + 0.25f * dist * dir; - - { - // To prevent 'shadow shimmering', we ensure that the position only moves in texel-sized - // increments. To do this we transform the position to a space where the light's forward/right/up - // axes are aligned with the x/y/z axes (not necessarily in that order, and +/- signs don't matter). - Math::Matrix lightRotation; - Math::LoadViewMatrix(lightRotation, Math::Vector{}, lightDir, worldUp); - pos = Math::MatrixVectorMultiply(lightRotation, pos); - // ...then we round to the nearest worldUnitsPerTexel: - const float worldUnitsPerTexel = (dist * 2.0f) / m_shadowMap.size.x; - pos /= worldUnitsPerTexel; - pos.x = round(pos.x); - pos.y = round(pos.y); - pos.z = round(pos.z); - pos *= worldUnitsPerTexel; - // ...and convert back to world space. - pos = Math::MatrixVectorMultiply(lightRotation.Inverse(), pos); - } - - Math::Vector lookAt = pos - lightDir; - - Math::LoadOrthoProjectionMatrix(m_shadowProjMat, -dist, dist, -dist, dist, -depth, depth); - Math::LoadViewMatrix(m_shadowViewMat, pos, lookAt, worldUp); - - Math::Matrix scaleMat; - Math::LoadScaleMatrix(scaleMat, Math::Vector(1.0f, 1.0f, -1.0f)); - m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat); - - Math::Matrix temporary = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat); - m_shadowTextureMat = Math::MultiplyMatrices(m_shadowBias, temporary); - - m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat); - - m_device->SetTransform(TRANSFORM_PROJECTION, m_shadowProjMat); - m_device->SetTransform(TRANSFORM_VIEW, m_shadowViewMat); - - m_device->SetTexture(0, 0); - m_device->SetTexture(1, 0); - m_device->SetTexture(2, 0); - - m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, true); - m_device->SetRenderState(RENDER_STATE_CULLING, false); - - auto projectionViewMatrix = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat); - auto renderer = m_device->GetShadowRenderer(); renderer->Begin(); - renderer->SetProjectionMatrix(m_shadowProjMat); - renderer->SetViewMatrix(m_shadowViewMat); + renderer->SetShadowMap(m_shadowMap); + renderer->SetShadowRegion({ 0.0, 0.0 }, { 1.0, 1.0 }); - // render objects into shadow map - for (int objRank = 0; objRank < static_cast(m_objects.size()); objRank++) + m_device->Clear(); + + for (int region = 0; region < m_shadowRegions; region++) { - if (!m_objects[objRank].used) - continue; + renderer->SetShadowRegion( + m_shadowParams[region].offset, + m_shadowParams[region].scale); - bool terrain = (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN); + // recompute matrices + Math::Vector worldUp(0.0f, 1.0f, 0.0f); + Math::Vector lightDir = Math::Vector(1.0f, 2.0f, -1.0f); + Math::Vector dir = m_lookatPt - m_eyePt; + dir.y = 0.0f; + dir.Normalize(); - if (terrain && !m_terrainShadows) continue; + float range = m_shadowParams[region].range; - auto combinedMatrix = Math::MultiplyMatrices(projectionViewMatrix, m_objects[objRank].transform); + float dist = range; + float depth = 200.0f; - if (!IsVisible(combinedMatrix, objRank)) - continue; - - int baseObjRank = m_objects[objRank].baseObjRank; - if (baseObjRank == -1) - continue; - - assert(baseObjRank >= 0 && baseObjRank < static_cast(m_baseObjects.size())); - - EngineBaseObject& p1 = m_baseObjects[baseObjRank]; - if (!p1.used) - continue; - - renderer->SetModelMatrix(m_objects[objRank].transform); - - for (int l2 = 0; l2 < static_cast(p1.next.size()); l2++) + if (dist < 0.5f) { - EngineBaseObjTexTier& p2 = p1.next[l2]; + float scale = log(m_shadowMap.size.x) / log(2.0f) - 6.5f; + dist = 75.0f * scale; + } - renderer->SetTexture(p2.tex1); + Math::Vector pos = m_lookatPt + 0.25f * dist * dir; - for (int l3 = 0; l3 < static_cast(p2.next.size()); l3++) + { + // To prevent 'shadow shimmering', we ensure that the position only moves in texel-sized + // increments. To do this we transform the position to a space where the light's forward/right/up + // axes are aligned with the x/y/z axes (not necessarily in that order, and +/- signs don't matter). + Math::Matrix lightRotation; + Math::LoadViewMatrix(lightRotation, Math::Vector{}, lightDir, worldUp); + pos = Math::MatrixVectorMultiply(lightRotation, pos); + // ...then we round to the nearest worldUnitsPerTexel: + const float worldUnitsPerTexel = (dist * 2.0f) / m_shadowMap.size.x; + pos /= worldUnitsPerTexel; + pos.x = round(pos.x); + pos.y = round(pos.y); + pos.z = round(pos.z); + pos *= worldUnitsPerTexel; + // ...and convert back to world space. + pos = Math::MatrixVectorMultiply(lightRotation.Inverse(), pos); + } + + Math::Vector lookAt = pos - lightDir; + + Math::LoadOrthoProjectionMatrix(m_shadowProjMat, -dist, dist, -dist, dist, -depth, depth); + Math::LoadViewMatrix(m_shadowViewMat, pos, lookAt, worldUp); + + Math::Matrix scaleMat; + Math::LoadScaleMatrix(scaleMat, Math::Vector(1.0f, 1.0f, -1.0f)); + m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat); + + Math::Matrix temporary = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat); + m_shadowTextureMat = Math::MultiplyMatrices(m_shadowBias, temporary); + + m_shadowViewMat = Math::MultiplyMatrices(scaleMat, m_shadowViewMat); + + auto projectionViewMatrix = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat); + + m_shadowParams[region].transform = m_shadowTextureMat; + + renderer->SetProjectionMatrix(m_shadowProjMat); + renderer->SetViewMatrix(m_shadowViewMat); + + // render objects into shadow map + for (int objRank = 0; objRank < static_cast(m_objects.size()); objRank++) + { + if (!m_objects[objRank].used) + continue; + + bool terrain = (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN); + + if (terrain && !m_terrainShadows) continue; + + auto combinedMatrix = Math::MultiplyMatrices(projectionViewMatrix, m_objects[objRank].transform); + + if (!IsVisible(combinedMatrix, objRank)) + continue; + + int baseObjRank = m_objects[objRank].baseObjRank; + if (baseObjRank == -1) + continue; + + assert(baseObjRank >= 0 && baseObjRank < static_cast(m_baseObjects.size())); + + EngineBaseObject& p1 = m_baseObjects[baseObjRank]; + if (!p1.used) + continue; + + renderer->SetModelMatrix(m_objects[objRank].transform); + + for (int l2 = 0; l2 < static_cast(p1.next.size()); l2++) { - EngineBaseObjDataTier& p3 = p2.next[l3]; + EngineBaseObjTexTier& p2 = p1.next[l2]; - renderer->DrawObject(p3.buffer, true); + renderer->SetTexture(p2.tex1); + + for (int l3 = 0; l3 < static_cast(p2.next.size()); l3++) + { + EngineBaseObjDataTier& p3 = p2.next[l3]; + + renderer->DrawObject(p3.buffer, true); + } } } } @@ -4091,14 +4128,8 @@ void CEngine::RenderShadowMap() m_device->SetRenderState(RENDER_STATE_CULLING, false); m_device->SetCullMode(CULL_CW); - if (m_offscreenShadowRendering) // shadow map texture already have depth information, just unbind it - { - m_device->GetFramebuffer("shadow")->Unbind(); - } - else // copy depth buffer to shadow map - { - m_device->CopyFramebufferToTexture(m_shadowMap, 0, 0, 0, 0, m_shadowMap.size.x, m_shadowMap.size.y); - } + m_device->GetFramebuffer("default")->Bind(); + //m_device->GetFramebuffer("shadow")->Unbind(); // restore default state m_device->SetViewport(0, 0, m_size.x, m_size.y); diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h index 9adde4e3..9017601e 100644 --- a/src/graphics/engine/engine.h +++ b/src/graphics/engine/engine.h @@ -1423,6 +1423,16 @@ protected: bool m_editIndentMode; int m_editIndentValue; + struct ShadowParam + { + glm::mat4 transform; + glm::vec2 offset; + glm::vec2 scale; + float range; + }; + + int m_shadowRegions = 4; + ShadowParam m_shadowParams[4]; Texture m_shadowMap; struct PendingDebugDraw diff --git a/src/graphics/opengl/gl33device.cpp b/src/graphics/opengl/gl33device.cpp index fccc4d91..9ad30366 100644 --- a/src/graphics/opengl/gl33device.cpp +++ b/src/graphics/opengl/gl33device.cpp @@ -32,6 +32,7 @@ #include "graphics/opengl/glframebuffer.h" #include "graphics/opengl/gl33renderers.h" +#include "graphics/opengl/gl33objectrenderer.h" #include "math/geometry.h" @@ -434,6 +435,7 @@ bool CGL33Device::Create() m_uiRenderer = std::make_unique(this); m_terrainRenderer = std::make_unique(this); + m_objectRenderer = std::make_unique(this); m_shadowRenderer = std::make_unique(this); glUseProgram(m_normalProgram); @@ -556,6 +558,11 @@ CTerrainRenderer* CGL33Device::GetTerrainRenderer() return m_terrainRenderer.get(); } +CObjectRenderer* CGL33Device::GetObjectRenderer() +{ + return m_objectRenderer.get(); +} + CShadowRenderer* CGL33Device::GetShadowRenderer() { return m_shadowRenderer.get(); @@ -566,6 +573,7 @@ void CGL33Device::Restore() m_uiRenderer->Flush(); glUseProgram(m_normalProgram); + //UpdateTextureState(0); //UpdateTextureState(1); //UpdateTextureState(2); diff --git a/src/graphics/opengl/gl33device.h b/src/graphics/opengl/gl33device.h index fa157849..6d9951a5 100644 --- a/src/graphics/opengl/gl33device.h +++ b/src/graphics/opengl/gl33device.h @@ -82,6 +82,7 @@ public: class CGL33UIRenderer; class CGL33TerrainRenderer; +class CGL33ObjectRenderer; class CGL33ShadowRenderer; /** @@ -118,6 +119,7 @@ public: CUIRenderer* GetUIRenderer() override; CTerrainRenderer* GetTerrainRenderer() override; + CObjectRenderer* GetObjectRenderer() override; CShadowRenderer* GetShadowRenderer() override; void Restore() override; @@ -309,6 +311,8 @@ private: std::unique_ptr m_uiRenderer; //! Terrain renderer std::unique_ptr m_terrainRenderer; + //! Object renderer + std::unique_ptr m_objectRenderer; //! Shadow renderer std::unique_ptr m_shadowRenderer; }; diff --git a/src/graphics/opengl/gl33objectrenderer.cpp b/src/graphics/opengl/gl33objectrenderer.cpp new file mode 100644 index 00000000..d909933e --- /dev/null +++ b/src/graphics/opengl/gl33objectrenderer.cpp @@ -0,0 +1,334 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +#include "graphics/opengl/gl33objectrenderer.h" + +#include "graphics/opengl/gl33device.h" +#include "graphics/opengl/glutil.h" + +#include "graphics/core/vertex.h" + +#include "common/logger.h" + +#include + +#include + +using namespace Gfx; + +CGL33ObjectRenderer::CGL33ObjectRenderer(CGL33Device* device) + :m_device(device) +{ + GetLogger()->Info("Creating CGL33ObjectRenderer\n"); + + + std::string preamble = LoadSource("shaders/gl33/preamble.glsl"); + std::string shadowSource = LoadSource("shaders/gl33/shadow.glsl"); + std::string vsSource = LoadSource("shaders/gl33/object_vs.glsl"); + std::string fsSource = LoadSource("shaders/gl33/object_fs.glsl"); + + GLint vsShader = CreateShader(GL_VERTEX_SHADER, { preamble, shadowSource, vsSource }); + if (vsShader == 0) + { + GetLogger()->Error("Cound not create vertex shader from file 'object_vs.glsl'\n"); + return; + } + + GLint fsShader = CreateShader(GL_FRAGMENT_SHADER, { preamble, shadowSource, fsSource }); + if (fsShader == 0) + { + GetLogger()->Error("Cound not create fragment shader from file 'object_fs.glsl'\n"); + return; + } + + m_program = LinkProgram({ vsShader, fsShader }); + + glDeleteShader(vsShader); + glDeleteShader(fsShader); + + glUseProgram(m_program); + + // Setup uniforms + auto identity = glm::identity(); + + m_projectionMatrix = glGetUniformLocation(m_program, "uni_ProjectionMatrix"); + m_viewMatrix = glGetUniformLocation(m_program, "uni_ViewMatrix"); + m_cameraMatrix = glGetUniformLocation(m_program, "uni_CameraMatrix"); + m_shadowMatrix = glGetUniformLocation(m_program, "uni_ShadowMatrix"); + m_modelMatrix = glGetUniformLocation(m_program, "uni_ModelMatrix"); + m_normalMatrix = glGetUniformLocation(m_program, "uni_NormalMatrix"); + m_lighting = glGetUniformLocation(m_program, "uni_Lighting"); + m_lightPosition = glGetUniformLocation(m_program, "uni_LightPosition"); + m_lightIntensity = glGetUniformLocation(m_program, "uni_LightIntensity"); + m_lightColor = glGetUniformLocation(m_program, "uni_LightColor"); + m_fogRange = glGetUniformLocation(m_program, "uni_FogRange"); + m_fogColor = glGetUniformLocation(m_program, "uni_FogColor"); + m_color = glGetUniformLocation(m_program, "uni_Color"); + m_primaryEnabled = glGetUniformLocation(m_program, "uni_PrimaryEnabled"); + m_dirty = glGetUniformLocation(m_program, "uni_Dirty"); + m_alphaScissor = glGetUniformLocation(m_program, "uni_AlphaScissor"); + + m_shadowRegions = glGetUniformLocation(m_program, "uni_ShadowRegions"); + + GLchar name[64]; + + for (int i = 0; i < 4; i++) + { + sprintf(name, "uni_ShadowParam[%d].transform", i); + m_shadows[i].transform = glGetUniformLocation(m_program, name); + + sprintf(name, "uni_ShadowParam[%d].uv_offset", i); + m_shadows[i].offset = glGetUniformLocation(m_program, name); + + sprintf(name, "uni_ShadowParam[%d].uv_scale", i); + m_shadows[i].scale = glGetUniformLocation(m_program, name); + } + + // Set texture units + auto texture = glGetUniformLocation(m_program, "uni_PrimaryTexture"); + glUniform1i(texture, 10); + + texture = glGetUniformLocation(m_program, "uni_SecondaryTexture"); + glUniform1i(texture, 11); + + texture = glGetUniformLocation(m_program, "uni_ShadowMap"); + glUniform1i(texture, 12); + + // White texture + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &m_whiteTexture); + glBindTexture(GL_TEXTURE_2D, m_whiteTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE); + + glUseProgram(0); + + GetLogger()->Info("CGL33ObjectRenderer created successfully\n"); +} + +CGL33ObjectRenderer::~CGL33ObjectRenderer() +{ + glDeleteProgram(m_program); + glDeleteTextures(1, &m_whiteTexture); +} + +void CGL33ObjectRenderer::CGL33ObjectRenderer::Begin() +{ + glUseProgram(m_program); + //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + glActiveTexture(GL_TEXTURE10); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE11); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE12); + glBindTexture(GL_TEXTURE_2D, 0); + + m_primaryTexture = 0; + m_secondaryTexture = 0; + m_shadowMap = 0; + + glDepthMask(GL_TRUE); +} + +void CGL33ObjectRenderer::CGL33ObjectRenderer::End() +{ + m_device->Restore(); + + glDepthMask(GL_TRUE); +} + +void CGL33ObjectRenderer::SetProjectionMatrix(const glm::mat4& matrix) +{ + glUniformMatrix4fv(m_projectionMatrix, 1, GL_FALSE, value_ptr(matrix)); +} + +void CGL33ObjectRenderer::SetViewMatrix(const glm::mat4& matrix) +{ + glm::mat4 scale(1.0f); + scale[2][2] = -1.0f; + + auto viewMatrix = scale * matrix; + auto cameraMatrix = glm::inverse(viewMatrix); + + glUniformMatrix4fv(m_viewMatrix, 1, GL_FALSE, value_ptr(viewMatrix)); + glUniformMatrix4fv(m_cameraMatrix, 1, GL_FALSE, value_ptr(cameraMatrix)); +} + +void CGL33ObjectRenderer::SetModelMatrix(const glm::mat4& matrix) +{ + auto normalMatrix = glm::transpose(glm::inverse(glm::mat3(matrix))); + + glUniformMatrix4fv(m_modelMatrix, 1, GL_FALSE, value_ptr(matrix)); + glUniformMatrix3fv(m_normalMatrix, 1, GL_FALSE, value_ptr(normalMatrix)); +} + +void CGL33ObjectRenderer::SetColor(const glm::vec4& color) +{ + glUniform4fv(m_color, 1, glm::value_ptr(color)); +} + +void CGL33ObjectRenderer::SetPrimaryTexture(const Texture& texture) +{ + if (m_primaryTexture == texture.id) return; + + m_primaryTexture = texture.id; + + glActiveTexture(GL_TEXTURE10); + + if (texture.id == 0) + glBindTexture(GL_TEXTURE_2D, m_whiteTexture); + else + glBindTexture(GL_TEXTURE_2D, texture.id); +} + +void CGL33ObjectRenderer::SetSecondaryTexture(const Texture& texture) +{ + if (m_secondaryTexture == texture.id) return; + + m_secondaryTexture = texture.id; + + glActiveTexture(GL_TEXTURE11); + + if (texture.id == 0) + glBindTexture(GL_TEXTURE_2D, m_whiteTexture); + else + glBindTexture(GL_TEXTURE_2D, texture.id); +} + +void CGL33ObjectRenderer::SetShadowMap(const Texture& texture) +{ + if (m_shadowMap == texture.id) return; + + m_shadowMap = texture.id; + + glActiveTexture(GL_TEXTURE12); + + if (texture.id == 0) + glBindTexture(GL_TEXTURE_2D, m_whiteTexture); + else + glBindTexture(GL_TEXTURE_2D, texture.id); +} + +void CGL33ObjectRenderer::SetLighting(bool enabled) +{ + glUniform1i(m_lighting, enabled ? 1 : 0); +} + +void CGL33ObjectRenderer::SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color) +{ + glUniform4fv(m_lightPosition, 1, glm::value_ptr(position)); + glUniform1f(m_lightIntensity, intensity); + glUniform3fv(m_lightColor, 1, glm::value_ptr(color)); +} + +void CGL33ObjectRenderer::SetShadowParams(int count, const ShadowParam* params) +{ + glUniform1i(m_shadowRegions, count); + + for (int i = 0; i < count; i++) + { + glUniformMatrix4fv(m_shadows[i].transform, 1, GL_FALSE, glm::value_ptr(params[i].matrix)); + glUniform2fv(m_shadows[i].offset, 1, glm::value_ptr(params[i].uv_offset)); + glUniform2fv(m_shadows[i].scale, 1, glm::value_ptr(params[i].uv_scale)); + } +} + +void CGL33ObjectRenderer::SetFog(float min, float max, const glm::vec3& color) +{ + glUniform2f(m_fogRange, min, max); + glUniform3f(m_fogColor, color.r, color.g, color.b); +} + +void CGL33ObjectRenderer::SetCullMode(bool enabled) +{ + if (enabled) + { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } + else + { + glDisable(GL_CULL_FACE); + } +} + +void CGL33ObjectRenderer::SetTransparency(TransparencyMode mode) +{ + switch (mode) + { + case TransparencyMode::NONE: + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + break; + case TransparencyMode::ALPHA: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + glDepthMask(GL_TRUE); + break; + case TransparencyMode::BLACK: + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); + glBlendEquation(GL_FUNC_ADD); + glDepthMask(GL_TRUE); + break; + case TransparencyMode::WHITE: + glEnable(GL_BLEND); + glBlendFunc(GL_DST_COLOR, GL_ZERO); + glBlendEquation(GL_FUNC_ADD); + glDepthMask(GL_TRUE); + break; + } +} + +void CGL33ObjectRenderer::SetPrimaryTextureEnabled(bool enabled) +{ + glUniform1f(m_primaryEnabled, enabled ? 1.0f : 0.0f); +} + +void CGL33ObjectRenderer::SetDirty(float amount) +{ + glUniform1f(m_dirty, amount); +} + +void CGL33ObjectRenderer::SetAlphaScissor(float alpha) +{ + glUniform1f(m_alphaScissor, alpha); +} + +void CGL33ObjectRenderer::DrawObject(const CVertexBuffer* buffer) +{ + auto b = dynamic_cast(buffer); + + if (b == nullptr) return; + + glBindVertexArray(b->GetVAO()); + + glDrawArrays(TranslateGfxPrimitive(b->GetType()), 0, b->Size()); +} diff --git a/src/graphics/opengl/gl33objectrenderer.h b/src/graphics/opengl/gl33objectrenderer.h new file mode 100644 index 00000000..b983adb0 --- /dev/null +++ b/src/graphics/opengl/gl33objectrenderer.h @@ -0,0 +1,134 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + + /** + * \file graphics/opengl/gl33objectrenderer.h + * \brief OpenGL 3.3 object renderer + */ + +#pragma once + +#include "graphics/core/renderers.h" + +#include + +#include +#include + +// Graphics module namespace +namespace Gfx +{ + +class CGL33Device; + +class CGL33ObjectRenderer : public CObjectRenderer +{ +public: + CGL33ObjectRenderer(CGL33Device* device); + virtual ~CGL33ObjectRenderer(); + + virtual void Begin() override; + + virtual void End() override; + + //! Sets projection matrix + virtual void SetProjectionMatrix(const glm::mat4& matrix) override; + //! Sets view matrix + virtual void SetViewMatrix(const glm::mat4& matrix) override; + //! Sets model matrix + virtual void SetModelMatrix(const glm::mat4& matrix) override; + + //! Sets color + virtual void SetColor(const glm::vec4& color) override; + + //! Sets primary texture + virtual void SetPrimaryTexture(const Texture& texture) override; + //! Sets secondary texture + virtual void SetSecondaryTexture(const Texture& texture) override; + //! Sets shadow map + virtual void SetShadowMap(const Texture& texture) override; + + //! Enables lighting + virtual void SetLighting(bool enabled) override; + //! Sets light parameters + virtual void SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color) override; + //! Sets shadow parameters + virtual void SetShadowParams(int count, const ShadowParam* params) override; + + //! Sets fog parameters + virtual void SetFog(float min, float max, const glm::vec3& color) override; + //! Sets alpha scissor + virtual void SetAlphaScissor(float alpha) override; + + //! Sets cull mode parameters + virtual void SetCullMode(bool enabled) override; + //! Sets transparency mode + virtual void SetTransparency(TransparencyMode mode) override; + virtual void SetPrimaryTextureEnabled(bool enabled) override; + //! Sets amount of dirt (second texture) to apply + virtual void SetDirty(float amount) override; + + //! Draws an object + virtual void DrawObject(const CVertexBuffer* buffer) override; + +private: + CGL33Device* const m_device; + + // Uniform data + GLint m_projectionMatrix = -1; + GLint m_viewMatrix = -1; + GLint m_cameraMatrix = -1; + GLint m_shadowMatrix = -1; + GLint m_modelMatrix = -1; + GLint m_normalMatrix = -1; + GLint m_lighting = -1; + GLint m_lightPosition = -1; + GLint m_lightIntensity = -1; + GLint m_lightColor = -1; + GLint m_fogRange = -1; + GLint m_fogColor = -1; + GLint m_color = -1; + GLint m_primaryEnabled = -1; + GLint m_dirty = -1; + GLint m_alphaScissor = -1; + + struct ShadowUniforms + { + GLint transform; + GLint offset; + GLint scale; + }; + + GLint m_shadowRegions; + ShadowUniforms m_shadows[4]; + + // Shader program + GLuint m_program = 0; + + // 1x1 white texture + GLuint m_whiteTexture = 0; + // Currently bound primary texture + GLuint m_primaryTexture = 0; + // Currently bound secondary texture + GLuint m_secondaryTexture = 0; + // Currently bound shadow map + GLuint m_shadowMap = 0; +}; + +} diff --git a/src/graphics/opengl/gl33renderers.cpp b/src/graphics/opengl/gl33renderers.cpp index 9dccb1b4..1edf62ea 100644 --- a/src/graphics/opengl/gl33renderers.cpp +++ b/src/graphics/opengl/gl33renderers.cpp @@ -229,31 +229,34 @@ CGL33TerrainRenderer::CGL33TerrainRenderer(CGL33Device* device) { GetLogger()->Info("Creating CGL33TerrainRenderer\n"); - GLint shaders[2] = {}; + std::string preamble = LoadSource("shaders/gl33/preamble.glsl"); + std::string shadowSource = LoadSource("shaders/gl33/shadow.glsl"); + std::string vsSource = LoadSource("shaders/gl33/terrain_vs.glsl"); + std::string fsSource = LoadSource("shaders/gl33/terrain_fs.glsl"); - shaders[0] = LoadShader(GL_VERTEX_SHADER, "shaders/gl33/terrain_vs.glsl"); - if (shaders[0] == 0) + GLint vsShader = CreateShader(GL_VERTEX_SHADER, { preamble, shadowSource, vsSource }); + if (vsShader == 0) { GetLogger()->Error("Cound not create vertex shader from file 'terrain_vs.glsl'\n"); return; } - shaders[1] = LoadShader(GL_FRAGMENT_SHADER, "shaders/gl33/terrain_fs.glsl"); - if (shaders[1] == 0) + GLint fsShader = CreateShader(GL_FRAGMENT_SHADER, { preamble, shadowSource, fsSource }); + if (fsShader == 0) { - GetLogger()->Error("Cound not create fragment shader from file 'terrain_fs.glsl'\n"); + GetLogger()->Error("Cound not create fragment shader from file 'terrain_vs.glsl'\n"); return; } - m_program = LinkProgram(2, shaders); + m_program = LinkProgram({ vsShader, fsShader }); if (m_program == 0) { GetLogger()->Error("Cound not link shader program for terrain renderer\n"); return; } - glDeleteShader(shaders[0]); - glDeleteShader(shaders[1]); + glDeleteShader(vsShader); + glDeleteShader(fsShader); glUseProgram(m_program); @@ -272,6 +275,22 @@ CGL33TerrainRenderer::CGL33TerrainRenderer(CGL33Device* device) m_fogRange = glGetUniformLocation(m_program, "uni_FogRange"); m_fogColor = glGetUniformLocation(m_program, "uni_FogColor"); + m_shadowRegions = glGetUniformLocation(m_program, "uni_ShadowRegions"); + + GLchar name[64]; + + for (int i = 0; i < 4; i++) + { + sprintf(name, "uni_ShadowParam[%d].transform", i); + m_shadows[i].transform = glGetUniformLocation(m_program, name); + + sprintf(name, "uni_ShadowParam[%d].uv_offset", i); + m_shadows[i].offset = glGetUniformLocation(m_program, name); + + sprintf(name, "uni_ShadowParam[%d].uv_scale", i); + m_shadows[i].scale = glGetUniformLocation(m_program, name); + } + // Set texture units to 10th and 11th auto texture = glGetUniformLocation(m_program, "uni_PrimaryTexture"); glUniform1i(texture, 10); @@ -310,10 +329,44 @@ CGL33TerrainRenderer::~CGL33TerrainRenderer() void CGL33TerrainRenderer::Begin() { glUseProgram(m_program); + + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + glDisable(GL_BLEND); + + glActiveTexture(GL_TEXTURE10); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE11); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE12); + glBindTexture(GL_TEXTURE_2D, 0); + + m_primaryTexture = 0; + m_secondaryTexture = 0; + m_shadowMap = 0; } void CGL33TerrainRenderer::End() { + glActiveTexture(GL_TEXTURE10); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE11); + glBindTexture(GL_TEXTURE_2D, 0); + + glActiveTexture(GL_TEXTURE12); + glBindTexture(GL_TEXTURE_2D, 0); + + m_primaryTexture = 0; + m_secondaryTexture = 0; + m_shadowMap = 0; + m_device->Restore(); } @@ -342,11 +395,6 @@ void CGL33TerrainRenderer::SetModelMatrix(const glm::mat4& matrix) glUniformMatrix3fv(m_normalMatrix, 1, GL_FALSE, value_ptr(normalMatrix)); } -void CGL33TerrainRenderer::SetShadowMatrix(const glm::mat4& matrix) -{ - glUniformMatrix4fv(m_shadowMatrix, 1, GL_FALSE, value_ptr(matrix)); -} - void CGL33TerrainRenderer::SetPrimaryTexture(const Texture& texture) { if (m_primaryTexture == texture.id) return; @@ -396,6 +444,18 @@ void CGL33TerrainRenderer::SetLight(const glm::vec4& position, const float& inte glUniform3fv(m_lightColor, 1, glm::value_ptr(color)); } +void CGL33TerrainRenderer::SetShadowParams(int count, const ShadowParam* params) +{ + glUniform1i(m_shadowRegions, count); + + for (int i = 0; i < count; i++) + { + glUniformMatrix4fv(m_shadows[i].transform, 1, GL_FALSE, glm::value_ptr(params[i].matrix)); + glUniform2fv(m_shadows[i].offset, 1, glm::value_ptr(params[i].uv_offset)); + glUniform2fv(m_shadows[i].scale, 1, glm::value_ptr(params[i].uv_scale)); + } +} + void CGL33TerrainRenderer::SetFog(float min, float max, const glm::vec3& color) { glUniform2f(m_fogRange, min, max); @@ -406,11 +466,7 @@ void CGL33TerrainRenderer::DrawObject(const glm::mat4& matrix, const CVertexBuff { auto b = dynamic_cast(buffer); - if (b == nullptr) - { - GetLogger()->Error("No vertex buffer"); - return; - } + if (b == nullptr) return; SetModelMatrix(matrix); glBindVertexArray(b->GetVAO()); @@ -469,21 +525,34 @@ CGL33ShadowRenderer::CGL33ShadowRenderer(CGL33Device* device) glUseProgram(0); + glGenFramebuffers(1, &m_framebuffer); + GetLogger()->Info("CGL33ShadowRenderer created successfully\n"); } CGL33ShadowRenderer::~CGL33ShadowRenderer() { glDeleteProgram(m_program); + + glDeleteFramebuffers(1, &m_framebuffer); } void CGL33ShadowRenderer::Begin() { + glViewport(0, 0, m_width, m_height); + glDepthMask(GL_TRUE); + glClear(GL_DEPTH_BUFFER_BIT); + glUseProgram(m_program); + + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); } void CGL33ShadowRenderer::End() { + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + m_device->Restore(); } @@ -513,15 +582,36 @@ void CGL33ShadowRenderer::SetTexture(const Texture& texture) glBindTexture(GL_TEXTURE_2D, texture.id); } +void CGL33ShadowRenderer::SetShadowMap(const Texture& texture) +{ + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture.id, 0); + + m_width = texture.size.x; + m_height = texture.size.y; + + auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + GetLogger()->Error("Framebuffer incomplete: %d\n", status); + } +} + +void CGL33ShadowRenderer::SetShadowRegion(const glm::vec2& offset, const glm::vec2& scale) +{ + int x = static_cast(m_width * offset.x); + int y = static_cast(m_height * offset.y); + int width = static_cast(m_width * scale.x); + int height = static_cast(m_height * scale.y); + + glViewport(x, y, width, height); +} + void CGL33ShadowRenderer::DrawObject(const CVertexBuffer* buffer, bool transparent) { auto b = dynamic_cast(buffer); - if (b == nullptr) - { - GetLogger()->Error("No vertex buffer"); - return; - } + if (b == nullptr) return; glUniform1i(m_alphaScissor, transparent ? 1 : 0); diff --git a/src/graphics/opengl/gl33renderers.h b/src/graphics/opengl/gl33renderers.h index dc251374..42239d2b 100644 --- a/src/graphics/opengl/gl33renderers.h +++ b/src/graphics/opengl/gl33renderers.h @@ -108,8 +108,6 @@ public: virtual void SetViewMatrix(const glm::mat4& matrix) override; //! Sets model matrix virtual void SetModelMatrix(const glm::mat4& matrix) override; - //! Sets shadow matrix - virtual void SetShadowMatrix(const glm::mat4& matrix) override; //! Sets primary texture, setting texture 0 means using white texture virtual void SetPrimaryTexture(const Texture& texture) override; @@ -121,6 +119,8 @@ public: //! Sets light parameters virtual void SetLight(const glm::vec4& position, const float& intensity, const glm::vec3& color) override; + virtual void SetShadowParams(int count, const ShadowParam* params) override; + //! Sets fog parameters virtual void SetFog(float min, float max, const glm::vec3& color) override; @@ -145,6 +145,16 @@ private: GLint m_fogRange; GLint m_fogColor; + struct ShadowUniforms + { + GLint transform; + GLint offset; + GLint scale; + }; + + GLint m_shadowRegions; + ShadowUniforms m_shadows[4]; + // Shader program GLuint m_program = 0; @@ -178,6 +188,11 @@ public: //! Sets texture virtual void SetTexture(const Texture& texture) override; + //! Sets shadow map + virtual void SetShadowMap(const Texture& texture) override; + //! Sets shadow region + virtual void SetShadowRegion(const glm::vec2& offset, const glm::vec2& scale) override; + //! Draws terrain object virtual void DrawObject(const CVertexBuffer* buffer, bool transparent) override; @@ -194,6 +209,11 @@ private: // Shader program GLuint m_program = 0; + + // Framebuffer + GLuint m_framebuffer = 0; + int m_width = 0; + int m_height = 0; }; } // namespace Gfx diff --git a/src/graphics/opengl/glframebuffer.cpp b/src/graphics/opengl/glframebuffer.cpp index 41ce771a..ed501e52 100644 --- a/src/graphics/opengl/glframebuffer.cpp +++ b/src/graphics/opengl/glframebuffer.cpp @@ -122,13 +122,10 @@ bool CGLFramebuffer::Create() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); - float color[] = { 1.0f, 1.0f, 1.0f, 1.0f }; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, previous); diff --git a/src/graphics/opengl/glutil.cpp b/src/graphics/opengl/glutil.cpp index 3c812ed9..3766255f 100644 --- a/src/graphics/opengl/glutil.cpp +++ b/src/graphics/opengl/glutil.cpp @@ -461,6 +461,61 @@ std::string GetLastShaderError() return lastShaderError; } +std::string LoadSource(const std::string& path) +{ + PHYSFS_file* file = PHYSFS_openRead(path.c_str()); + if (file == nullptr) + { + CLogger::GetInstance().Error("Cannot read shader source file\n"); + CLogger::GetInstance().Error("Missing file \"%s\"\n", path); + return 0; + } + + size_t len = PHYSFS_fileLength(file); + + std::string source(len, ' '); + + size_t length = PHYSFS_read(file, source.data(), 1, len); + + PHYSFS_close(file); + + return source; +} + +GLint CreateShader(GLint type, const std::vector& sources) +{ + std::vector parts; + + for (const auto& source : sources) + { + parts.push_back(source.c_str()); + } + + GLuint shader = glCreateShader(type); + glShaderSource(shader, parts.size(), parts.data(), nullptr); + glCompileShader(shader); + + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + + if (status != GL_TRUE) + { + GLint len; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); + + auto message = MakeUniqueArray(len + 1); + glGetShaderInfoLog(shader, len + 1, nullptr, message.get()); + + GetLogger()->Error("Shader compilation error occurred!\n%s\n", message.get()); + lastShaderError = std::string("Shader compilation error occurred!\n\n") + std::string(message.get()); + + glDeleteShader(shader); + return 0; + } + + return shader; +} + GLint LoadShader(GLint type, const char* filename) { PHYSFS_file *file = PHYSFS_openRead(filename); @@ -540,6 +595,40 @@ GLint LinkProgram(int count, const GLint* shaders) return program; } +GLint LinkProgram(const std::vector& shaders) +{ + GLint program = glCreateProgram(); + + for (auto shader : shaders) + glAttachShader(program, shader); + + glLinkProgram(program); + + for (auto shader : shaders) + glDetachShader(program, shader); + + GLint status; + glGetProgramiv(program, GL_LINK_STATUS, &status); + + if (status != GL_TRUE) + { + GLint len; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len); + + std::string message(len, ' '); + glGetProgramInfoLog(program, len + 1, nullptr, message.data()); + + GetLogger()->Error("Shader program linking error occurred!\n%s\n", message.c_str()); + lastShaderError = std::string("Shader program linking error occurred!\n\n") + std::string(message.c_str()); + + glDeleteProgram(program); + + return 0; + } + + return program; +} + std::unique_ptr GetGLFrameBufferPixels(Math::IntPoint size) { auto pixels = MakeUnique(4 * size.x * size.y); diff --git a/src/graphics/opengl/glutil.h b/src/graphics/opengl/glutil.h index c205a0cc..e4c712e7 100644 --- a/src/graphics/opengl/glutil.h +++ b/src/graphics/opengl/glutil.h @@ -33,6 +33,7 @@ #include #include +#include struct SDL_Surface; @@ -97,10 +98,16 @@ GLenum TranslateType(Type type); std::string GetLastShaderError(); +std::string LoadSource(const std::string& path); + +GLint CreateShader(GLint type, const std::vector& sources); + GLint LoadShader(GLint type, const char* filename); GLint LinkProgram(int count, const GLint* shaders); +GLint LinkProgram(const std::vector& shaders); + // TODO: Moved this here temporarily only to remove code duplication in CGLDeviceXX struct PreparedTextureData { diff --git a/src/graphics/opengl/shaders/gl33/object_fs.glsl b/src/graphics/opengl/shaders/gl33/object_fs.glsl new file mode 100644 index 00000000..b2b4b2a6 --- /dev/null +++ b/src/graphics/opengl/shaders/gl33/object_fs.glsl @@ -0,0 +1,138 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +// FRAGMENT SHADER - TERRAIN RENDERER + +uniform bool uni_Lighting; +uniform mat4 uni_CameraMatrix; +uniform vec4 uni_LightPosition; +uniform float uni_LightIntensity; +uniform vec3 uni_LightColor; + +uniform vec2 uni_FogRange; +uniform vec3 uni_FogColor; + +uniform sampler2D uni_PrimaryTexture; +uniform sampler2D uni_SecondaryTexture; + +uniform vec4 uni_Color; +uniform float uni_PrimaryEnabled; +uniform float uni_Dirty; +uniform float uni_AlphaScissor; + +const float PI = 3.1415926; + +in VertexData +{ + vec4 Color; + vec2 TexCoord0; + vec2 TexCoord1; + vec3 Normal; + vec3 Position; + vec3 ShadowCoords[4]; +} data; + +out vec4 out_FragColor; + +vec3 schlickFresnel(float LdH, float metalness, vec3 color) +{ + vec3 f = mix(vec3(0.04), color, metalness); + + return f + (1.0 - f) * pow(1.0 - LdH, 5.0); +} + +float geomSmith(float dotProd, float roughness) +{ + float k = (roughness + 1.0) * (roughness + 1.0) / 8.0; + float denom = dotProd * (1 - k) + k; + return 1.0 / denom; +} + +float ggxDistribution(float NdH, float roughness) +{ + float alpha2 = roughness * roughness * roughness * roughness; + float d = (NdH * NdH) * (alpha2 - 1) + 1.0; + return alpha2 / (PI * d * d); +} + +vec3 PBR(vec3 position, vec3 color, vec3 normal, float roughness, float metalness) +{ + vec3 diffuseBrdf = mix(color, vec3(0.0), metalness); + + vec3 light = normalize(uni_LightPosition.xyz); + float lightIntensity = 1.0; + + vec3 view = normalize(uni_CameraMatrix[3].xyz - position); + vec3 halfway = normalize(view + light); + + float NdH = dot(normal, halfway); + float LdH = dot(light, halfway); + float NdL = max(dot(normal, light), 0.0); + float NdV = dot(normal, view); + + vec3 specBrdf = 0.25 * ggxDistribution(NdH, roughness) + * schlickFresnel(LdH, metalness, color) + * geomSmith(NdL, roughness) + * geomSmith(NdV, roughness); + + return (diffuseBrdf + PI * specBrdf) * lightIntensity * uni_LightColor * NdL; +} + +void main() +{ + vec4 albedo = data.Color * uni_Color; + + vec4 primary = texture(uni_PrimaryTexture, data.TexCoord0); + primary = mix(vec4(1.0), primary, uni_PrimaryEnabled); + albedo *= primary; + + vec3 dirty = texture(uni_SecondaryTexture, data.TexCoord1).rgb; + dirty = mix(vec3(1.0), dirty, uni_Dirty); + albedo.rgb *= dirty; + + vec3 color = albedo.rgb; + + if (uni_Lighting) + { + float roughness = 0.9; + float metalness = 0.0; + + float shadow = CalculateShadow(data.ShadowCoords); + + shadow = mix(0.5, 1.0, shadow); + + vec3 normal = mix(-data.Normal, data.Normal, float(gl_FrontFacing)); + + vec3 lighting = PBR(data.Position, color.rgb, normal, roughness, metalness); + + vec3 skyColor = vec3(1.0); + float skyIntensity = 0.25; + + color = lighting * shadow * (1.0 - skyIntensity) + color.rgb * skyColor * skyIntensity; + } + + float dist = length(uni_CameraMatrix[3].xyz - data.Position); + float fogAmount = clamp((dist - uni_FogRange.x) / (uni_FogRange.y - uni_FogRange.x), 0.0, 1.0); + + color = mix(color, uni_FogColor, fogAmount); + + if (albedo.a < uni_AlphaScissor) discard; + + out_FragColor = vec4(color, albedo.a); +} diff --git a/src/graphics/opengl/shaders/gl33/object_vs.glsl b/src/graphics/opengl/shaders/gl33/object_vs.glsl new file mode 100644 index 00000000..e49de0e3 --- /dev/null +++ b/src/graphics/opengl/shaders/gl33/object_vs.glsl @@ -0,0 +1,56 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +// VERTEX SHADER - TERRAIN RENDERER + +uniform mat4 uni_ProjectionMatrix; +uniform mat4 uni_ViewMatrix; +uniform mat4 uni_ModelMatrix; +uniform mat3 uni_NormalMatrix; + +layout(location = 0) in vec4 in_VertexCoord; +layout(location = 1) in vec3 in_Normal; +layout(location = 2) in vec4 in_Color; +layout(location = 3) in vec2 in_TexCoord0; +layout(location = 4) in vec2 in_TexCoord1; + +out VertexData +{ + vec4 Color; + vec2 TexCoord0; + vec2 TexCoord1; + vec3 Normal; + vec3 Position; + vec3 ShadowCoords[4]; +} data; + +void main() +{ + vec4 position = uni_ModelMatrix * in_VertexCoord; + vec4 eyeSpace = uni_ViewMatrix * position; + gl_Position = uni_ProjectionMatrix * eyeSpace; + + data.Color = in_Color; + data.TexCoord0 = in_TexCoord0; + data.TexCoord1 = in_TexCoord1; + data.Normal = mat3(uni_NormalMatrix) * in_Normal; + //data.Distance = abs(eyeSpace.z); + data.Position = position.xyz; + data.ShadowCoords = ProjectShadows(position.xyz); +} diff --git a/src/graphics/opengl/shaders/gl33/preamble.glsl b/src/graphics/opengl/shaders/gl33/preamble.glsl new file mode 100644 index 00000000..2ac103a8 --- /dev/null +++ b/src/graphics/opengl/shaders/gl33/preamble.glsl @@ -0,0 +1,21 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +// Common shader preamble +#version 330 core diff --git a/src/graphics/opengl/shaders/gl33/shadow.glsl b/src/graphics/opengl/shaders/gl33/shadow.glsl new file mode 100644 index 00000000..d2d2dbd6 --- /dev/null +++ b/src/graphics/opengl/shaders/gl33/shadow.glsl @@ -0,0 +1,74 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2021, Daniel Roux, EPSITEC SA & TerranovaTeam + * http://epsitec.ch; http://colobot.info; http://github.com/colobot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://gnu.org/licenses + */ + +// Shadow mapping implementation + +struct ShadowParam +{ + mat4 transform; + vec2 uv_offset; + vec2 uv_scale; +}; + +uniform sampler2DShadow uni_ShadowMap; +uniform int uni_ShadowRegions; +uniform ShadowParam uni_ShadowParam[4]; + +// Projects world-space position into an array of shadow coordinates +vec3[4] ProjectShadows(vec3 position) +{ + vec3 result[4]; + + for (int i = 0; i < uni_ShadowRegions; i++) + { + vec4 projected = uni_ShadowParam[i].transform * vec4(position, 1.0); + + result[i] = projected.xyz / projected.w; + } + + return result; +} + +// Calculates shadow based on array of shadow coordinates +float CalculateShadow(vec3 projected[4]) +{ + for (int i = 0; i < uni_ShadowRegions; i++) + { + if (projected[i].x < 0.0 || projected[i].x > 1.0) continue; + if (projected[i].y < 0.0 || projected[i].y > 1.0) continue; + + vec2 uv = projected[i].xy * uni_ShadowParam[i].uv_scale + uni_ShadowParam[i].uv_offset; + float depth = projected[i].z; + + vec3 coord = vec3(uv, depth); + + /* + float value = texture(uni_ShadowMap, coord); + value += textureOffset(uni_ShadowMap, coord, ivec2( 1, 0)); + value += textureOffset(uni_ShadowMap, coord, ivec2(-1, 0)); + value += textureOffset(uni_ShadowMap, coord, ivec2( 0, 1)); + value += textureOffset(uni_ShadowMap, coord, ivec2( 0,-1)); + return value * (1.0f / 5.0f); + // */ + + return texture(uni_ShadowMap, coord); + } + + return 1.0; +} diff --git a/src/graphics/opengl/shaders/gl33/terrain_fs.glsl b/src/graphics/opengl/shaders/gl33/terrain_fs.glsl index 8aa30310..28172655 100644 --- a/src/graphics/opengl/shaders/gl33/terrain_fs.glsl +++ b/src/graphics/opengl/shaders/gl33/terrain_fs.glsl @@ -18,7 +18,6 @@ */ // FRAGMENT SHADER - TERRAIN RENDERER -#version 330 core uniform mat4 uni_CameraMatrix; uniform vec4 uni_LightPosition; @@ -30,7 +29,6 @@ uniform vec3 uni_FogColor; uniform sampler2D uni_PrimaryTexture; uniform sampler2D uni_SecondaryTexture; -uniform sampler2DShadow uni_ShadowMap; const float PI = 3.1415926; @@ -40,8 +38,8 @@ in VertexData vec2 TexCoord0; vec2 TexCoord1; vec3 Normal; - vec4 ShadowCoord; vec3 Position; + vec3 ShadowCoords[4]; } data; out vec4 out_FragColor; @@ -100,12 +98,7 @@ void main() float roughness = 0.7; float metalness = 0.0; - float value = texture(uni_ShadowMap, data.ShadowCoord.xyz); - value += textureOffset(uni_ShadowMap, data.ShadowCoord.xyz, ivec2( 1, 0)); - value += textureOffset(uni_ShadowMap, data.ShadowCoord.xyz, ivec2(-1, 0)); - value += textureOffset(uni_ShadowMap, data.ShadowCoord.xyz, ivec2( 0, 1)); - value += textureOffset(uni_ShadowMap, data.ShadowCoord.xyz, ivec2( 0,-1)); - float shadow = value * (1.0f / 5.0f); + float shadow = CalculateShadow(data.ShadowCoords); shadow = mix(0.5, 1.0, shadow); diff --git a/src/graphics/opengl/shaders/gl33/terrain_vs.glsl b/src/graphics/opengl/shaders/gl33/terrain_vs.glsl index 3d820cfe..e49de0e3 100644 --- a/src/graphics/opengl/shaders/gl33/terrain_vs.glsl +++ b/src/graphics/opengl/shaders/gl33/terrain_vs.glsl @@ -18,12 +18,10 @@ */ // VERTEX SHADER - TERRAIN RENDERER -#version 330 core uniform mat4 uni_ProjectionMatrix; uniform mat4 uni_ViewMatrix; uniform mat4 uni_ModelMatrix; -uniform mat4 uni_ShadowMatrix; uniform mat3 uni_NormalMatrix; layout(location = 0) in vec4 in_VertexCoord; @@ -38,8 +36,8 @@ out VertexData vec2 TexCoord0; vec2 TexCoord1; vec3 Normal; - vec4 ShadowCoord; vec3 Position; + vec3 ShadowCoords[4]; } data; void main() @@ -47,13 +45,12 @@ void main() vec4 position = uni_ModelMatrix * in_VertexCoord; vec4 eyeSpace = uni_ViewMatrix * position; gl_Position = uni_ProjectionMatrix * eyeSpace; - vec4 shadowCoord = uni_ShadowMatrix * position; data.Color = in_Color; data.TexCoord0 = in_TexCoord0; data.TexCoord1 = in_TexCoord1; data.Normal = mat3(uni_NormalMatrix) * in_Normal; - data.ShadowCoord = vec4(shadowCoord.xyz / shadowCoord.w, 1.0f); //data.Distance = abs(eyeSpace.z); data.Position = position.xyz; + data.ShadowCoords = ProjectShadows(position.xyz); }