diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index fd912aa2d3..f766e81eea 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -245,6 +245,7 @@ namespace MWRender globalDefines["clamp"] = Settings::Manager::getBool("clamp lighting", "Shaders") ? "1" : "0"; globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0"; globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0"; + globalDefines["useGPUShader4"] = "0"; float groundcoverDistance = (Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover")) - 1024) * 0.93; globalDefines["groundcoverFadeStart"] = std::to_string(groundcoverDistance * 0.9f); diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 12e6c9b494..89e001a980 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -894,12 +894,17 @@ void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & sh // This can't be part of the constructor as OSG mandates that there be a trivial constructor available osg::ref_ptr castingVertexShader = shaderManager.getShader("shadowcasting_vertex.glsl", {}, osg::Shader::VERTEX); + osg::ref_ptr exts = osg::GLExtensions::Get(0, false); + std::string useGPUShader4 = exts && exts->isGpuShader4Supported ? "1" : "0"; for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc) { auto& program = _castingPrograms[alphaFunc - GL_NEVER]; program = new osg::Program(); program->addShader(castingVertexShader); - program->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", { {"alphaFunc", std::to_string(alphaFunc)}, {"alphaToCoverage", "0"} }, osg::Shader::FRAGMENT)); + program->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", { {"alphaFunc", std::to_string(alphaFunc)}, + {"alphaToCoverage", "0"}, + {"useGPUShader4", useGPUShader4} + }, osg::Shader::FRAGMENT)); } } diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 782ffa016d..eae9ad2dbb 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -412,6 +413,12 @@ namespace Shader writableStateSet->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, osg::StateAttribute::ON); defineMap["alphaToCoverage"] = "1"; } + + // Preventing alpha tested stuff shrinking as lower mip levels are used requires knowing the texture size + osg::ref_ptr exts = osg::GLExtensions::Get(0, false); + if (exts && exts->isGpuShader4Supported) + defineMap["useGPUShader4"] = "1"; + // We could fall back to a texture size uniform if EXT_gpu_shader4 is missing } if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT) diff --git a/files/shaders/alpha.glsl b/files/shaders/alpha.glsl index 2551a02052..05be801e93 100644 --- a/files/shaders/alpha.glsl +++ b/files/shaders/alpha.glsl @@ -12,6 +12,29 @@ uniform float alphaRef; #endif +float mipmapLevel(vec2 scaleduv) +{ + vec2 dUVdx = dFdx(scaleduv); + vec2 dUVdy = dFdy(scaleduv); + float maxDUVSquared = max(dot(dUVdx, dUVdx), dot(dUVdy, dUVdy)); + return max(0.0, 0.5 * log2(maxDUVSquared)); +} + +float coveragePreservingAlphaScale(sampler2D diffuseMap, vec2 uv) +{ + #if @alphaFunc != FUNC_ALWAYS && @alphaFunc != FUNC_NEVER + vec2 textureSize; + #if @useGPUShader4 + textureSize = textureSize2D(diffuseMap, 0); + #else + textureSize = vec2(256.0); + #endif + return 1.0 + mipmapLevel(uv * textureSize) * 0.25; + #else + return 1.0; + #endif +} + void alphaTest() { #if @alphaToCoverage diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index 51a568c7a6..e0d7833c9e 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -1,5 +1,9 @@ #version 120 +#if @useGPUShader4 + #extension GL_EXT_gpu_shader4: require +#endif + #if @diffuseMap uniform sampler2D diffuseMap; varying vec2 diffuseMapUV; @@ -110,6 +114,7 @@ void main() #if @diffuseMap gl_FragData[0] = texture2D(diffuseMap, adjustedDiffuseUV); + gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, adjustedDiffuseUV); #else gl_FragData[0] = vec4(1.0); #endif diff --git a/files/shaders/shadowcasting_fragment.glsl b/files/shaders/shadowcasting_fragment.glsl index 8c53c542bd..ea8a63313e 100644 --- a/files/shaders/shadowcasting_fragment.glsl +++ b/files/shaders/shadowcasting_fragment.glsl @@ -1,5 +1,7 @@ #version 120 +#extension EXT_gpu_shader4: enable + uniform sampler2D diffuseMap; varying vec2 diffuseMapUV;