mirror of https://github.com/OpenMW/openmw.git
Merge branch 'hdr_quick' into 'master'
Fix auto exposure See merge request OpenMW/openmw!2174check_span
commit
26bd907b0b
@ -1,123 +0,0 @@
|
||||
#include "hdr.hpp"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
|
||||
#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<osg::Texture2D> HDRDriver::getLuminanceTexture(size_t frameId) const
|
||||
{
|
||||
return mBuffers[frameId].finalTexture;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#ifndef OPENMW_MWRENDER_HDR_H
|
||||
#define OPENMW_MWRENDER_HDR_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <osg/FrameBufferObject>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Program>
|
||||
|
||||
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<osg::Texture2D> getLuminanceTexture(size_t frameId) const;
|
||||
|
||||
private:
|
||||
|
||||
void compile();
|
||||
|
||||
struct HDRContainer
|
||||
{
|
||||
osg::ref_ptr<osg::FrameBufferObject> fullscreenFbo;
|
||||
osg::ref_ptr<osg::FrameBufferObject> mipmapFbo;
|
||||
osg::ref_ptr<osg::FrameBufferObject> finalFbo;
|
||||
osg::ref_ptr<osg::Texture2D> texture;
|
||||
osg::ref_ptr<osg::Texture2D> finalTexture;
|
||||
osg::ref_ptr<osg::StateSet> fullscreenStateset;
|
||||
osg::ref_ptr<osg::StateSet> mipmapStateset;
|
||||
};
|
||||
|
||||
std::array<HDRContainer, 2> mBuffers;
|
||||
osg::ref_ptr<osg::Program> mLuminanceProgram;
|
||||
osg::ref_ptr<osg::Program> mProgram;
|
||||
|
||||
bool mCompiled;
|
||||
bool mEnabled;
|
||||
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,134 @@
|
||||
#include "luminancecalculator.hpp"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
|
||||
#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<osg::Texture2D> LuminanceCalculator::getLuminanceTexture(size_t frameId) const
|
||||
{
|
||||
return mBuffers[frameId].luminanceTex;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
#ifndef OPENMW_MWRENDER_LUMINANCECALCULATOR_H
|
||||
#define OPENMW_MWRENDER_LUMINANCECALCULATOR_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <osg/FrameBufferObject>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Program>
|
||||
|
||||
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<osg::Texture2D> getLuminanceTexture(size_t frameId) const;
|
||||
|
||||
private:
|
||||
|
||||
void compile();
|
||||
|
||||
struct Container
|
||||
{
|
||||
osg::ref_ptr<osg::FrameBufferObject> sceneLumFbo;
|
||||
osg::ref_ptr<osg::FrameBufferObject> resolveSceneLumFbo;
|
||||
osg::ref_ptr<osg::FrameBufferObject> resolveFbo;
|
||||
osg::ref_ptr<osg::FrameBufferObject> luminanceProxyFbo;
|
||||
osg::ref_ptr<osg::Texture2D> mipmappedSceneLuminanceTex;
|
||||
osg::ref_ptr<osg::Texture2D> luminanceTex;
|
||||
osg::ref_ptr<osg::Texture2D> luminanceProxyTex;
|
||||
osg::ref_ptr<osg::StateSet> sceneLumSS;
|
||||
osg::ref_ptr<osg::StateSet> resolveSS;
|
||||
};
|
||||
|
||||
std::array<Container, 2> mBuffers;
|
||||
osg::ref_ptr<osg::Program> mLuminanceProgram;
|
||||
osg::ref_ptr<osg::Program> mResolveProgram;
|
||||
|
||||
bool mCompiled = false;
|
||||
bool mEnabled = false;
|
||||
|
||||
int mWidth = 1;
|
||||
int mHeight = 1;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -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;
|
||||
}
|
@ -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));
|
||||
}
|
Loading…
Reference in New Issue