diff --git a/apps/launcher/advancedpage.cpp b/apps/launcher/advancedpage.cpp index eb1a88ea59..9f1c0c6a03 100644 --- a/apps/launcher/advancedpage.cpp +++ b/apps/launcher/advancedpage.cpp @@ -149,7 +149,7 @@ bool Launcher::AdvancedPage::loadSettings() loadSettingBool(postprocessEnabledCheckBox, "enabled", "Post Processing"); loadSettingBool(postprocessLiveReloadCheckBox, "live reload", "Post Processing"); loadSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing"); - postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("hdr exposure time", "Post Processing")); + postprocessHDRTimeComboBox->setValue(Settings::Manager::getDouble("auto exposure speed", "Post Processing")); connect(skyBlendingCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotSkyBlendingToggled(bool))); loadSettingBool(radialFogCheckBox, "radial fog", "Fog"); @@ -305,8 +305,8 @@ void Launcher::AdvancedPage::saveSettings() saveSettingBool(postprocessLiveReloadCheckBox, "live reload", "Post Processing"); saveSettingBool(postprocessTransparentPostpassCheckBox, "transparent postpass", "Post Processing"); double hdrExposureTime = postprocessHDRTimeComboBox->value(); - if (hdrExposureTime != Settings::Manager::getDouble("hdr exposure time", "Post Processing")) - Settings::Manager::setDouble("hdr exposure time", "Post Processing", hdrExposureTime); + if (hdrExposureTime != Settings::Manager::getDouble("auto exposure speed", "Post Processing")) + Settings::Manager::setDouble("auto exposure speed", "Post Processing", hdrExposureTime); saveSettingBool(radialFogCheckBox, "radial fog", "Fog"); saveSettingBool(exponentialFogCheckBox, "exponential fog", "Fog"); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 75d051c63b..ed742fcbc9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -23,7 +23,7 @@ add_openmw_dir (mwrender creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation screenshotmanager bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover - postprocessor pingpongcull hdr pingpongcanvas transparentpass navmeshmode + postprocessor pingpongcull luminancecalculator pingpongcanvas transparentpass navmeshmode ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/hdr.cpp b/apps/openmw/mwrender/hdr.cpp deleted file mode 100644 index 95015da83a..0000000000 --- a/apps/openmw/mwrender/hdr.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "hdr.hpp" - -#include -#include - -#include "pingpongcanvas.hpp" - -namespace MWRender -{ - HDRDriver::HDRDriver(Shader::ShaderManager& shaderManager) - : mCompiled(false) - , mEnabled(false) - , mWidth(1) - , mHeight(1) - { - const float hdrExposureTime = std::clamp(Settings::Manager::getFloat("hdr exposure time", "Post Processing"), 0.f, 1.f); - - constexpr float minLog = -9.0; - constexpr float maxLog = 4.0; - constexpr float logLumRange = (maxLog - minLog); - constexpr float invLogLumRange = 1.0 / logLumRange; - constexpr float epsilon = 0.004; - - Shader::ShaderManager::DefineMap defines = { - {"minLog", std::to_string(minLog)}, - {"maxLog", std::to_string(maxLog)}, - {"logLumRange", std::to_string(logLumRange)}, - {"invLogLumRange", std::to_string(invLogLumRange)}, - {"hdrExposureTime", std::to_string(hdrExposureTime)}, - {"epsilon", std::to_string(epsilon)}, - }; - - auto vertex = shaderManager.getShader("fullscreen_tri_vertex.glsl", {}, osg::Shader::VERTEX); - auto hdrLuminance = shaderManager.getShader("hdr_luminance_fragment.glsl", defines, osg::Shader::FRAGMENT); - auto hdr = shaderManager.getShader("hdr_fragment.glsl", defines, osg::Shader::FRAGMENT); - - mProgram = shaderManager.getProgram(vertex, hdr); - mLuminanceProgram = shaderManager.getProgram(vertex, hdrLuminance); - } - - void HDRDriver::compile() - { - int mipmapLevels = osg::Image::computeNumberOfMipmapLevels(mWidth, mHeight); - - for (auto& buffer : mBuffers) - { - buffer.texture = new osg::Texture2D; - buffer.texture->setInternalFormat(GL_R16F); - buffer.texture->setSourceFormat(GL_RED); - buffer.texture->setSourceType(GL_FLOAT); - buffer.texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR_MIPMAP_NEAREST); - buffer.texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); - buffer.texture->setTextureSize(mWidth, mHeight); - buffer.texture->setNumMipmapLevels(mipmapLevels); - - buffer.finalTexture = new osg::Texture2D; - buffer.finalTexture->setInternalFormat(GL_R16F); - buffer.finalTexture->setSourceFormat(GL_RED); - buffer.finalTexture->setSourceType(GL_FLOAT); - buffer.finalTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); - buffer.finalTexture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); - buffer.finalTexture->setTextureSize(1, 1); - - buffer.finalFbo = new osg::FrameBufferObject; - buffer.finalFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.finalTexture)); - - buffer.fullscreenFbo = new osg::FrameBufferObject; - buffer.fullscreenFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.texture)); - - buffer.mipmapFbo = new osg::FrameBufferObject; - buffer.mipmapFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.texture, mipmapLevels - 1)); - - buffer.fullscreenStateset = new osg::StateSet; - buffer.fullscreenStateset->setAttributeAndModes(mLuminanceProgram); - buffer.fullscreenStateset->addUniform(new osg::Uniform("sceneTex", 0)); - - buffer.mipmapStateset = new osg::StateSet; - buffer.mipmapStateset->setAttributeAndModes(mProgram); - buffer.mipmapStateset->setTextureAttributeAndModes(0, buffer.texture); - buffer.mipmapStateset->addUniform(new osg::Uniform("luminanceSceneTex", 0)); - buffer.mipmapStateset->addUniform(new osg::Uniform("prevLuminanceSceneTex", 1)); - } - - mBuffers[0].mipmapStateset->setTextureAttributeAndModes(1, mBuffers[1].finalTexture); - mBuffers[1].mipmapStateset->setTextureAttributeAndModes(1, mBuffers[0].finalTexture); - - mCompiled = true; - } - - void HDRDriver::draw(const PingPongCanvas& canvas, osg::RenderInfo& renderInfo, osg::State& state, osg::GLExtensions* ext, size_t frameId) - { - if (!mEnabled) - return; - - if (!mCompiled) - compile(); - - auto& hdrBuffer = mBuffers[frameId]; - hdrBuffer.fullscreenFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); - hdrBuffer.fullscreenStateset->setTextureAttributeAndModes(0, canvas.getSceneTexture(frameId)); - - state.apply(hdrBuffer.fullscreenStateset); - canvas.drawGeometry(renderInfo); - - state.applyTextureAttribute(0, hdrBuffer.texture); - ext->glGenerateMipmap(GL_TEXTURE_2D); - - hdrBuffer.mipmapFbo->apply(state, osg::FrameBufferObject::READ_FRAMEBUFFER); - hdrBuffer.finalFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); - - ext->glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_LINEAR); - - state.apply(hdrBuffer.mipmapStateset); - canvas.drawGeometry(renderInfo); - - ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0); - } - - osg::ref_ptr HDRDriver::getLuminanceTexture(size_t frameId) const - { - return mBuffers[frameId].finalTexture; - } -} \ No newline at end of file diff --git a/apps/openmw/mwrender/hdr.hpp b/apps/openmw/mwrender/hdr.hpp deleted file mode 100644 index 95bdc6aa0a..0000000000 --- a/apps/openmw/mwrender/hdr.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef OPENMW_MWRENDER_HDR_H -#define OPENMW_MWRENDER_HDR_H - -#include - -#include -#include -#include - -namespace Shader -{ - class ShaderManager; -} - -namespace MWRender -{ - class PingPongCanvas; - - class HDRDriver - { - - public: - - HDRDriver() = default; - - HDRDriver(Shader::ShaderManager& shaderManager); - - void draw(const PingPongCanvas& canvas, osg::RenderInfo& renderInfo, osg::State& state, osg::GLExtensions* ext, size_t frameId); - - bool isEnabled() const { return mEnabled; } - - void enable() { mEnabled = true; } - void disable() { mEnabled = false; } - - void dirty(int w, int h) - { - mWidth = w; - mHeight = h; - mCompiled = false; - } - - osg::ref_ptr getLuminanceTexture(size_t frameId) const; - - private: - - void compile(); - - struct HDRContainer - { - osg::ref_ptr fullscreenFbo; - osg::ref_ptr mipmapFbo; - osg::ref_ptr finalFbo; - osg::ref_ptr texture; - osg::ref_ptr finalTexture; - osg::ref_ptr fullscreenStateset; - osg::ref_ptr mipmapStateset; - }; - - std::array mBuffers; - osg::ref_ptr mLuminanceProgram; - osg::ref_ptr mProgram; - - bool mCompiled; - bool mEnabled; - - int mWidth; - int mHeight; - }; -} - -#endif diff --git a/apps/openmw/mwrender/luminancecalculator.cpp b/apps/openmw/mwrender/luminancecalculator.cpp new file mode 100644 index 0000000000..06a0441d0d --- /dev/null +++ b/apps/openmw/mwrender/luminancecalculator.cpp @@ -0,0 +1,134 @@ +#include "luminancecalculator.hpp" + +#include +#include + +#include "pingpongcanvas.hpp" + +namespace MWRender +{ + LuminanceCalculator::LuminanceCalculator(Shader::ShaderManager& shaderManager) + { + const float hdrExposureTime = std::max(Settings::Manager::getFloat("auto exposure speed", "Post Processing"), 0.0001f); + + constexpr float minLog = -9.0; + constexpr float maxLog = 4.0; + constexpr float logLumRange = (maxLog - minLog); + constexpr float invLogLumRange = 1.0 / logLumRange; + constexpr float epsilon = 0.004; + + Shader::ShaderManager::DefineMap defines = { + {"minLog", std::to_string(minLog)}, + {"maxLog", std::to_string(maxLog)}, + {"logLumRange", std::to_string(logLumRange)}, + {"invLogLumRange", std::to_string(invLogLumRange)}, + {"hdrExposureTime", std::to_string(hdrExposureTime)}, + {"epsilon", std::to_string(epsilon)}, + }; + + auto vertex = shaderManager.getShader("fullscreen_tri_vertex.glsl", {}, osg::Shader::VERTEX); + auto luminanceFragment = shaderManager.getShader("hdr_luminance_fragment.glsl", defines, osg::Shader::FRAGMENT); + auto resolveFragment = shaderManager.getShader("hdr_resolve_fragment.glsl", defines, osg::Shader::FRAGMENT); + + mResolveProgram = shaderManager.getProgram(vertex, resolveFragment); + mLuminanceProgram = shaderManager.getProgram(vertex, luminanceFragment); + } + + void LuminanceCalculator::compile() + { + int mipmapLevels = osg::Image::computeNumberOfMipmapLevels(mWidth, mHeight); + + for (auto& buffer : mBuffers) + { + buffer.mipmappedSceneLuminanceTex = new osg::Texture2D; + buffer.mipmappedSceneLuminanceTex->setInternalFormat(GL_R16F); + buffer.mipmappedSceneLuminanceTex->setSourceFormat(GL_RED); + buffer.mipmappedSceneLuminanceTex->setSourceType(GL_FLOAT); + buffer.mipmappedSceneLuminanceTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR_MIPMAP_NEAREST); + buffer.mipmappedSceneLuminanceTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); + buffer.mipmappedSceneLuminanceTex->setTextureSize(mWidth, mHeight); + buffer.mipmappedSceneLuminanceTex->setNumMipmapLevels(mipmapLevels); + + buffer.luminanceTex = new osg::Texture2D; + buffer.luminanceTex->setInternalFormat(GL_R16F); + buffer.luminanceTex->setSourceFormat(GL_RED); + buffer.luminanceTex->setSourceType(GL_FLOAT); + buffer.luminanceTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); + buffer.luminanceTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); + buffer.luminanceTex->setTextureSize(1, 1); + + buffer.luminanceProxyTex = new osg::Texture2D(*buffer.luminanceTex); + + buffer.resolveFbo = new osg::FrameBufferObject; + buffer.resolveFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.luminanceTex)); + + buffer.luminanceProxyFbo = new osg::FrameBufferObject; + buffer.luminanceProxyFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.luminanceProxyTex)); + + buffer.resolveSceneLumFbo = new osg::FrameBufferObject; + buffer.resolveSceneLumFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.mipmappedSceneLuminanceTex, mipmapLevels - 1)); + + buffer.sceneLumFbo = new osg::FrameBufferObject; + buffer.sceneLumFbo->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, osg::FrameBufferAttachment(buffer.mipmappedSceneLuminanceTex)); + + buffer.sceneLumSS = new osg::StateSet; + buffer.sceneLumSS->setAttributeAndModes(mLuminanceProgram); + buffer.sceneLumSS->addUniform(new osg::Uniform("sceneTex", 0)); + + buffer.resolveSS = new osg::StateSet; + buffer.resolveSS->setAttributeAndModes(mResolveProgram); + buffer.resolveSS->setTextureAttributeAndModes(0, buffer.luminanceProxyTex); + buffer.resolveSS->addUniform(new osg::Uniform("luminanceSceneTex", 0)); + buffer.resolveSS->addUniform(new osg::Uniform("prevLuminanceSceneTex", 1)); + } + + mBuffers[0].resolveSS->setTextureAttributeAndModes(1, mBuffers[1].luminanceTex); + mBuffers[1].resolveSS->setTextureAttributeAndModes(1, mBuffers[0].luminanceTex); + + mCompiled = true; + } + + void LuminanceCalculator::draw(const PingPongCanvas& canvas, osg::RenderInfo& renderInfo, osg::State& state, osg::GLExtensions* ext, size_t frameId) + { + if (!mEnabled) + return; + + bool dirty = !mCompiled; + + if (dirty) + compile(); + + auto& buffer = mBuffers[frameId]; + buffer.sceneLumFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); + buffer.sceneLumSS->setTextureAttributeAndModes(0, canvas.getSceneTexture(frameId)); + + state.apply(buffer.sceneLumSS); + canvas.drawGeometry(renderInfo); + + state.applyTextureAttribute(0, buffer.mipmappedSceneLuminanceTex); + ext->glGenerateMipmap(GL_TEXTURE_2D); + + buffer.resolveSceneLumFbo->apply(state, osg::FrameBufferObject::READ_FRAMEBUFFER); + buffer.luminanceProxyFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); + ext->glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + if (dirty) { + // Use current frame data for previous frame to warm up calculations and prevent popin + mBuffers[(frameId + 1) % 2].resolveFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); + ext->glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + buffer.luminanceProxyFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); + } + + buffer.resolveFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); + state.apply(buffer.resolveSS); + canvas.drawGeometry(renderInfo); + + ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0); + } + + osg::ref_ptr LuminanceCalculator::getLuminanceTexture(size_t frameId) const + { + return mBuffers[frameId].luminanceTex; + } +} \ No newline at end of file diff --git a/apps/openmw/mwrender/luminancecalculator.hpp b/apps/openmw/mwrender/luminancecalculator.hpp new file mode 100644 index 0000000000..9d17b05653 --- /dev/null +++ b/apps/openmw/mwrender/luminancecalculator.hpp @@ -0,0 +1,74 @@ +#ifndef OPENMW_MWRENDER_LUMINANCECALCULATOR_H +#define OPENMW_MWRENDER_LUMINANCECALCULATOR_H + +#include + +#include +#include +#include + +namespace Shader +{ + class ShaderManager; +} + +namespace MWRender +{ + class PingPongCanvas; + + class LuminanceCalculator + { + + public: + + LuminanceCalculator() = default; + + LuminanceCalculator(Shader::ShaderManager& shaderManager); + + void draw(const PingPongCanvas& canvas, osg::RenderInfo& renderInfo, osg::State& state, osg::GLExtensions* ext, size_t frameId); + + bool isEnabled() const { return mEnabled; } + + void enable() { mEnabled = true; } + void disable() { mEnabled = false; } + + void dirty(int w, int h) + { + constexpr float scale = 0.5; + mWidth = w * scale; + mHeight = h * scale; + mCompiled = false; + } + + osg::ref_ptr getLuminanceTexture(size_t frameId) const; + + private: + + void compile(); + + struct Container + { + osg::ref_ptr sceneLumFbo; + osg::ref_ptr resolveSceneLumFbo; + osg::ref_ptr resolveFbo; + osg::ref_ptr luminanceProxyFbo; + osg::ref_ptr mipmappedSceneLuminanceTex; + osg::ref_ptr luminanceTex; + osg::ref_ptr luminanceProxyTex; + osg::ref_ptr sceneLumSS; + osg::ref_ptr resolveSS; + }; + + std::array mBuffers; + osg::ref_ptr mLuminanceProgram; + osg::ref_ptr mResolveProgram; + + bool mCompiled = false; + bool mEnabled = false; + + int mWidth = 1; + int mHeight = 1; + }; +} + +#endif diff --git a/apps/openmw/mwrender/pingpongcanvas.cpp b/apps/openmw/mwrender/pingpongcanvas.cpp index f6b8e464fe..37c357f3fb 100644 --- a/apps/openmw/mwrender/pingpongcanvas.cpp +++ b/apps/openmw/mwrender/pingpongcanvas.cpp @@ -27,8 +27,8 @@ namespace MWRender addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, 3)); - mHDRDriver = HDRDriver(shaderManager); - mHDRDriver.disable(); + mLuminanceCalculator = LuminanceCalculator(shaderManager); + mLuminanceCalculator.disable(); Shader::ShaderManager::DefineMap defines; Stereo::Manager::instance().shaderStereoDefines(defines); @@ -158,7 +158,7 @@ namespace MWRender mMultiviewResolveStateSet->setTextureAttribute(PostProcessor::Unit_LastShader, (osg::Texture*)mMultiviewResolveFramebuffer->getAttachment(osg::Camera::COLOR_BUFFER0).getTexture()); } - mHDRDriver.dirty(bufferData.sceneTex->getTextureWidth(), bufferData.sceneTex->getTextureHeight()); + mLuminanceCalculator.dirty(bufferData.sceneTex->getTextureWidth(), bufferData.sceneTex->getTextureHeight()); if (Stereo::getStereo()) mRenderViewport = new osg::Viewport(0, 0, bufferData.sceneTex->getTextureWidth(), bufferData.sceneTex->getTextureHeight()); @@ -174,10 +174,10 @@ namespace MWRender {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT} }}; - (bufferData.hdr) ? mHDRDriver.enable() : mHDRDriver.disable(); + (bufferData.hdr) ? mLuminanceCalculator.enable() : mLuminanceCalculator.disable(); // A histogram based approach is superior way to calculate scene luminance. Using mipmaps is more broadly supported, so that's what we use for now. - mHDRDriver.draw(*this, renderInfo, state, ext, frameId); + mLuminanceCalculator.draw(*this, renderInfo, state, ext, frameId); auto buffer = buffers[0]; @@ -217,7 +217,7 @@ namespace MWRender node.mRootStateSet->setTextureAttribute(PostProcessor::Unit_Depth, bufferData.depthTex); if (bufferData.hdr) - node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_EyeAdaptation, mHDRDriver.getLuminanceTexture(frameId)); + node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_EyeAdaptation, mLuminanceCalculator.getLuminanceTexture(frameId)); if (bufferData.normalsTex) node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_Normals, bufferData.normalsTex); diff --git a/apps/openmw/mwrender/pingpongcanvas.hpp b/apps/openmw/mwrender/pingpongcanvas.hpp index 8df141f587..c3eedb3494 100644 --- a/apps/openmw/mwrender/pingpongcanvas.hpp +++ b/apps/openmw/mwrender/pingpongcanvas.hpp @@ -11,7 +11,7 @@ #include #include "postprocessor.hpp" -#include "hdr.hpp" +#include "luminancecalculator.hpp" namespace Shader { @@ -55,7 +55,7 @@ namespace MWRender private: void copyNewFrameData(size_t frameId) const; - mutable HDRDriver mHDRDriver; + mutable LuminanceCalculator mLuminanceCalculator; osg::ref_ptr mFallbackProgram; osg::ref_ptr mMultiviewResolveProgram; diff --git a/docs/source/reference/modding/settings/postprocessing.rst b/docs/source/reference/modding/settings/postprocessing.rst index 49b7ab7813..bd99704489 100644 --- a/docs/source/reference/modding/settings/postprocessing.rst +++ b/docs/source/reference/modding/settings/postprocessing.rst @@ -34,12 +34,12 @@ Automatically reloads a shader if the file has been changed. This is useful for .. warning:: This should be disabled for normal gameplay -hdr exposure time ------------------ +auto exposure speed +------------------- :Type: float -:Range: 0.0 to 1.0 -:Default: 0.05 +:Range: Any number > 0.0001 +:Default: 0.9 Use for eye adaptation to control speed at which average scene luminance can change from one frame to the next. Average scene luminance is used in some shader effects for features such as dynamic eye adaptation. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 2675004907..d7cdde2022 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -1204,7 +1204,7 @@ chain = live reload = false # Used for eye adaptation to control speed at which scene luminance can change from one frame to the next. No effect when HDR is not being utilized. -hdr exposure time = 0.05 +auto exposure speed = 0.9 # Transparent depth postpass. Re-renders transparent objects with alpha-clipping forced with a fixed threshold. transparent postpass = true diff --git a/files/shaders/CMakeLists.txt b/files/shaders/CMakeLists.txt index 05fe62bf0a..d688f9e7d9 100644 --- a/files/shaders/CMakeLists.txt +++ b/files/shaders/CMakeLists.txt @@ -50,7 +50,7 @@ set(SHADER_FILES sky_fragment.glsl skypasses.glsl softparticles.glsl - hdr_fragment.glsl + hdr_resolve_fragment.glsl hdr_luminance_fragment.glsl fullscreen_tri_vertex.glsl fullscreen_tri_fragment.glsl diff --git a/files/shaders/hdr_fragment.glsl b/files/shaders/hdr_fragment.glsl deleted file mode 100644 index 5490a6873d..0000000000 --- a/files/shaders/hdr_fragment.glsl +++ /dev/null @@ -1,14 +0,0 @@ -#version 120 - -varying vec2 uv; -uniform sampler2D luminanceSceneTex; -uniform sampler2D prevLuminanceSceneTex; - -void main() -{ - float prevLum = texture2D(prevLuminanceSceneTex, vec2(0.5, 0.5)).r; - - float l = texture2D(luminanceSceneTex, vec2(0.5, 0.5)).r; - float weightedAvgLum = exp2((l * @logLumRange) + @minLog); - gl_FragColor.r = prevLum + (weightedAvgLum - prevLum) * @hdrExposureTime; -} diff --git a/files/shaders/hdr_luminance_fragment.glsl b/files/shaders/hdr_luminance_fragment.glsl index f78dc41dfa..b6d527c85e 100644 --- a/files/shaders/hdr_luminance_fragment.glsl +++ b/files/shaders/hdr_luminance_fragment.glsl @@ -6,12 +6,7 @@ uniform sampler2D sceneTex; void main() { float lum = dot(texture2D(sceneTex, uv).rgb, vec3(0.2126, 0.7152, 0.0722)); - - if (lum < @epsilon) - { - gl_FragColor.r = 0.0; - return; - } + lum = max(lum, @epsilon); gl_FragColor.r = clamp((log2(lum) - @minLog) * @invLogLumRange, 0.0, 1.0); } diff --git a/files/shaders/hdr_resolve_fragment.glsl b/files/shaders/hdr_resolve_fragment.glsl new file mode 100644 index 0000000000..e04ff7cb6a --- /dev/null +++ b/files/shaders/hdr_resolve_fragment.glsl @@ -0,0 +1,16 @@ +#version 120 + +varying vec2 uv; +uniform sampler2D luminanceSceneTex; +uniform sampler2D prevLuminanceSceneTex; + +uniform float osg_DeltaFrameTime; + +void main() +{ + float prevLum = texture2D(prevLuminanceSceneTex, vec2(0.5, 0.5)).r; + float currLum = texture2D(luminanceSceneTex, vec2(0.5, 0.5)).r; + + float avgLum = exp2((currLum * @logLumRange) + @minLog); + gl_FragColor.r = prevLum + (avgLum - prevLum) * (1.0 - exp(-osg_DeltaFrameTime * @hdrExposureTime)); +} diff --git a/files/ui/advancedpage.ui b/files/ui/advancedpage.ui index 0a662fc091..3fdf80e52f 100644 --- a/files/ui/advancedpage.ui +++ b/files/ui/advancedpage.ui @@ -695,7 +695,7 @@ <html><head/><body><p>Controls how much eye adaptation can change from frame to frame. Smaller values makes for slower transitions.</p></body></html> - HDR exposure time + Auto exposure speed @@ -708,10 +708,10 @@ 3 - 0.000000000000000 + 0.010000000000000 - 1.000000000000000 + 10.000000000000000 0.001000000000000