diff --git a/data/shaders/sunlightshadow.frag b/data/shaders/sunlightshadow.frag index 4dbc9cbee..ca199d4cf 100644 --- a/data/shaders/sunlightshadow.frag +++ b/data/shaders/sunlightshadow.frag @@ -1,6 +1,6 @@ uniform sampler2D ntex; uniform sampler2D dtex; -uniform sampler2DArray shadowtex; +uniform sampler2DArrayShadow shadowtex; uniform float split0; uniform float split1; @@ -22,10 +22,17 @@ float getShadowFactor(vec3 pos, int index) vec4 shadowcoord = (ShadowViewProjMatrixes[index] * InverseViewMatrix * vec4(pos, 1.0)); shadowcoord.xy /= shadowcoord.w; vec2 shadowtexcoord = shadowcoord.xy * 0.5 + 0.5; + float d = .5 * shadowcoord.z + .5; - float z = texture(shadowtex, vec3(shadowtexcoord, float(index))).x; - float d = shadowcoord.z; - return min(pow(exp(-32. * d) * z, 8.), 1.); + float result = 0.; + + for (float i = -1.; i <= 1.; i += 1.) + { + for (float j = -1.; j <= 1.; j += 1.) + result += texture(shadowtex, vec4(shadowtexcoord + vec2(i,j) / 1024., float(index), d)); + } + + return result / 9.; } void main() { diff --git a/data/shaders/sunlightshadowesm.frag b/data/shaders/sunlightshadowesm.frag new file mode 100644 index 000000000..4dbc9cbee --- /dev/null +++ b/data/shaders/sunlightshadowesm.frag @@ -0,0 +1,61 @@ +uniform sampler2D ntex; +uniform sampler2D dtex; +uniform sampler2DArray shadowtex; + +uniform float split0; +uniform float split1; +uniform float split2; +uniform float splitmax; + +in vec2 uv; +out vec4 Diff; +out vec4 Spec; + +vec3 DecodeNormal(vec2 n); +vec3 SpecularBRDF(vec3 normal, vec3 eyedir, vec3 lightdir, vec3 color, float roughness); +vec3 DiffuseBRDF(vec3 normal, vec3 eyedir, vec3 lightdir, vec3 color, float roughness); +vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix); +vec3 SunMRP(vec3 normal, vec3 eyedir); + +float getShadowFactor(vec3 pos, int index) +{ + vec4 shadowcoord = (ShadowViewProjMatrixes[index] * InverseViewMatrix * vec4(pos, 1.0)); + shadowcoord.xy /= shadowcoord.w; + vec2 shadowtexcoord = shadowcoord.xy * 0.5 + 0.5; + + float z = texture(shadowtex, vec3(shadowtexcoord, float(index))).x; + float d = shadowcoord.z; + return min(pow(exp(-32. * d) * z, 8.), 1.); +} + +void main() { + vec2 uv = gl_FragCoord.xy / screen; + float z = texture(dtex, uv).x; + vec4 xpos = getPosFromUVDepth(vec3(uv, z), InverseProjectionMatrix); + + vec3 norm = normalize(DecodeNormal(2. * texture(ntex, uv).xy - 1.)); + float roughness =texture(ntex, uv).z; + vec3 eyedir = -normalize(xpos.xyz); + + vec3 Lightdir = SunMRP(norm, eyedir); + float NdotL = clamp(dot(norm, Lightdir), 0., 1.); + + vec3 Specular = SpecularBRDF(norm, eyedir, Lightdir, vec3(1.), roughness); + vec3 Diffuse = DiffuseBRDF(norm, eyedir, Lightdir, vec3(1.), roughness); + + // Shadows + float factor; + if (xpos.z < split0) + factor = getShadowFactor(xpos.xyz, 0); + else if (xpos.z < split1) + factor = getShadowFactor(xpos.xyz, 1); + else if (xpos.z < split2) + factor = getShadowFactor(xpos.xyz, 2); + else if (xpos.z < splitmax) + factor = getShadowFactor(xpos.xyz, 3); + else + factor = 1.; + + Diff = vec4(factor * NdotL * Diffuse * sun_col, 1.); + Spec = vec4(factor * NdotL * Specular * sun_col, 1.); +} diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index f24079747..8df9baf42 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -470,6 +470,9 @@ namespace UserConfigParams PARAM_PREFIX BoolUserConfigParam m_sdsm PARAM_DEFAULT(BoolUserConfigParam(false, "enable_sdsm", &m_video_group, "Enable Sampled Distribued Shadow Map (buggy atm)")); + PARAM_PREFIX BoolUserConfigParam m_esm + PARAM_DEFAULT(BoolUserConfigParam(false, "enable_esm", + &m_video_group, "Enable Exponential Shadow Map (better but slower)")); // ---- Debug - not saved to config file /** If gamepad debugging is enabled. */ diff --git a/src/graphics/central_settings.cpp b/src/graphics/central_settings.cpp index d97d56499..5ef7170e5 100644 --- a/src/graphics/central_settings.cpp +++ b/src/graphics/central_settings.cpp @@ -309,4 +309,10 @@ bool CentralVideoSettings::isSDSMEnabled() const bool CentralVideoSettings::isAZDOEnabled() const { return supportsIndirectInstancingRendering() && isARBBindlessTextureUsable() && isARBMultiDrawIndirectUsable() && UserConfigParams::m_azdo; +} + +// Switch between Exponential Shadow Map (better but slower filtering) and Percentage Closer Filtering (faster but with some stability issue) +bool CentralVideoSettings::isESMEnabled() const +{ + return UserConfigParams::m_esm; } \ No newline at end of file diff --git a/src/graphics/central_settings.hpp b/src/graphics/central_settings.hpp index 1f38eb65f..a354c73fe 100644 --- a/src/graphics/central_settings.hpp +++ b/src/graphics/central_settings.hpp @@ -65,6 +65,7 @@ public: bool isTextureCompressionEnabled() const; bool isSDSMEnabled() const; bool isAZDOEnabled() const; + bool isESMEnabled() const; }; extern CentralVideoSettings* CVS; diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index c708ed021..15a9ce1b0 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -271,19 +271,6 @@ void PostProcessing::renderSunlight(const core::vector3df &direction, const vide DrawFullScreenEffect(direction, col); } -extern float shadowSplit[5]; - -void PostProcessing::renderShadowedSunlight(const core::vector3df &direction, const video::SColorf &col, const std::vector &sun_ortho_matrix, GLuint depthtex) -{ - glEnable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glBlendFunc(GL_ONE, GL_ONE); - glBlendEquation(GL_FUNC_ADD); - - FullScreenShader::ShadowedSunLightShader::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), depthtex); - DrawFullScreenEffect(shadowSplit[1], shadowSplit[2], shadowSplit[3], shadowSplit[4], direction, col); -} - static std::vector getGaussianWeight(float sigma, size_t count) { diff --git a/src/graphics/post_processing.hpp b/src/graphics/post_processing.hpp index a33f3c19c..8f373d36c 100644 --- a/src/graphics/post_processing.hpp +++ b/src/graphics/post_processing.hpp @@ -75,7 +75,6 @@ public: /** Generate diffuse and specular map */ void renderSunlight(const core::vector3df &direction, const video::SColorf &col); - void renderShadowedSunlight(const core::vector3df &direction, const video::SColorf &col, const std::vector &sun_ortho_matrix, unsigned depthtex); void renderSSAO(); void renderEnvMap(const float *bSHCoeff, const float *gSHCoeff, const float *rSHCoeff, unsigned skycubemap); diff --git a/src/graphics/render_lighting.cpp b/src/graphics/render_lighting.cpp index f573aa9fc..4438613c2 100644 --- a/src/graphics/render_lighting.cpp +++ b/src/graphics/render_lighting.cpp @@ -127,6 +127,8 @@ void IrrDriver::uploadLightingData() glBufferSubData(GL_UNIFORM_BUFFER, 0, 36 * sizeof(float), Lighting); } +extern float shadowSplit[5]; + void IrrDriver::renderLights(unsigned pointlightcount, bool hasShadow) { //RH @@ -184,7 +186,23 @@ void IrrDriver::renderLights(unsigned pointlightcount, bool hasShadow) { ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_SUN)); if (World::getWorld() && CVS->isShadowEnabled() && hasShadow) - m_post_processing->renderShadowedSunlight(irr_driver->getSunDirection(), irr_driver->getSunColor(), sun_ortho_matrix, m_rtts->getShadowFBO().getRTT()[0]); + { + glEnable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glBlendFunc(GL_ONE, GL_ONE); + glBlendEquation(GL_FUNC_ADD); + + if (CVS->isESMEnabled()) + { + FullScreenShader::ShadowedSunLightShaderESM::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), m_rtts->getShadowFBO().getRTT()[0]); + DrawFullScreenEffect(shadowSplit[1], shadowSplit[2], shadowSplit[3], shadowSplit[4], irr_driver->getSunDirection(), irr_driver->getSunColor()); + } + else + { + FullScreenShader::ShadowedSunLightShaderPCF::getInstance()->SetTextureUnits(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), m_rtts->getShadowFBO().getDepthTexture()); + DrawFullScreenEffect(shadowSplit[1], shadowSplit[2], shadowSplit[3], shadowSplit[4], irr_driver->getSunDirection(), irr_driver->getSunColor()); + } + } else m_post_processing->renderSunlight(irr_driver->getSunDirection(), irr_driver->getSunColor()); } diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index dae5f176c..c278c5f98 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -863,10 +863,8 @@ GLuint createShadowSampler() glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - int aniso = UserConfigParams::m_anisotropic; - if (aniso == 0) aniso = 1; - glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso); + glSamplerParameterf(id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glSamplerParameterf(id, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); return id; #endif } @@ -883,6 +881,38 @@ void BindTextureShadow(GLuint TU, GLuint tex) glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); } + +GLuint createTrilinearClampedArray() +{ +#ifdef GL_VERSION_3_3 + unsigned id; + glGenSamplers(1, &id); + glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + int aniso = UserConfigParams::m_anisotropic; + if (aniso == 0) aniso = 1; + glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso); + return id; +#endif +} + +void BindTrilinearClampedArrayTexture(unsigned TU, unsigned tex) +{ + glActiveTexture(GL_TEXTURE0 + TU); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + int aniso = UserConfigParams::m_anisotropic; + if (aniso == 0) aniso = 1; + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso); +} + void BindTextureVolume(GLuint TU, GLuint tex) { glActiveTexture(GL_TEXTURE0 + TU); @@ -1659,7 +1689,7 @@ namespace FullScreenShader AssignSamplerNames(Program, 0, "ntex", 1, "dtex", 2, "probe"); } - ShadowedSunLightShader::ShadowedSunLightShader() + ShadowedSunLightShaderPCF::ShadowedSunLightShaderPCF() { Program = LoadProgram(OBJECT, GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), @@ -1675,6 +1705,22 @@ namespace FullScreenShader AssignUniforms("split0", "split1", "split2", "splitmax", "direction", "col"); } + ShadowedSunLightShaderESM::ShadowedSunLightShaderESM() + { + Program = LoadProgram(OBJECT, + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/decodeNormal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/SpecularBRDF.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/DiffuseBRDF.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/SunMRP.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/sunlightshadowesm.frag").c_str()); + + // Use 8 to circumvent a catalyst bug when binding sampler + AssignSamplerNames(Program, 0, "ntex", 1, "dtex", 8, "shadowtex"); + AssignUniforms("split0", "split1", "split2", "splitmax", "direction", "col"); + } + RadianceHintsConstructionShader::RadianceHintsConstructionShader() { if (CVS->isAMDVertexShaderLayerUsable()) diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 6892ddd78..25e3fcb94 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -426,10 +426,16 @@ public: IBLShader(); }; -class ShadowedSunLightShader : public ShaderHelperSingleton, public TextureRead +class ShadowedSunLightShaderPCF : public ShaderHelperSingleton, public TextureRead { public: - ShadowedSunLightShader(); + ShadowedSunLightShaderPCF(); +}; + +class ShadowedSunLightShaderESM : public ShaderHelperSingleton, public TextureRead +{ +public: + ShadowedSunLightShaderESM(); }; class RadianceHintsConstructionShader : public ShaderHelperSingleton, public TextureRead diff --git a/src/graphics/shaders_util.hpp b/src/graphics/shaders_util.hpp index 8532ed876..24b2df7f6 100644 --- a/src/graphics/shaders_util.hpp +++ b/src/graphics/shaders_util.hpp @@ -221,6 +221,7 @@ enum SamplerType { Shadow_Sampler, Volume_Linear_Filtered, Trilinear_cubemap, + Trilinear_Clamped_Array2D, }; void setTextureSampler(GLenum, GLuint, GLuint, GLuint); @@ -486,6 +487,33 @@ struct BindTexture } }; +GLuint createTrilinearClampedArray(); + +template +struct CreateSamplers +{ + static void exec(std::vector &v, std::vector &e) + { + v.push_back(createTrilinearClampedArray()); + e.push_back(GL_TEXTURE_2D_ARRAY); + CreateSamplers::exec(v, e); + } +}; + +void BindTrilinearClampedArrayTexture(unsigned TU, unsigned tex); + +template +struct BindTexture +{ + template + static void exec(const std::vector &TU, GLuint TexId, Args... args) + { + BindTrilinearClampedArrayTexture(TU[N], TexId); + BindTexture::template exec(TU, args...); + } +}; + + template class TextureRead {