Merge branch 'fbos_for_all' into 'master'

[renderer] render scene exclusively to fbo

Closes #7364 and #7354

See merge request OpenMW/openmw!3243
macos_ci_fix
uramer 1 year ago
commit 6f4e7ed5dc

@ -328,46 +328,37 @@ namespace MWRender
{ {
osg::State* state = renderInfo.getState(); osg::State* state = renderInfo.getState();
PostProcessor* postProcessor = dynamic_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData()); PostProcessor* postProcessor = static_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData());
state->applyAttribute(mDepth); state->applyAttribute(mDepth);
unsigned int frameId = state->getFrameStamp()->getFrameNumber() % 2; unsigned int frameId = state->getFrameStamp()->getFrameNumber() % 2;
if (postProcessor && postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId)) postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId)->apply(*state);
if (mPassNormals)
{ {
postProcessor->getFbo(PostProcessor::FBO_FirstPerson, frameId)->apply(*state); state->get<osg::GLExtensions>()->glColorMaski(1, true, true, true, true);
if (mPassNormals) state->haveAppliedAttribute(osg::StateAttribute::COLORMASK);
{ }
state->get<osg::GLExtensions>()->glColorMaski(1, true, true, true, true); glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
state->haveAppliedAttribute(osg::StateAttribute::COLORMASK); // color accumulation pass
} bin->drawImplementation(renderInfo, previous);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// color accumulation pass
bin->drawImplementation(renderInfo, previous);
auto primaryFBO = postProcessor->getPrimaryFbo(frameId); auto primaryFBO = postProcessor->getPrimaryFbo(frameId);
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)) if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)->apply(*state); postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)->apply(*state);
else else
primaryFBO->apply(*state); primaryFBO->apply(*state);
// depth accumulation pass // depth accumulation pass
osg::ref_ptr<osg::StateSet> restore = bin->getStateSet(); osg::ref_ptr<osg::StateSet> restore = bin->getStateSet();
bin->setStateSet(mStateSet); bin->setStateSet(mStateSet);
bin->drawImplementation(renderInfo, previous); bin->drawImplementation(renderInfo, previous);
bin->setStateSet(restore); bin->setStateSet(restore);
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)) if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
primaryFBO->apply(*state); primaryFBO->apply(*state);
}
else
{
// fallback to standard depth clear when we are not rendering our main scene via an intermediate FBO
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
bin->drawImplementation(renderInfo, previous);
}
state->checkGLErrors("after DepthClearCallback::drawImplementation"); state->checkGLErrors("after DepthClearCallback::drawImplementation");
} }

