Attempting to fix gamma by properly managing state.

pull/615/head
madsbuvi 4 years ago
parent 2923174f9a
commit b2445e4a89

@ -163,7 +163,6 @@ namespace MWVR
{
if (frameMeta->mShouldRender)
{
viewer.blit(gc);
gc->swapBuffersImplementation();
std::array<CompositionLayerProjectionView, 2> layerStack{};
layerStack[(int)Side::LEFT_SIDE].subImage = viewer.subImage(Side::LEFT_SIDE);

@ -10,6 +10,9 @@
#include "../mwrender/vismask.hpp"
#include <osgViewer/Renderer>
#include <osg/StateAttribute>
#include <osg/BufferObject>
#include <osg/VertexArrayState>
#include <components/debug/gldebug.hpp>
@ -44,6 +47,7 @@ namespace MWVR
: mViewer(viewer)
, mPreDraw(new PredrawCallback(this))
, mPostDraw(new PostdrawCallback(this))
, mFinalDraw(new FinaldrawCallback(this))
, mUpdateViewCallback(new UpdateViewCallback(this))
, mMsaaResolveTexture{}
, mMirrorTexture{ nullptr }
@ -126,13 +130,13 @@ namespace MWVR
mSwapchainConfig[i].selectedWidth = parseResolution(xConfString[i], mSwapchainConfig[i].recommendedWidth, mSwapchainConfig[i].maxWidth);
mSwapchainConfig[i].selectedHeight = parseResolution(yConfString[i], mSwapchainConfig[i].recommendedHeight, mSwapchainConfig[i].maxHeight);
mSwapchainConfig[i].selectedSamples =
mSwapchainConfig[i].selectedSamples =
std::max(1, // OpenXR requires a non-zero value
std::min(mSwapchainConfig[i].maxSamples,
std::min(mSwapchainConfig[i].maxSamples,
Settings::Manager::getInt("antialiasing", "Video")
)
);
Log(Debug::Verbose) << name << " resolution: Recommended x=" << mSwapchainConfig[i].recommendedWidth << ", y=" << mSwapchainConfig[i].recommendedHeight;
Log(Debug::Verbose) << name << " resolution: Max x=" << mSwapchainConfig[i].maxWidth << ", y=" << mSwapchainConfig[i].maxHeight;
Log(Debug::Verbose) << name << " resolution: Selected x=" << mSwapchainConfig[i].selectedWidth << ", y=" << mSwapchainConfig[i].selectedHeight;
@ -152,10 +156,10 @@ namespace MWVR
int height = std::max(mSubImages[0].height, mSubImages[1].height);
int samples = std::max(mSwapchainConfig[0].selectedSamples, mSwapchainConfig[1].selectedSamples);
mFramebuffer.reset(new VRFramebuffer(gc->getState(),width,height, samples));
mFramebuffer.reset(new VRFramebuffer(gc->getState(), width, height, samples));
mFramebuffer->createColorBuffer(gc);
mFramebuffer->createDepthBuffer(gc);
mMsaaResolveTexture.reset(new VRFramebuffer(gc->getState(),width,height,0));
mMsaaResolveTexture.reset(new VRFramebuffer(gc->getState(), width, height, 0));
mMsaaResolveTexture->createColorBuffer(gc);
mGammaResolveTexture.reset(new VRFramebuffer(gc->getState(), width, height, 0));
mGammaResolveTexture->createColorBuffer(gc);
@ -179,6 +183,7 @@ namespace MWVR
Misc::StereoView::instance().setInitialDrawCallback(new InitialDrawCallback(this));
Misc::StereoView::instance().setPredrawCallback(mPreDraw);
Misc::StereoView::instance().setPostdrawCallback(mPostDraw);
Misc::StereoView::instance().setFinaldrawCallback(mFinalDraw);
//auto cullMask = Misc::StereoView::instance().getCullMask();
auto cullMask = ~(MWRender::VisMask::Mask_UpdateVisitor | MWRender::VisMask::Mask_SimpleWater);
cullMask &= ~MWRender::VisMask::Mask_GUI;
@ -257,108 +262,106 @@ namespace MWVR
return shader;
}
static bool applyGamma(osg::GraphicsContext* gc, VRFramebuffer& target, VRFramebuffer& source)
static bool applyGamma(osg::RenderInfo& info, VRFramebuffer& target, VRFramebuffer& source)
{
// TODO: Temporary solution for applying gamma and contrast modifications
// When OpenMW implements post processing, this will be performed there instead.
// I'm just throwing things into static locals since this is temporary code that will be trashed later.
osg::State* state = info.getState();
static const char* vSource = "#version 120\n varying vec2 uv; void main(){ gl_Position = vec4(gl_Vertex.xy*2.0 - 1, 0, 1); uv = gl_Vertex.xy;}";
static const char* fSource = "#version 120\n varying vec2 uv; uniform sampler2D t; uniform float gamma; uniform float contrast;"
"void main() {"
"vec4 color1 = texture2D(t, uv);"
"vec3 rgb = color1.rgb;"
"rgb = (rgb - 0.5f) * contrast + 0.5f;"
"rgb = pow(rgb, vec3(1.0/gamma));"
"gl_FragColor = vec4(rgb, color1.a);"
"}";
static bool first = true;
static GLuint vShader = 0;
static GLuint fShader = 0;
static GLuint program = 0;
static GLuint vbo = 0;
static GLuint gammaUniform = 0;
static GLuint contrastUniform = 0;
auto* state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
static osg::ref_ptr<osg::Program> program = nullptr;
static osg::ref_ptr<osg::Shader> vShader = nullptr;
static osg::ref_ptr<osg::Shader> fShader = nullptr;
static osg::ref_ptr<osg::Uniform> gammaUniform = nullptr;
static GLint gammaUniformLocation = 0;
static osg::ref_ptr<osg::Uniform> contrastUniform = nullptr;
static GLint contrastUniformLocation = 0;
osg::Viewport* viewport = nullptr;
static osg::ref_ptr<osg::StateSet> stateset = nullptr;
static osg::ref_ptr<osg::Geometry> geometry = nullptr;
static osg::ref_ptr<osg::Texture2D> texture = nullptr;
static osg::ref_ptr<osg::Texture::TextureObject> textureObject = nullptr;
static std::vector<osg::Vec4> vertices =
{
{0, 0, 0, 0},
{1, 0, 0, 0},
{1, 1, 0, 0},
{0, 0, 0, 0},
{1, 1, 0, 0},
{0, 1, 0, 0}
};
static osg::ref_ptr<osg::Vec4Array> vertexArray = new osg::Vec4Array(vertices.begin(), vertices.end());
if (first)
{
first = false;
const char* vSource = "#version 120\n varying vec2 uv; void main(){ gl_Position = vec4(gl_Vertex.xy*2.0 - 1, 0, 1); uv = gl_Vertex.xy;}";
const char* fSource = "#version 120\n varying vec2 uv; uniform sampler2D t; uniform float gamma; uniform float contrast;"
"void main() {"
"vec4 color1 = texture2D(t, uv);"
"vec3 rgb = color1.rgb;"
"rgb = (rgb - 0.5f) * contrast + 0.5f;"
"rgb = pow(rgb, vec3(1.0/gamma));"
"gl_FragColor = vec4(rgb, color1.a);"
"}";
vShader = createShader(gl, vSource, GL_VERTEX_SHADER);
fShader = createShader(gl, fSource, GL_FRAGMENT_SHADER);
program = gl->glCreateProgram();
gl->glAttachShader(program, vShader);
gl->glAttachShader(program, fShader);
gl->glLinkProgram(program);
GLint isCompiled = 0;
gl->glGetProgramiv(program, GL_LINK_STATUS, &isCompiled);
if (isCompiled == GL_FALSE)
{
GLint maxLength = 0;
gl->glGetProgramInfoLog(program, 0, &maxLength, nullptr);
std::vector<GLchar> infoLog(maxLength);
gl->glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
gl->glDeleteProgram(program);
program = 0;
Log(Debug::Error) << "Failed to link program: " << infoLog.data();
}
geometry = new osg::Geometry();
geometry->setVertexArray(vertexArray);
geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES, 0, 6));
geometry->setUseDisplayList(false);
stateset = geometry->getOrCreateStateSet();
vShader = new osg::Shader(osg::Shader::Type::VERTEX, vSource);
fShader = new osg::Shader(osg::Shader::Type::FRAGMENT, fSource);
program = new osg::Program();
program->addShader(vShader);
program->addShader(fShader);
program->compileGLObjects(*state);
stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
texture = new osg::Texture2D();
texture->setName("diffuseMap");
textureObject = new osg::Texture::TextureObject(texture, source.colorBuffer(), GL_TEXTURE_2D);
texture->setTextureObject(state->getContextID(), textureObject);
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::PROTECTED);
stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::PROTECTED);
gammaUniform = new osg::Uniform("gamma", Settings::Manager::getFloat("gamma", "Video"));
contrastUniform = new osg::Uniform("contrast", Settings::Manager::getFloat("contrast", "Video"));
stateset->addUniform(gammaUniform);
stateset->addUniform(contrastUniform);
geometry->compileGLObjects(info);
if (program)
{
GLfloat vertices[] =
{
0, 0, 0, 0,
1, 0, 0, 0,
1, 1, 0, 0,
0, 0, 0, 0,
1, 1, 0, 0,
0, 1, 0, 0
};
gl->glGenBuffers(1, &vbo);
gl->glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo);
gl->glBufferData(GL_ARRAY_BUFFER_ARB, sizeof(vertices), vertices, GL_STATIC_DRAW);
gammaUniform = gl->glGetUniformLocation(program, "gamma");
contrastUniform = gl->glGetUniformLocation(program, "contrast");
}
first = false;
}
target.bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
target.bindFramebuffer(state->getGraphicsContext(), GL_FRAMEBUFFER_EXT);
if (program > 0)
{
gl->glUseProgram(program);
gl->glBindVertexArray(0);
glViewport(0, 0, target.width(), target.height());
// OSG does not pop statesets until after the final draw callback. Unrelated statesets may therefore still be on the stack at this point.
// Pop these to avoid inheriting arbitrary state from these. They will not be used more in this frame.
state->popAllStateSets();
state->apply();
gl->glUniform1f(gammaUniform, Settings::Manager::getFloat("gamma", "Video"));
gl->glUniform1f(contrastUniform, Settings::Manager::getFloat("contrast", "Video"));
gammaUniform->set(Settings::Manager::getFloat("gamma", "Video"));
contrastUniform->set(Settings::Manager::getFloat("contrast", "Video"));
state->pushStateSet(stateset);
state->apply();
gl->glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo);
glVertexPointer(4, GL_FLOAT, 0, 0);
gl->glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, source.colorBuffer());
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glDisable(GL_BLEND);
if(!viewport)
viewport = new osg::Viewport(0, 0, target.width(), target.height());
viewport->setViewport(0, 0, target.width(), target.height());
viewport->apply(*state);
glDrawArrays(GL_TRIANGLES, 0, 6);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
geometry->draw(info);
gl->glUseProgram(0);
glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_BLEND);
gl->glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
state->popStateSet();
return true;
}
return false;
}
void VRViewer::blit(osg::GraphicsContext* gc)
void VRViewer::blit(osg::RenderInfo& info)
{
if (mMirrorTextureShouldBeCleanedUp)
{
@ -368,21 +371,23 @@ namespace MWVR
if (!mMirrorTextureEnabled)
return;
auto* state = info.getState();
auto* gc = state->getGraphicsContext();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
auto* traits = SDLUtil::GraphicsWindowSDL2::findContext(*mViewer)->getTraits();
int screenWidth = traits->width;
int screenHeight = traits->height;
if (!mMirrorTexture)
{
;
mMirrorTexture.reset(new VRFramebuffer(gc->getState(),
mMirrorTexture.reset(new VRFramebuffer(state,
screenWidth,
screenHeight,
0));
mMirrorTexture->createColorBuffer(gc);
}
auto* state = gc->getState();
auto* gl = osg::GLExtensions::Get(state->getContextID(), false);
int mirrorWidth = screenWidth / mMirrorTextureViews.size();
@ -393,7 +398,7 @@ namespace MWVR
mFramebuffer->blit(gc, 0, 0, mFramebuffer->width(), mFramebuffer->height(), 0, 0, mMsaaResolveTexture->width(), mMsaaResolveTexture->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
bool shouldDoGamma = Settings::Manager::getBool("gamma postprocessing", "VR Debug");
if (!shouldDoGamma || !applyGamma(gc, *mGammaResolveTexture, *mMsaaResolveTexture))
if (!shouldDoGamma || !applyGamma(info, *mGammaResolveTexture, *mMsaaResolveTexture))
{
mGammaResolveTexture->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
mMsaaResolveTexture->blit(gc, 0, 0, mMsaaResolveTexture->width(), mMsaaResolveTexture->height(), 0, 0, mGammaResolveTexture->width(), mGammaResolveTexture->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
@ -484,6 +489,20 @@ namespace MWVR
}
}
void VRViewer::finalDrawCallback(osg::RenderInfo& info)
{
auto* session = Environment::get().getSession();
auto* frameMeta = session->getFrame(VRSession::FramePhase::Draw).get();
if (frameMeta->mShouldSyncFrameLoop)
{
if (frameMeta->mShouldRender)
{
blit(info);
}
}
}
void VRViewer::swapBuffersCallback(osg::GraphicsContext* gc)
{
auto* session = Environment::get().getSession();
@ -508,4 +527,10 @@ namespace MWVR
{
mViewer->updateView(left, right);
}
void VRViewer::FinaldrawCallback::operator()(osg::RenderInfo& info, Misc::StereoView::StereoDrawCallback::View view) const
{
if (view != Misc::StereoView::StereoDrawCallback::View::Left)
mViewer->finalDrawCallback(info);
}
}

@ -90,6 +90,20 @@ namespace MWVR
VRViewer* mViewer;
};
class FinaldrawCallback : public Misc::StereoView::StereoDrawCallback
{
public:
FinaldrawCallback(VRViewer* viewer)
: mViewer(viewer)
{}
void operator()(osg::RenderInfo& info, Misc::StereoView::StereoDrawCallback::View view) const override;
private:
VRViewer* mViewer;
};
static const std::array<const char*, 2> sViewNames;
enum class MirrorTextureEye
{
@ -108,7 +122,8 @@ namespace MWVR
void initialDrawCallback(osg::RenderInfo& info);
void preDrawCallback(osg::RenderInfo& info);
void postDrawCallback(osg::RenderInfo& info);
void blit(osg::GraphicsContext* gc);
void finalDrawCallback(osg::RenderInfo& info);
void blit(osg::RenderInfo& gc);
void configureXR(osg::GraphicsContext* gc);
void configureCallbacks();
void setupMirrorTexture();
@ -128,6 +143,7 @@ namespace MWVR
osg::ref_ptr<osgViewer::Viewer> mViewer = nullptr;
osg::ref_ptr<PredrawCallback> mPreDraw{ nullptr };
osg::ref_ptr<PostdrawCallback> mPostDraw{ nullptr };
osg::ref_ptr<FinaldrawCallback> mFinalDraw{ nullptr };
std::shared_ptr<UpdateViewCallback> mUpdateViewCallback{ nullptr };
bool mRenderingReady{ false };

@ -461,11 +461,13 @@ namespace Misc
auto initialDrawCB = mInitialDrawCallback;
auto predrawCB = mPreDrawCallback;
auto postDrawCB = mPostDrawCallback;
auto finalDrawCB = mFinalDrawCallback;
setCullCallback(nullptr);
setInitialDrawCallback(nullptr);
setPostdrawCallback(nullptr);
setPredrawCallback(nullptr);
setFinaldrawCallback(nullptr);
disableStereo();
mTechnique = technique;
@ -475,6 +477,7 @@ namespace Misc
setInitialDrawCallback(initialDrawCB);
setPostdrawCallback(predrawCB);
setPredrawCallback(postDrawCB);
setFinaldrawCallback(finalDrawCB);
}
void StereoView::update()
@ -705,6 +708,12 @@ namespace Misc
mMainCamera->setPostDrawCallback(cb);
}
void StereoView::setFinaldrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb)
{
mFinalDrawCallback = cb;
mMainCamera->setFinalDrawCallback(cb);
}
void StereoView::setCullCallback(osg::ref_ptr<osg::NodeCallback> cb)
{
mMainCamera->setCullCallback(cb);
@ -746,4 +755,47 @@ namespace Misc
{
return mRightCamera->getViewMatrix();
}
void StereoView::StereoDrawCallback::operator()(osg::RenderInfo& info) const
{
// OSG does not give any information about stereo in these callbacks so i have to infer this myself.
// And hopefully OSG won't change this behaviour.
View view = View::Both;
auto camera = info.getCurrentCamera();
auto viewport = camera->getViewport();
// Find the current scene view.
osg::GraphicsOperation* graphicsOperation = info.getCurrentCamera()->getRenderer();
osgViewer::Renderer* renderer = dynamic_cast<osgViewer::Renderer*>(graphicsOperation);
for (int i = 0; i < 2; i++) // OSG alternates between two sceneviews.
{
auto* sceneView = renderer->getSceneView(i);
// The render info argument is a member of scene view, allowing me to identify it.
if (&sceneView->getRenderInfo() == &info)
{
// Now i can simply examine the viewport.
auto activeViewport = static_cast<osg::Viewport*>(sceneView->getLocalStateSet()->getAttribute(osg::StateAttribute::Type::VIEWPORT, 0));
if (activeViewport)
{
if (activeViewport->width() == viewport->width() && activeViewport->height() == viewport->height())
view = View::Both;
else if (activeViewport->x() == viewport->x() && activeViewport->y() == viewport->y())
view = View::Left;
else
view = View::Right;
}
else
{
// OSG always sets a viewport in the local stateset if osg's stereo is enabled.
// If it isn't, assume both.
view = View::Both;
}
break;
}
}
operator()(info, view);
}
}

@ -89,6 +89,29 @@ namespace Misc
GeometryShader_IndexedViewports, //!< Frustum camera culls and draws stereo into indexed viewports using an automatically generated geometry shader.
};
//! A draw callback that adds stereo information to the operator.
//! The stereo information is an enum describing which of the views the callback concerns.
//! With some stereo methods, there is only one callback, in which case the enum will be 'Both'.
//!
//! A typical use case of this callback is to prevent firing callbacks twice and correctly identifying the last/first callback.
struct StereoDrawCallback : public osg::Camera::DrawCallback
{
public:
enum class View
{
Both, Left, Right
};
public:
StereoDrawCallback()
{}
void operator()(osg::RenderInfo& info) const override;
virtual void operator()(osg::RenderInfo& info, View view) const = 0;
private:
};
static StereoView& instance();
//! Adds two cameras in stereo to the mainCamera.
@ -121,6 +144,9 @@ namespace Misc
//! Set the postdraw callback on the appropriate camera object
void setPostdrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb);
//! Set the final draw callback on the appropriate camera object
void setFinaldrawCallback(osg::ref_ptr<osg::Camera::DrawCallback> cb);
//! Set the cull callback on the appropriate camera object
void setCullCallback(osg::ref_ptr<osg::NodeCallback> cb);
@ -180,6 +206,7 @@ namespace Misc
osg::ref_ptr<osg::Camera::DrawCallback> mInitialDrawCallback{ nullptr };
osg::ref_ptr<osg::Camera::DrawCallback> mPreDrawCallback{ nullptr };
osg::ref_ptr<osg::Camera::DrawCallback> mPostDrawCallback{ nullptr };
osg::ref_ptr<osg::Camera::DrawCallback> mFinalDrawCallback{ nullptr };
};
//! Overrides all stereo-related states/uniforms to disable stereo for the scene rendered by camera

Loading…
Cancel
Save