@ -43,19 +43,16 @@ namespace MWRender
mMultiviewResolveStateSet->addUniform(new osg::Uniform("lastShader", 0)); mMultiviewResolveStateSet->addUniform(new osg::Uniform("lastShader", 0));
} }
void PingPongCanvas::setCurrentFrameData(size_t frameId, fx::DispatchArray&& data) void PingPongCanvas::setPasses(fx::DispatchArray&& passes)
{ {
mBufferData[frameId].data = std::move(data); mPasses = std::move(passes);
} }
void PingPongCanvas::setMask(size_t frameId, bool underwater, bool exterior) void PingPongCanvas::setMask(bool underwater, bool exterior)
{ {
mBufferData[frameId].mask = 0; mMask = 0;
mMask |= underwater ? fx::Technique::Flag_Disable_Underwater : fx::Technique::Flag_Disable_Abovewater;
mBufferData[frameId].mask mMask |= exterior ? fx::Technique::Flag_Disable_Exteriors : fx::Technique::Flag_Disable_Interiors;
|= underwater ? fx::Technique::Flag_Disable_Underwater : fx::Technique::Flag_Disable_Abovewater;
mBufferData[frameId].mask
|= exterior ? fx::Technique::Flag_Disable_Exteriors : fx::Technique::Flag_Disable_Interiors;
} }
void PingPongCanvas::drawGeometry(osg::RenderInfo& renderInfo) const void PingPongCanvas::drawGeometry(osg::RenderInfo& renderInfo) const
@ -77,19 +74,15 @@ namespace MWRender
size_t frameId = state.getFrameStamp()->getFrameNumber() % 2; size_t frameId = state.getFrameStamp()->getFrameNumber() % 2;
auto& bufferData = mBufferData[frameId];
const auto& data = bufferData.data;
std::vector<size_t> filtered; std::vector<size_t> filtered;
filtered.reserve(data.size()); filtered.reserve(mPasses.size());
for (size_t i = 0; i < data.size(); ++i) for (size_t i = 0; i < mPasses.size(); ++i)
{ {
const auto& node = data[i]; const auto& node = mPasses[i];
if (bufferData.mask & node.mFlags) if (mMask & node.mFlags)
continue; continue;
filtered.push_back(i); filtered.push_back(i);
@ -97,7 +90,7 @@ namespace MWRender
auto* resolveViewport = state.getCurrentViewport(); auto* resolveViewport = state.getCurrentViewport();
if (filtered.empty() || !bufferData.postprocessing) if (filtered.empty() || !mPostprocessing)
{ {
state.pushStateSet(mFallbackStateSet); state.pushStateSet(mFallbackStateSet);
state.apply(); state.apply();
@ -108,7 +101,7 @@ namespace MWRender
state.apply(); state.apply();
} }
state.applyTextureAttribute(0, bufferData.sceneTex); state.applyTextureAttribute(0, mTextureScene);
resolveViewport->apply(state); resolveViewport->apply(state);
drawGeometry(renderInfo); drawGeometry(renderInfo);
@ -124,13 +117,12 @@ namespace MWRender
const unsigned int handle = mFbos[0] ? mFbos[0]->getHandle(state.getContextID()) : 0; const unsigned int handle = mFbos[0] ? mFbos[0]->getHandle(state.getContextID()) : 0;
if (handle == 0 || bufferData.dirty) if (handle == 0 || mDirty)
{ {
for (auto& fbo : mFbos) for (auto& fbo : mFbos)
{ {
fbo = new osg::FrameBufferObject; fbo = new osg::FrameBufferObject;
attachCloneOfTemplate( attachCloneOfTemplate(fbo, osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, mTextureScene);
fbo, osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, bufferData.sceneTexLDR);
fbo->apply(state); fbo->apply(state);
glClearColor(0.5, 0.5, 0.5, 1); glClearColor(0.5, 0.5, 0.5, 1);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
@ -140,7 +132,7 @@ namespace MWRender
{ {
mMultiviewResolveFramebuffer = new osg::FrameBufferObject(); mMultiviewResolveFramebuffer = new osg::FrameBufferObject();
attachCloneOfTemplate(mMultiviewResolveFramebuffer, attachCloneOfTemplate(mMultiviewResolveFramebuffer,
osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, bufferData.sceneTexLDR); osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0, mTextureScene);
mMultiviewResolveFramebuffer->apply(state); mMultiviewResolveFramebuffer->apply(state);
glClearColor(0.5, 0.5, 0.5, 1); glClearColor(0.5, 0.5, 0.5, 1);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
@ -150,15 +142,15 @@ namespace MWRender
.getTexture()); .getTexture());
} }
mLuminanceCalculator.dirty(bufferData.sceneTex->getTextureWidth(), bufferData.sceneTex->getTextureHeight()); mLuminanceCalculator.dirty(mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
if (Stereo::getStereo()) if (Stereo::getStereo())
mRenderViewport = new osg::Viewport( mRenderViewport
0, 0, bufferData.sceneTex->getTextureWidth(), bufferData.sceneTex->getTextureHeight()); = new osg::Viewport(0, 0, mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
else else
mRenderViewport = nullptr; mRenderViewport = nullptr;
bufferData.dirty = false; mDirty = false;
} }
constexpr std::array<std::array<int, 2>, 3> buffers constexpr std::array<std::array<int, 2>, 3> buffers
@ -166,7 +158,7 @@ namespace MWRender
{ GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT2_EXT }, { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT2_EXT },
{ GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT } } }; { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT } } };
(bufferData.hdr) ? mLuminanceCalculator.enable() : mLuminanceCalculator.disable(); (mAvgLum) ? mLuminanceCalculator.enable() : mLuminanceCalculator.disable();
// A histogram based approach is superior way to calculate scene luminance. Using mipmaps is more broadly // 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. // supported, so that's what we use for now.
@ -181,8 +173,7 @@ namespace MWRender
const unsigned int cid = state.getContextID(); const unsigned int cid = state.getContextID();
const osg::ref_ptr<osg::FrameBufferObject>& destinationFbo const osg::ref_ptr<osg::FrameBufferObject>& destinationFbo = mDestinationFBO ? mDestinationFBO : nullptr;
= bufferData.destination ? bufferData.destination : nullptr;
unsigned int destinationHandle = destinationFbo ? destinationFbo->getHandle(cid) : 0; unsigned int destinationHandle = destinationFbo ? destinationFbo->getHandle(cid) : 0;
auto bindDestinationFbo = [&]() { auto bindDestinationFbo = [&]() {
@ -206,17 +197,16 @@ namespace MWRender
for (const size_t& index : filtered) for (const size_t& index : filtered)
{ {
const auto& node = data[index]; const auto& node = mPasses[index];
node.mRootStateSet->setTextureAttribute(PostProcessor::Unit_Depth, bufferData.depthTex); node.mRootStateSet->setTextureAttribute(PostProcessor::Unit_Depth, mTextureDepth);
if (bufferData.hdr) if (mAvgLum)
node.mRootStateSet->setTextureAttribute( node.mRootStateSet->setTextureAttribute(
PostProcessor::TextureUnits::Unit_EyeAdaptation, mLuminanceCalculator.getLuminanceTexture(frameId)); PostProcessor::TextureUnits::Unit_EyeAdaptation, mLuminanceCalculator.getLuminanceTexture(frameId));
if (bufferData.normalsTex) if (mTextureNormals)
node.mRootStateSet->setTextureAttribute( node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_Normals, mTextureNormals);
PostProcessor::TextureUnits::Unit_Normals, bufferData.normalsTex);
state.pushStateSet(node.mRootStateSet); state.pushStateSet(node.mRootStateSet);
state.apply(); state.apply();
@ -231,7 +221,7 @@ namespace MWRender
// VR-TODO: This won't actually work for tex2darrays // VR-TODO: This won't actually work for tex2darrays
if (lastShader == 0) if (lastShader == 0)
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastShader, bufferData.sceneTex); pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastShader, mTextureScene);
else else
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastShader, pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastShader,
(osg::Texture*)mFbos[lastShader - GL_COLOR_ATTACHMENT0_EXT] (osg::Texture*)mFbos[lastShader - GL_COLOR_ATTACHMENT0_EXT]
@ -239,7 +229,7 @@ namespace MWRender
.getTexture()); .getTexture());
if (lastDraw == 0) if (lastDraw == 0)
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastPass, bufferData.sceneTex); pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastPass, mTextureScene);
else else
pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastPass, pass.mStateSet->setTextureAttribute(PostProcessor::Unit_LastPass,
(osg::Texture*)mFbos[lastDraw - GL_COLOR_ATTACHMENT0_EXT] (osg::Texture*)mFbos[lastDraw - GL_COLOR_ATTACHMENT0_EXT]
@ -260,7 +250,6 @@ namespace MWRender
} }
lastApplied = pass.mRenderTarget->getHandle(state.getContextID()); lastApplied = pass.mRenderTarget->getHandle(state.getContextID());
;
} }
else if (pass.mResolve && index == filtered.back()) else if (pass.mResolve && index == filtered.back())
{ {

@ -24,76 +24,52 @@ namespace MWRender
public: public:
PingPongCanvas(Shader::ShaderManager& shaderManager); PingPongCanvas(Shader::ShaderManager& shaderManager);
void drawImplementation(osg::RenderInfo& renderInfo) const override; void drawGeometry(osg::RenderInfo& renderInfo) const;
void dirty(size_t frameId) { mBufferData[frameId].dirty = true; } void drawImplementation(osg::RenderInfo& renderInfo) const override;
const fx::DispatchArray& getCurrentFrameData(size_t frame) { return mBufferData[frame % 2].data; } void dirty() { mDirty = true; }
// Sets current frame pass data and stores copy of dispatch array to apply to next frame data const fx::DispatchArray& getPasses() { return mPasses; }
void setCurrentFrameData(size_t frameId, fx::DispatchArray&& data);
void setMask(size_t frameId, bool underwater, bool exterior); void setPasses(fx::DispatchArray&& passes);
void setSceneTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex) { mBufferData[frameId].sceneTex = tex; } void setMask(bool underwater, bool exterior);
void setLDRSceneTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex) void setTextureScene(osg::ref_ptr<osg::Texture> tex) { mTextureScene = tex; }
{
mBufferData[frameId].sceneTexLDR = tex;
}
void setDepthTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex) { mBufferData[frameId].depthTex = tex; } void setTextureDepth(osg::ref_ptr<osg::Texture> tex) { mTextureDepth = tex; }
void setNormalsTexture(size_t frameId, osg::ref_ptr<osg::Texture> tex) void setTextureNormals(osg::ref_ptr<osg::Texture> tex) { mTextureNormals = tex; }
{
mBufferData[frameId].normalsTex = tex;
}
void setHDR(size_t frameId, bool hdr) { mBufferData[frameId].hdr = hdr; } void setCalculateAvgLum(bool enabled) { mAvgLum = enabled; }
void setPostProcessing(size_t frameId, bool postprocessing) void setPostProcessing(bool enabled) { mPostprocessing = enabled; }
{
mBufferData[frameId].postprocessing = postprocessing;
}
const osg::ref_ptr<osg::Texture>& getSceneTexture(size_t frameId) const const osg::ref_ptr<osg::Texture>& getSceneTexture(size_t frameId) const { return mTextureScene; }
{
return mBufferData[frameId].sceneTex;
}
void drawGeometry(osg::RenderInfo& renderInfo) const;
private: private:
void copyNewFrameData(size_t frameId) const; bool mAvgLum = false;
bool mPostprocessing = false;
mutable LuminanceCalculator mLuminanceCalculator; fx::DispatchArray mPasses;
fx::FlagsType mMask;
osg::ref_ptr<osg::Program> mFallbackProgram; osg::ref_ptr<osg::Program> mFallbackProgram;
osg::ref_ptr<osg::Program> mMultiviewResolveProgram; osg::ref_ptr<osg::Program> mMultiviewResolveProgram;
osg::ref_ptr<osg::StateSet> mFallbackStateSet; osg::ref_ptr<osg::StateSet> mFallbackStateSet;
osg::ref_ptr<osg::StateSet> mMultiviewResolveStateSet; osg::ref_ptr<osg::StateSet> mMultiviewResolveStateSet;
mutable osg::ref_ptr<osg::FrameBufferObject> mMultiviewResolveFramebuffer;
struct BufferData osg::ref_ptr<osg::Texture> mTextureScene;
{ osg::ref_ptr<osg::Texture> mTextureDepth;
bool dirty = false; osg::ref_ptr<osg::Texture> mTextureNormals;
bool hdr = false;
bool postprocessing = true;
fx::DispatchArray data; mutable bool mDirty = false;
fx::FlagsType mask;
osg::ref_ptr<osg::FrameBufferObject> destination;
osg::ref_ptr<osg::Texture> sceneTex;
osg::ref_ptr<osg::Texture> depthTex;
osg::ref_ptr<osg::Texture> sceneTexLDR;
osg::ref_ptr<osg::Texture> normalsTex;
};
mutable std::array<BufferData, 2> mBufferData;
mutable std::array<osg::ref_ptr<osg::FrameBufferObject>, 3> mFbos;
mutable osg::ref_ptr<osg::Viewport> mRenderViewport; mutable osg::ref_ptr<osg::Viewport> mRenderViewport;
mutable osg::ref_ptr<osg::FrameBufferObject> mMultiviewResolveFramebuffer;
mutable osg::ref_ptr<osg::FrameBufferObject> mDestinationFBO;
mutable std::array<osg::ref_ptr<osg::FrameBufferObject>, 3> mFbos;
mutable LuminanceCalculator mLuminanceCalculator;
}; };
} }

@ -21,7 +21,7 @@ namespace MWRender
if (Stereo::getStereo()) if (Stereo::getStereo())
{ {
mViewportStateset = new osg::StateSet(); mViewportStateset = new osg::StateSet();
mViewport = new osg::Viewport(0, 0, pp->renderWidth(), pp->renderHeight()); mViewport = new osg::Viewport;
mViewportStateset->setAttribute(mViewport); mViewportStateset->setAttribute(mViewport);
} }
} }
@ -37,41 +37,31 @@ namespace MWRender
size_t frame = cv->getTraversalNumber(); size_t frame = cv->getTraversalNumber();
size_t frameId = frame % 2; size_t frameId = frame % 2;
MWRender::PostProcessor* postProcessor
= dynamic_cast<MWRender::PostProcessor*>(cv->getCurrentCamera()->getUserData());
if (!postProcessor)
throw std::runtime_error("PingPongCull: failed to get a PostProcessor!");
if (Stereo::getStereo()) if (Stereo::getStereo())
{ {
auto& sm = Stereo::Manager::instance(); auto& sm = Stereo::Manager::instance();
auto view = sm.getEye(cv); auto view = sm.getEye(cv);
int index = view == Stereo::Eye::Right ? 1 : 0; int index = view == Stereo::Eye::Right ? 1 : 0;
auto projectionMatrix = sm.computeEyeProjection(index, true); auto projectionMatrix = sm.computeEyeProjection(index, true);
postProcessor->getStateUpdater()->setProjectionMatrix(projectionMatrix); mPostProcessor->getStateUpdater()->setProjectionMatrix(projectionMatrix);
} }
postProcessor->getStateUpdater()->setViewMatrix(cv->getCurrentCamera()->getViewMatrix()); mPostProcessor->getStateUpdater()->setViewMatrix(cv->getCurrentCamera()->getViewMatrix());
postProcessor->getStateUpdater()->setPrevViewMatrix(mLastViewMatrix[0]); mPostProcessor->getStateUpdater()->setPrevViewMatrix(mLastViewMatrix[0]);
mLastViewMatrix[0] = cv->getCurrentCamera()->getViewMatrix(); mLastViewMatrix[0] = cv->getCurrentCamera()->getViewMatrix();
postProcessor->getStateUpdater()->setEyePos(cv->getEyePoint()); mPostProcessor->getStateUpdater()->setEyePos(cv->getEyePoint());
postProcessor->getStateUpdater()->setEyeVec(cv->getLookVectorLocal()); mPostProcessor->getStateUpdater()->setEyeVec(cv->getLookVectorLocal());
if (!postProcessor->getFbo(PostProcessor::FBO_Primary, frameId)) if (!mPostProcessor->getFbo(PostProcessor::FBO_Multisample, frameId))
{
renderStage->setMultisampleResolveFramebufferObject(nullptr);
renderStage->setFrameBufferObject(nullptr);
}
else if (!postProcessor->getFbo(PostProcessor::FBO_Multisample, frameId))
{ {
renderStage->setFrameBufferObject(postProcessor->getFbo(PostProcessor::FBO_Primary, frameId)); renderStage->setFrameBufferObject(mPostProcessor->getFbo(PostProcessor::FBO_Primary, frameId));
} }
else else
{ {
renderStage->setMultisampleResolveFramebufferObject( renderStage->setMultisampleResolveFramebufferObject(
postProcessor->getFbo(PostProcessor::FBO_Primary, frameId)); mPostProcessor->getFbo(PostProcessor::FBO_Primary, frameId));
renderStage->setFrameBufferObject(postProcessor->getFbo(PostProcessor::FBO_Multisample, frameId)); renderStage->setFrameBufferObject(mPostProcessor->getFbo(PostProcessor::FBO_Multisample, frameId));
// The MultiView patch has a bug where it does not update resolve layers if the resolve framebuffer is // The MultiView patch has a bug where it does not update resolve layers if the resolve framebuffer is
// changed. So we do blit manually in this case // changed. So we do blit manually in this case

@ -110,30 +110,45 @@ namespace MWRender
PostProcessor::PostProcessor( PostProcessor::PostProcessor(
RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode, const VFS::Manager* vfs) RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode, const VFS::Manager* vfs)
: osg::Group() : osg::Group()
, mEnableLiveReload(false)
, mRootNode(rootNode) , mRootNode(rootNode)
, mSamples(Settings::video().mAntialiasing) , mHUDCamera(new osg::Camera)
, mDirty(false)
, mDirtyFrameId(0)
, mRendering(rendering) , mRendering(rendering)
, mViewer(viewer) , mViewer(viewer)
, mVFS(vfs) , mVFS(vfs)
, mTriggerShaderReload(false)
, mReload(false)
, mEnabled(false)
, mUsePostProcessing(Settings::postProcessing().mEnabled) , mUsePostProcessing(Settings::postProcessing().mEnabled)
, mDisableDepthPasses(false) , mSamples(Settings::video().mAntialiasing)
, mLastFrameNumber(0) , mPingPongCull(new PingPongCull(this))
, mLastSimulationTime(0.f) , mCanvases({ new PingPongCanvas(mRendering.getResourceSystem()->getSceneManager()->getShaderManager()),
, mExteriorFlag(false) new PingPongCanvas(mRendering.getResourceSystem()->getSceneManager()->getShaderManager()) })
, mUnderwater(false)
, mHDR(false)
, mNormals(false)
, mPrevNormals(false)
, mNormalsSupported(false)
, mPassLights(false)
, mPrevPassLights(false)
{ {
mHUDCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
mHUDCamera->setRenderOrder(osg::Camera::POST_RENDER);
mHUDCamera->setClearColor(osg::Vec4(0.45, 0.45, 0.14, 1.0));
mHUDCamera->setClearMask(0);
mHUDCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
mHUDCamera->setAllowEventFocus(false);
mHUDCamera->setViewport(0, 0, mWidth, mHeight);
mHUDCamera->setNodeMask(Mask_RenderToTexture);
mHUDCamera->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
mHUDCamera->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
mHUDCamera->addChild(mCanvases[0]);
mHUDCamera->addChild(mCanvases[1]);
mHUDCamera->setCullCallback(new HUDCullCallback);
mViewer->getCamera()->addCullCallback(mPingPongCull);
if (Settings::shaders().mSoftParticles || Settings::postProcessing().mTransparentPostpass)
{
mTransparentDepthPostPass
= new TransparentDepthBinCallback(mRendering.getResourceSystem()->getSceneManager()->getShaderManager(),
Settings::postProcessing().mTransparentPostpass);
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(mTransparentDepthPostPass);
}
createObjectsForFrame(0);
createObjectsForFrame(1);
populateTechniqueFiles();
osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext(); osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext();
osg::GLExtensions* ext = gc->getState()->get<osg::GLExtensions>(); osg::GLExtensions* ext = gc->getState()->get<osg::GLExtensions>();
@ -169,11 +184,18 @@ namespace MWRender
mUBO = ext->isUniformBufferObjectSupported && mGLSLVersion >= 330; mUBO = ext->isUniformBufferObjectSupported && mGLSLVersion >= 330;
mStateUpdater = new fx::StateUpdater(mUBO); mStateUpdater = new fx::StateUpdater(mUBO);
if (!Stereo::getStereo() && !SceneUtil::AutoDepth::isReversed() && !Settings::shaders().mSoftParticles addChild(mHUDCamera);
&& !mUsePostProcessing) addChild(mRootNode);
return;
enable(mUsePostProcessing); mViewer->setSceneData(this);
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this));
mViewer->getCamera()->setUserData(this);
setCullCallback(mStateUpdater);
if (mUsePostProcessing)
enable();
} }
PostProcessor::~PostProcessor() PostProcessor::~PostProcessor()
@ -202,7 +224,6 @@ namespace MWRender
size_t frameId = frame() % 2; size_t frameId = frame() % 2;
createTexturesAndCamera(frameId);
createObjectsForFrame(frameId); createObjectsForFrame(frameId);
mRendering.updateProjectionMatrix(); mRendering.updateProjectionMatrix();
@ -210,8 +231,6 @@ namespace MWRender
dirtyTechniques(); dirtyTechniques();
mPingPongCanvas->dirty(frameId);
mDirty = true; mDirty = true;
mDirtyFrameId = !frameId; mDirtyFrameId = !frameId;
} }
@ -230,77 +249,20 @@ namespace MWRender
} }
} }
void PostProcessor::enable(bool usePostProcessing) void PostProcessor::enable()
{ {
mReload = true; mReload = true;
mEnabled = true; mUsePostProcessing = true;
const bool postPass = Settings::postProcessing().mTransparentPostpass;
mUsePostProcessing = usePostProcessing;
mDisableDepthPasses = !Settings::shaders().mSoftParticles && !postPass;
#ifdef ANDROID
mDisableDepthPasses = true;
#endif
if (!mDisableDepthPasses)
{
mTransparentDepthPostPass = new TransparentDepthBinCallback(
mRendering.getResourceSystem()->getSceneManager()->getShaderManager(), postPass);
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(mTransparentDepthPostPass);
}
if (mUsePostProcessing && mTechniqueFileMap.empty())
{
populateTechniqueFiles();
}
createTexturesAndCamera(frame() % 2);
removeChild(mHUDCamera);
removeChild(mRootNode);
addChild(mHUDCamera);
addChild(mRootNode);
mViewer->setSceneData(this);
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this));
mViewer->getCamera()->setUserData(this);
setCullCallback(mStateUpdater);
mHUDCamera->setCullCallback(new HUDCullCallback);
} }
void PostProcessor::disable() void PostProcessor::disable()
{ {
if (!Settings::shaders().mSoftParticles)
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(nullptr);
if (!SceneUtil::AutoDepth::isReversed() && !Settings::shaders().mSoftParticles)
{
removeChild(mHUDCamera);
setCullCallback(nullptr);
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER);
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(nullptr);
mViewer->getCamera()->setUserData(nullptr);
mEnabled = false;
}
mUsePostProcessing = false; mUsePostProcessing = false;
mRendering.getSkyManager()->setSunglare(true); mRendering.getSkyManager()->setSunglare(true);
} }
void PostProcessor::traverse(osg::NodeVisitor& nv) void PostProcessor::traverse(osg::NodeVisitor& nv)
{ {
if (!mEnabled)
{
osg::Group::traverse(nv);
return;
}
size_t frameId = nv.getTraversalNumber() % 2; size_t frameId = nv.getTraversalNumber() % 2;
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
@ -313,26 +275,23 @@ namespace MWRender
void PostProcessor::cull(size_t frameId, osgUtil::CullVisitor* cv) void PostProcessor::cull(size_t frameId, osgUtil::CullVisitor* cv)
{ {
const auto& fbo = getFbo(FBO_Intercept, frameId); if (const auto& fbo = getFbo(FBO_Intercept, frameId))
if (fbo)
{ {
osgUtil::RenderStage* rs = cv->getRenderStage(); osgUtil::RenderStage* rs = cv->getRenderStage();
if (rs && rs->getMultisampleResolveFramebufferObject()) if (rs && rs->getMultisampleResolveFramebufferObject())
rs->setMultisampleResolveFramebufferObject(fbo); rs->setMultisampleResolveFramebufferObject(fbo);
} }
mPingPongCanvas->setPostProcessing(frameId, mUsePostProcessing); mCanvases[frameId]->setPostProcessing(mUsePostProcessing);
mPingPongCanvas->setNormalsTexture(frameId, mNormals ? getTexture(Tex_Normal, frameId) : nullptr); mCanvases[frameId]->setTextureNormals(mNormals ? getTexture(Tex_Normal, frameId) : nullptr);
mPingPongCanvas->setMask(frameId, mUnderwater, mExteriorFlag); mCanvases[frameId]->setMask(mUnderwater, mExteriorFlag);
mPingPongCanvas->setHDR(frameId, getHDR()); mCanvases[frameId]->setCalculateAvgLum(mHDR);
mPingPongCanvas->setSceneTexture(frameId, getTexture(Tex_Scene, frameId)); mCanvases[frameId]->setTextureScene(getTexture(Tex_Scene, frameId));
if (mDisableDepthPasses) if (mTransparentDepthPostPass)
mPingPongCanvas->setDepthTexture(frameId, getTexture(Tex_Depth, frameId)); mCanvases[frameId]->setTextureDepth(getTexture(Tex_OpaqueDepth, frameId));
else else
mPingPongCanvas->setDepthTexture(frameId, getTexture(Tex_OpaqueDepth, frameId)); mCanvases[frameId]->setTextureDepth(getTexture(Tex_Depth, frameId));
mPingPongCanvas->setLDRSceneTexture(frameId, getTexture(Tex_Scene_LDR, frameId));
if (mTransparentDepthPostPass) if (mTransparentDepthPostPass)
{ {
@ -355,7 +314,7 @@ namespace MWRender
mStateUpdater->setDeltaSimulationTime(static_cast<float>(stamp->getSimulationTime() - mLastSimulationTime)); mStateUpdater->setDeltaSimulationTime(static_cast<float>(stamp->getSimulationTime() - mLastSimulationTime));
mLastSimulationTime = stamp->getSimulationTime(); mLastSimulationTime = stamp->getSimulationTime();
for (const auto& dispatchNode : mPingPongCanvas->getCurrentFrameData(frame)) for (const auto& dispatchNode : mCanvases[frameId]->getPasses())
{ {
for (auto& uniform : dispatchNode.mHandle->getUniformMap()) for (auto& uniform : dispatchNode.mHandle->getUniformMap())
{ {
@ -421,13 +380,15 @@ namespace MWRender
reloadIfRequired(); reloadIfRequired();
mCanvases[frameId]->setNodeMask(~0u);
mCanvases[!frameId]->setNodeMask(0);
if (mDirty && mDirtyFrameId == frameId) if (mDirty && mDirtyFrameId == frameId)
{ {
createTexturesAndCamera(frameId);
createObjectsForFrame(frameId); createObjectsForFrame(frameId);
mDirty = false;
mPingPongCanvas->setCurrentFrameData(frameId, fx::DispatchArray(mTemplateData)); mDirty = false;
mCanvases[frameId]->setPasses(fx::DispatchArray(mTemplateData));
} }
if ((mNormalsSupported && mNormals != mPrevNormals) || (mPassLights != mPrevPassLights)) if ((mNormalsSupported && mNormals != mPrevNormals) || (mPassLights != mPrevPassLights))
@ -448,7 +409,6 @@ namespace MWRender
mViewer->startThreading(); mViewer->startThreading();
createTexturesAndCamera(frameId);
createObjectsForFrame(frameId); createObjectsForFrame(frameId);
mDirty = true; mDirty = true;
@ -458,19 +418,55 @@ namespace MWRender
void PostProcessor::createObjectsForFrame(size_t frameId) void PostProcessor::createObjectsForFrame(size_t frameId)
{ {
auto& fbos = mFbos[frameId];
auto& textures = mTextures[frameId]; auto& textures = mTextures[frameId];
auto width = renderWidth();
auto height = renderHeight();
for (auto& tex : textures) int width = renderWidth();
int height = renderHeight();
for (osg::ref_ptr<osg::Texture>& texture : textures)
{ {
if (!tex) if (!texture)
continue; {
if (Stereo::getMultiview())
texture = new osg::Texture2DArray;
else
texture = new osg::Texture2D;
}
Stereo::setMultiviewCompatibleTextureSize(texture, width, height);
texture->setSourceFormat(GL_RGBA);
texture->setSourceType(GL_UNSIGNED_BYTE);
texture->setInternalFormat(GL_RGBA);
texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
texture->setResizeNonPowerOfTwoHint(false);
Stereo::setMultiviewCompatibleTextureSize(texture, width, height);
texture->dirtyTextureObject();
}
Stereo::setMultiviewCompatibleTextureSize(tex, width, height); textures[Tex_Normal]->setSourceFormat(GL_RGB);
tex->dirtyTextureObject(); textures[Tex_Normal]->setInternalFormat(GL_RGB);
auto setupDepth = [](osg::Texture* tex) {
tex->setSourceFormat(GL_DEPTH_STENCIL_EXT);
tex->setSourceType(SceneUtil::AutoDepth::depthSourceType());
tex->setInternalFormat(SceneUtil::AutoDepth::depthInternalFormat());
};
setupDepth(textures[Tex_Depth]);
if (!mTransparentDepthPostPass)
{
textures[Tex_OpaqueDepth] = nullptr;
} }
else
{
setupDepth(textures[Tex_OpaqueDepth]);
textures[Tex_OpaqueDepth]->setName("opaqueTexMap");
}
auto& fbos = mFbos[frameId];
fbos[FBO_Primary] = new osg::FrameBufferObject; fbos[FBO_Primary] = new osg::FrameBufferObject;
fbos[FBO_Primary]->setAttachment( fbos[FBO_Primary]->setAttachment(
@ -534,13 +530,12 @@ namespace MWRender
osg::FrameBufferAttachment(new osg::RenderBuffer(textures[Tex_OpaqueDepth]->getTextureWidth(), osg::FrameBufferAttachment(new osg::RenderBuffer(textures[Tex_OpaqueDepth]->getTextureWidth(),
textures[Tex_OpaqueDepth]->getTextureHeight(), textures[Tex_Scene]->getInternalFormat()))); textures[Tex_OpaqueDepth]->getTextureHeight(), textures[Tex_Scene]->getInternalFormat())));
#endif #endif
mCanvases[frameId]->dirty();
} }
void PostProcessor::dirtyTechniques() void PostProcessor::dirtyTechniques()
{ {
if (!isEnabled())
return;
size_t frameId = frame() % 2; size_t frameId = frame() % 2;
mDirty = true; mDirty = true;
@ -667,7 +662,7 @@ namespace MWRender
mTemplateData.emplace_back(std::move(node)); mTemplateData.emplace_back(std::move(node));
} }
mPingPongCanvas->setCurrentFrameData(frameId, fx::DispatchArray(mTemplateData)); mCanvases[frameId]->setPasses(fx::DispatchArray(mTemplateData));
if (auto hud = MWBase::Environment::get().getWindowManager()->getPostProcessorHud()) if (auto hud = MWBase::Environment::get().getWindowManager()->getPostProcessorHud())
hud->updateTechniques(); hud->updateTechniques();
@ -678,12 +673,6 @@ namespace MWRender
PostProcessor::Status PostProcessor::enableTechnique( PostProcessor::Status PostProcessor::enableTechnique(
std::shared_ptr<fx::Technique> technique, std::optional<int> location) std::shared_ptr<fx::Technique> technique, std::optional<int> location)
{ {
if (!isEnabled())
{
Log(Debug::Warning) << "PostProcessing disabled, cannot load technique '" << technique->getName() << "'";
return Status_Error;
}
if (!technique || technique->getLocked() || (location.has_value() && location.value() < 0)) if (!technique || technique->getLocked() || (location.has_value() && location.value() < 0))
return Status_Error; return Status_Error;
@ -721,86 +710,8 @@ namespace MWRender
return technique->isValid(); return technique->isValid();
} }
void PostProcessor::createTexturesAndCamera(size_t frameId)
{
auto& textures = mTextures[frameId];
auto width = renderWidth();
auto height = renderHeight();
for (auto& texture : textures)
{
if (!texture)
{
if (Stereo::getMultiview())
texture = new osg::Texture2DArray;
else
texture = new osg::Texture2D;
}
Stereo::setMultiviewCompatibleTextureSize(texture, width, height);
texture->setSourceFormat(GL_RGBA);
texture->setSourceType(GL_UNSIGNED_BYTE);
texture->setInternalFormat(GL_RGBA);
texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
texture->setResizeNonPowerOfTwoHint(false);
}
textures[Tex_Normal]->setSourceFormat(GL_RGB);
textures[Tex_Normal]->setInternalFormat(GL_RGB);
auto setupDepth = [](osg::Texture* tex) {
tex->setSourceFormat(GL_DEPTH_STENCIL_EXT);
tex->setSourceType(SceneUtil::AutoDepth::depthSourceType());
tex->setInternalFormat(SceneUtil::AutoDepth::depthInternalFormat());
};
setupDepth(textures[Tex_Depth]);
if (mDisableDepthPasses)
{
textures[Tex_OpaqueDepth] = nullptr;
}
else
{
setupDepth(textures[Tex_OpaqueDepth]);
textures[Tex_OpaqueDepth]->setName("opaqueTexMap");
}
if (mHUDCamera)
return;
mHUDCamera = new osg::Camera;
mHUDCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
mHUDCamera->setRenderOrder(osg::Camera::POST_RENDER);
mHUDCamera->setClearColor(osg::Vec4(0.45, 0.45, 0.14, 1.0));
mHUDCamera->setClearMask(0);
mHUDCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
mHUDCamera->setAllowEventFocus(false);
mHUDCamera->setViewport(0, 0, mWidth, mHeight);
mViewer->getCamera()->removeCullCallback(mPingPongCull);
mPingPongCull = new PingPongCull(this);
mViewer->getCamera()->addCullCallback(mPingPongCull);
mPingPongCanvas = new PingPongCanvas(mRendering.getResourceSystem()->getSceneManager()->getShaderManager());
mHUDCamera->addChild(mPingPongCanvas);
mHUDCamera->setNodeMask(Mask_RenderToTexture);
mHUDCamera->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
mHUDCamera->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
}
std::shared_ptr<fx::Technique> PostProcessor::loadTechnique(const std::string& name, bool loadNextFrame) std::shared_ptr<fx::Technique> PostProcessor::loadTechnique(const std::string& name, bool loadNextFrame)
{ {
if (!isEnabled())
{
Log(Debug::Warning) << "PostProcessing disabled, cannot load technique '" << name << "'";
return nullptr;
}
for (const auto& technique : mTemplates) for (const auto& technique : mTemplates)
if (Misc::StringUtils::ciEqual(technique->getName(), name)) if (Misc::StringUtils::ciEqual(technique->getName(), name))
return technique; return technique;
@ -831,9 +742,6 @@ namespace MWRender
void PostProcessor::loadChain() void PostProcessor::loadChain()
{ {
if (!isEnabled())
return;
mTechniques.clear(); mTechniques.clear();
for (const std::string& techniqueName : Settings::postProcessing().mChain.get()) for (const std::string& techniqueName : Settings::postProcessing().mChain.get())

@ -115,14 +115,14 @@ namespace MWRender
return mFbos[frameId][FBO_Multisample] ? mFbos[frameId][FBO_Multisample] : mFbos[frameId][FBO_Primary]; return mFbos[frameId][FBO_Multisample] ? mFbos[frameId][FBO_Multisample] : mFbos[frameId][FBO_Primary];
} }
osg::ref_ptr<osg::Camera> getHUDCamera() { return mHUDCamera; }
osg::ref_ptr<fx::StateUpdater> getStateUpdater() { return mStateUpdater; } osg::ref_ptr<fx::StateUpdater> getStateUpdater() { return mStateUpdater; }
const TechniqueList& getTechniques() { return mTechniques; } const TechniqueList& getTechniques() { return mTechniques; }
const TechniqueList& getTemplates() const { return mTemplates; } const TechniqueList& getTemplates() const { return mTemplates; }
osg::ref_ptr<PingPongCanvas> getCanvas() { return mPingPongCanvas; }
const auto& getTechniqueMap() const { return mTechniqueFileMap; } const auto& getTechniqueMap() const { return mTechniqueFileMap; }
void resize(); void resize();
@ -173,13 +173,11 @@ namespace MWRender
std::shared_ptr<fx::Technique> loadTechnique(const std::string& name, bool loadNextFrame = false); std::shared_ptr<fx::Technique> loadTechnique(const std::string& name, bool loadNextFrame = false);
bool isEnabled() const { return mUsePostProcessing && mEnabled; } bool isEnabled() const { return mUsePostProcessing; }
bool getHDR() const { return mHDR; }
void disable(); void disable();
void enable(bool usePostProcessing = true); void enable();
void setRenderTargetSize(int width, int height) void setRenderTargetSize(int width, int height)
{ {
@ -194,7 +192,7 @@ namespace MWRender
void triggerShaderReload(); void triggerShaderReload();
bool mEnableLiveReload; bool mEnableLiveReload = false;
void loadChain(); void loadChain();
void saveChain(); void saveChain();
@ -206,10 +204,6 @@ namespace MWRender
void createObjectsForFrame(size_t frameId); void createObjectsForFrame(size_t frameId);
void createTexturesAndCamera(size_t frameId);
void reloadMainPass(fx::Technique& technique);
void dirtyTechniques(); void dirtyTechniques();
void update(size_t frameId); void update(size_t frameId);
@ -232,43 +226,39 @@ namespace MWRender
std::unordered_map<std::string, std::filesystem::path> mTechniqueFileMap; std::unordered_map<std::string, std::filesystem::path> mTechniqueFileMap;
int mSamples;
bool mDirty;
size_t mDirtyFrameId;
RenderingManager& mRendering; RenderingManager& mRendering;
osgViewer::Viewer* mViewer; osgViewer::Viewer* mViewer;
const VFS::Manager* mVFS; const VFS::Manager* mVFS;
bool mTriggerShaderReload; size_t mDirtyFrameId = 0;
bool mReload; size_t mLastFrameNumber = 0;
bool mEnabled; float mLastSimulationTime = 0.f;
bool mUsePostProcessing;
bool mDisableDepthPasses; bool mDirty = false;
bool mReload = true;
size_t mLastFrameNumber; bool mTriggerShaderReload = false;
float mLastSimulationTime; bool mUsePostProcessing = false;
bool mExteriorFlag; bool mUBO = false;
bool mUnderwater; bool mHDR = false;
bool mHDR; bool mNormals = false;
bool mNormals; bool mUnderwater = false;
bool mPrevNormals; bool mPassLights = false;
bool mNormalsSupported; bool mPrevNormals = false;
bool mPassLights; bool mExteriorFlag = false;
bool mPrevPassLights; bool mNormalsSupported = false;
bool mUBO; bool mPrevPassLights = false;
int mGLSLVersion; int mGLSLVersion;
int mWidth;
int mHeight;
int mSamples;
osg::ref_ptr<fx::StateUpdater> mStateUpdater; osg::ref_ptr<fx::StateUpdater> mStateUpdater;
osg::ref_ptr<PingPongCull> mPingPongCull; osg::ref_ptr<PingPongCull> mPingPongCull;
osg::ref_ptr<PingPongCanvas> mPingPongCanvas; std::array<osg::ref_ptr<PingPongCanvas>, 2> mCanvases;
osg::ref_ptr<TransparentDepthBinCallback> mTransparentDepthPostPass; osg::ref_ptr<TransparentDepthBinCallback> mTransparentDepthPostPass;
int mWidth;
int mHeight;
fx::DispatchArray mTemplateData; fx::DispatchArray mTemplateData;
}; };
} }

@ -1256,7 +1256,7 @@ namespace MWRender
mSharedUniformStateUpdater->setScreenRes(res.x(), res.y()); mSharedUniformStateUpdater->setScreenRes(res.x(), res.y());
Stereo::Manager::instance().setMasterProjectionMatrix(mPerViewUniformStateUpdater->getProjectionMatrix()); Stereo::Manager::instance().setMasterProjectionMatrix(mPerViewUniformStateUpdater->getProjectionMatrix());
} }
else if (!mPostProcessor->isEnabled()) else
{ {
mSharedUniformStateUpdater->setScreenRes(width, height); mSharedUniformStateUpdater->setScreenRes(width, height);
} }

@ -21,6 +21,7 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "postprocessor.hpp" #include "postprocessor.hpp"
#include "util.hpp" #include "util.hpp"
@ -102,24 +103,6 @@ namespace MWRender
int width = screenW - leftPadding * 2; int width = screenW - leftPadding * 2;
int height = screenH - topPadding * 2; int height = screenH - topPadding * 2;
// Ensure we are reading from the resolved framebuffer and not the multisampled render buffer. Also ensure
// that the readbuffer is set correctly with rendeirng to FBO. glReadPixel() cannot read from multisampled
// targets
PostProcessor* postProcessor = dynamic_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData());
osg::GLExtensions* ext = osg::GLExtensions::Get(renderInfo.getContextID(), false);
if (ext)
{
size_t frameId = renderInfo.getState()->getFrameStamp()->getFrameNumber() % 2;
osg::FrameBufferObject* fbo = nullptr;
if (postProcessor && postProcessor->getFbo(PostProcessor::FBO_Primary, frameId))
fbo = postProcessor->getFbo(PostProcessor::FBO_Primary, frameId);
if (fbo)
fbo->apply(*renderInfo.getState(), osg::FrameBufferObject::READ_FRAMEBUFFER);
}
mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE); mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE);
mImage->scaleImage(mWidth, mHeight, 1); mImage->scaleImage(mWidth, mHeight, 1);
} }
@ -145,7 +128,7 @@ namespace MWRender
void ScreenshotManager::screenshot(osg::Image* image, int w, int h) void ScreenshotManager::screenshot(osg::Image* image, int w, int h)
{ {
osg::Camera* camera = mViewer->getCamera(); osg::Camera* camera = MWBase::Environment::get().getWorld()->getPostProcessor()->getHUDCamera();
osg::ref_ptr<osg::Drawable> tempDrw = new osg::Drawable; osg::ref_ptr<osg::Drawable> tempDrw = new osg::Drawable;
tempDrw->setDrawCallback(new ReadImageFromFramebufferCallback(image, w, h)); tempDrw->setDrawCallback(new ReadImageFromFramebufferCallback(image, w, h));
tempDrw->setCullingActive(false); tempDrw->setCullingActive(false);

Loading…
Cancel
Save