diff --git a/apps/opencs/view/render/cellwater.cpp b/apps/opencs/view/render/cellwater.cpp index f8857c3afc..485eed00fe 100644 --- a/apps/opencs/view/render/cellwater.cpp +++ b/apps/opencs/view/render/cellwater.cpp @@ -161,7 +161,7 @@ namespace CSVRender } mWaterGeometry = SceneUtil::createWaterGeometry(size, segments, textureRepeats); - mWaterGeometry->setStateSet(SceneUtil::createSimpleWaterStateSet(Alpha, RenderBin)); + mWaterGeometry->setStateSet(SceneUtil::createSimpleWaterStateSet(Alpha, RenderBin, false)); // Add water texture std::string textureName = Fallback::Map::getString("Water_SurfaceTexture"); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 6d58e880ed..a6d9402c4f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -21,7 +21,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation screenshotmanager bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation - renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover + renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover postprocessor ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index 49fae04619..c902ceb88a 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -8,7 +8,11 @@ #include "MyGUI_FactoryManager.h" #include +#include +#include +#include +#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -1217,8 +1221,10 @@ public: RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); + float z = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ() ? 1.0 : -1.0; + GlyphStream glyphStream(textFormat.mFont, static_cast(mCoord.left), static_cast(mCoord.top - mViewTop), - -1 /*mNode->getNodeDepth()*/, vertices, renderXform); + z /*mNode->getNodeDepth()*/, vertices, renderXform); int visit_top = (std::max) (mViewTop, mViewTop + int (renderXform.clipTop )); int visit_bottom = (std::min) (mViewBottom, mViewTop + int (renderXform.clipBottom)); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index efb434d3bc..538b866cac 100644 --- a/apps/openmw/mwgui/mapwindow.cpp +++ b/apps/openmw/mwgui/mapwindow.cpp @@ -741,7 +741,7 @@ namespace MWGui } // ------------------------------------------------------------------------------------------ - MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue) + MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue, bool reverseZ) : WindowPinnableBase("openmw_map_window.layout") , LocalMapBase(customMarkers, localMapRender) , NoDrop(drag, mMainWidget) @@ -751,7 +751,7 @@ namespace MWGui , mGlobal(Settings::Manager::getBool("global", "Map")) , mEventBoxGlobal(nullptr) , mEventBoxLocal(nullptr) - , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot(), workQueue)) + , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot(), workQueue, reverseZ)) , mEditNoteDialog() { static bool registered = false; diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index cb0d368b30..b7664505af 100644 --- a/apps/openmw/mwgui/mapwindow.hpp +++ b/apps/openmw/mwgui/mapwindow.hpp @@ -222,7 +222,7 @@ namespace MWGui class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase, public NoDrop { public: - MapWindow(CustomMarkerCollection& customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue); + MapWindow(CustomMarkerCollection& customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue, bool reverseZ); virtual ~MapWindow(); void setCellName(const std::string& cellName); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6eeb2d3654..5feed41864 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -305,8 +305,9 @@ namespace MWGui mGuiModeStates[GM_MainMenu] = GuiModeState(menu); mWindows.push_back(menu); - mLocalMapRender = new MWRender::LocalMap(mViewer->getSceneData()->asGroup()); - mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender, mWorkQueue); + bool reverseZ = mResourceSystem->getSceneManager()->getReverseZ(); + mLocalMapRender = new MWRender::LocalMap(mViewer->getSceneData()->asGroup(), reverseZ); + mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender, mWorkQueue, reverseZ); mWindows.push_back(mMap); mMap->renderGlobalMap(); trackWindow(mMap, "map"); diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index e88c4cee32..c4c628e04d 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -168,7 +168,7 @@ namespace MWRender mCamera->setRenderOrder(osg::Camera::PRE_RENDER); mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture, 0, 0, false, Settings::Manager::getInt("antialiasing", "Video")); mCamera->setName("CharacterPreview"); - mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); + mCamera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); mCamera->setCullMask(~(Mask_UpdateVisitor)); mCamera->setNodeMask(Mask_RenderToTexture); @@ -188,6 +188,9 @@ namespace MWRender defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); stateset->setAttribute(defaultMat); + osg::ref_ptr depth = new osg::Depth; + stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); + SceneUtil::ShadowManager::disableShadowsForStateSet(stateset); // assign large value to effectively turn off fog diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index dcaf9c1612..f9bbb3d2f4 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -15,6 +15,7 @@ #include #include +#include #include @@ -219,13 +220,14 @@ namespace MWRender osg::ref_ptr mOverlayTexture; }; - GlobalMap::GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue) + GlobalMap::GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue, bool reverseZ) : mRoot(root) , mWorkQueue(workQueue) , mWidth(0) , mHeight(0) , mMinX(0), mMaxX(0) , mMinY(0), mMaxY(0) + , mReverseZ(reverseZ) { mCellSize = Settings::Manager::getInt("global map cell size", "Map"); @@ -323,7 +325,7 @@ namespace MWRender if (texture) { osg::ref_ptr geom = createTexturedQuad(srcLeft, srcTop, srcRight, srcBottom); - osg::ref_ptr depth = new osg::Depth; + auto depth = SceneUtil::createDepth(mReverseZ); depth->setWriteMask(0); osg::StateSet* stateset = geom->getOrCreateStateSet(); stateset->setAttribute(depth); diff --git a/apps/openmw/mwrender/globalmap.hpp b/apps/openmw/mwrender/globalmap.hpp index fd8a8d1016..a27621c9f4 100644 --- a/apps/openmw/mwrender/globalmap.hpp +++ b/apps/openmw/mwrender/globalmap.hpp @@ -33,7 +33,7 @@ namespace MWRender class GlobalMap { public: - GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue); + GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue, bool reverseZ); ~GlobalMap(); void render(); @@ -126,6 +126,8 @@ namespace MWRender int mHeight; int mMinX, mMaxX, mMinY, mMaxY; + + bool mReverseZ; }; } diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 858d577335..e0b3d6c69b 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -83,13 +83,14 @@ namespace namespace MWRender { -LocalMap::LocalMap(osg::Group* root) +LocalMap::LocalMap(osg::Group* root, bool reverseZ) : mRoot(root) , mMapResolution(Settings::Manager::getInt("local map resolution", "Map")) , mMapWorldSize(Constants::CellSizeInUnits) , mCellDistance(Constants::CellGridRadius) , mAngle(0.f) , mInterior(false) + , mReverseZ(reverseZ) { // Increase map resolution, if use UI scaling float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor(); @@ -176,7 +177,12 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax) { osg::ref_ptr camera (new osg::Camera); - camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); + + if (mReverseZ) + camera->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10)); + else + camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); + camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); @@ -195,6 +201,13 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f osg::ref_ptr stateset = new osg::StateSet; stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE); + if (mReverseZ) + { + camera->setClearDepth(0.0); + auto depth = SceneUtil::createDepth(mReverseZ); + stateset->setAttributeAndModes(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); + } + // assign large value to effectively turn off fog // shaders don't respect glDisable(GL_FOG) osg::ref_ptr fog (new osg::Fog); diff --git a/apps/openmw/mwrender/localmap.hpp b/apps/openmw/mwrender/localmap.hpp index f9ccd5a011..316cbd53d3 100644 --- a/apps/openmw/mwrender/localmap.hpp +++ b/apps/openmw/mwrender/localmap.hpp @@ -36,7 +36,7 @@ namespace MWRender class LocalMap { public: - LocalMap(osg::Group* root); + LocalMap(osg::Group* root, bool reverseZ); ~LocalMap(); /** @@ -156,6 +156,7 @@ namespace MWRender void setupRenderToTexture(osg::ref_ptr camera, int x, int y); bool mInterior; + bool mReverseZ; osg::BoundingBox mBounds; }; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d338d70873..d103234f31 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -370,9 +370,9 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) class DepthClearCallback : public osgUtil::RenderBin::DrawCallback { public: - DepthClearCallback() + DepthClearCallback(bool reverseZ) { - mDepth = new osg::Depth; + mDepth = SceneUtil::createDepth(reverseZ); mDepth->setWriteMask(true); } @@ -432,7 +432,7 @@ void NpcAnimation::setRenderBin() if (!prototypeAdded) { osg::ref_ptr depthClearBin (new osgUtil::RenderBin); - depthClearBin->setDrawCallback(new DepthClearCallback); + depthClearBin->setDrawCallback(new DepthClearCallback(mResourceSystem->getSceneManager()->getReverseZ())); osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); prototypeAdded = true; } diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index aae8f2e4f1..709c67bc2d 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -661,6 +661,7 @@ namespace MWRender if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; + optimizer.setReverseZ(mSceneManager->getReverseZ()); if (size > 1/8.f) { optimizer.setViewPoint(relativeViewPoint); diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp new file mode 100644 index 0000000000..ed8171bd91 --- /dev/null +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -0,0 +1,216 @@ +#include "postprocessor.hpp" + +#include +#include +#include +#include +#include + +#include + +#include + +namespace +{ + osg::ref_ptr createFullScreenTri() + { + osg::ref_ptr geom = new osg::Geometry; + + osg::ref_ptr verts = new osg::Vec3Array; + verts->push_back(osg::Vec3f(-1, -1, 0)); + verts->push_back(osg::Vec3f(-1, 3, 0)); + verts->push_back(osg::Vec3f(3, -1, 0)); + + geom->setVertexArray(verts); + + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, 3)); + + return geom; + } + + class CullCallback : public osg::NodeCallback + { + public: + CullCallback(MWRender::PostProcessor* postProcessor) + : mPostProcessor(postProcessor) + , mLastFrameNumber(0) + {} + + void operator()(osg::Node* node, osg::NodeVisitor* nv) override + { + osgUtil::RenderStage* renderStage = nv->asCullVisitor()->getCurrentRenderStage(); + + unsigned int frame = nv->getTraversalNumber(); + if (frame != mLastFrameNumber) + { + mLastFrameNumber = frame; + if (!mPostProcessor->getMsaaFbo()) + { + renderStage->setFrameBufferObject(mPostProcessor->getFbo()); + } + else + { + renderStage->setMultisampleResolveFramebufferObject(mPostProcessor->getFbo()); + renderStage->setFrameBufferObject(mPostProcessor->getMsaaFbo()); + } + } + + traverse(node, nv); + } + MWRender::PostProcessor* mPostProcessor; + unsigned int mLastFrameNumber; + }; + + struct ResizedCallback : osg::GraphicsContext::ResizedCallback + { + ResizedCallback(MWRender::PostProcessor* postProcessor) + : mPostProcessor(postProcessor) + { + } + + void resizedImplementation(osg::GraphicsContext* gc, int x, int y, int width, int height) override + { + mPostProcessor->resize(width, height); + } + + MWRender::PostProcessor* mPostProcessor; + }; +} + +namespace MWRender +{ + PostProcessor::PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode) + : mViewer(viewer) + , mRootNode(new osg::Group) + { + int width = viewer->getCamera()->getViewport()->width(); + int height = viewer->getCamera()->getViewport()->height(); + + createTexturesAndCamera(width, height); + resize(width, height); + + mRootNode->addChild(mHUDCamera); + mRootNode->addChild(rootNode); + mViewer->setSceneData(mRootNode); + + // Main camera is treated specially, we need to manually set the FBO and + // resolve FBO during the cull callback. + mViewer->getCamera()->addCullCallback(new CullCallback(this)); + mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + mViewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, mSceneTex); + mViewer->getCamera()->attach(osg::Camera::DEPTH_BUFFER, mDepthTex); + + mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this)); + } + + void PostProcessor::resize(int width, int height) + { + mDepthTex->setTextureSize(width, height); + mSceneTex->setTextureSize(width, height); + mDepthTex->dirtyTextureObject(); + mSceneTex->dirtyTextureObject(); + + int samples = Settings::Manager::getInt("antialiasing", "Video"); + + mFbo = new osg::FrameBufferObject; + mFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(mSceneTex)); + mFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(mDepthTex)); + + // When MSAA is enabled we must first render to a render buffer, then + // blit the result to the FBO which is either passed to the main frame + // buffer for display or used as the entry point for a post process chain. + if (samples > 0) + { + mMsaaFbo = new osg::FrameBufferObject; + osg::ref_ptr colorRB = new osg::RenderBuffer(width, height, mSceneTex->getInternalFormat(), samples); + osg::ref_ptr depthRB = new osg::RenderBuffer(width, height, mDepthTex->getInternalFormat(), samples); + mMsaaFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(colorRB)); + mMsaaFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(depthRB)); + } + + double prevWidth = mViewer->getCamera()->getViewport()->width(); + double prevHeight = mViewer->getCamera()->getViewport()->height(); + double scaleX = prevWidth / width; + double scaleY = prevHeight / height; + + mViewer->getCamera()->resize(width,height); + mHUDCamera->resize(width,height); + + mViewer->getCamera()->getProjectionMatrix() *= osg::Matrix::scale(scaleX, scaleY, 1.0); + } + + void PostProcessor::createTexturesAndCamera(int width, int height) + { + mDepthTex = new osg::Texture2D; + mDepthTex->setTextureSize(width, height); + mDepthTex->setSourceFormat(GL_DEPTH_COMPONENT); + mDepthTex->setSourceType(GL_FLOAT); + mDepthTex->setInternalFormat(GL_DEPTH_COMPONENT32F); + mDepthTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); + mDepthTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); + mDepthTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + mDepthTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + mDepthTex->setResizeNonPowerOfTwoHint(false); + + mSceneTex = new osg::Texture2D; + mSceneTex->setTextureSize(width, height); + mSceneTex->setSourceFormat(GL_RGB); + mSceneTex->setSourceType(GL_UNSIGNED_BYTE); + mSceneTex->setInternalFormat(GL_RGB); + mSceneTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); + mSceneTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); + mSceneTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + mSceneTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + mSceneTex->setResizeNonPowerOfTwoHint(false); + + 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->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1)); + mHUDCamera->setAllowEventFocus(false); + mHUDCamera->setViewport(0, 0, width, height); + + // Shaders calculate correct UV coordinates for our fullscreen triangle + constexpr char vertSrc[] = R"GLSL( + #version 120 + + varying vec2 uv; + + void main() + { + gl_Position = vec4(gl_Vertex.xy, 0.0, 1.0); + uv = gl_Position.xy * 0.5 + 0.5; + } + )GLSL"; + + constexpr char fragSrc[] = R"GLSL( + #version 120 + + varying vec2 uv; + uniform sampler2D sceneTex; + + void main() + { + gl_FragData[0] = texture2D(sceneTex, uv); + } + )GLSL"; + + osg::ref_ptr vertShader = new osg::Shader(osg::Shader::VERTEX, vertSrc); + osg::ref_ptr fragShader = new osg::Shader(osg::Shader::FRAGMENT, fragSrc); + + osg::ref_ptr program = new osg::Program; + program->addShader(vertShader); + program->addShader(fragShader); + + mHUDCamera->addChild(createFullScreenTri()); + + auto* stateset = mHUDCamera->getOrCreateStateSet(); + stateset->setTextureAttributeAndModes(0, mSceneTex, osg::StateAttribute::ON); + stateset->setAttributeAndModes(program, osg::StateAttribute::ON); + stateset->addUniform(new osg::Uniform("sceneTex", 0)); + stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + } + +} diff --git a/apps/openmw/mwrender/postprocessor.hpp b/apps/openmw/mwrender/postprocessor.hpp new file mode 100644 index 0000000000..c9d353f8cb --- /dev/null +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -0,0 +1,46 @@ +#ifndef OPENMW_MWRENDER_POSTPROCESSOR_H +#define OPENMW_MWRENDER_POSTPROCESSOR_H + +#include + +namespace osg +{ + class Texture2D; + class Group; + class FrameBufferObject; + class Camera; +} + +namespace osgViewer +{ + class Viewer; +} + +namespace MWRender +{ + class PostProcessor + { + public: + PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode); + + auto getMsaaFbo() { return mMsaaFbo; } + auto getFbo() { return mFbo; } + + void resize(int width, int height); + + private: + osgViewer::Viewer* mViewer; + osg::ref_ptr mRootNode; + osg::ref_ptr mHUDCamera; + + osg::ref_ptr mMsaaFbo; + osg::ref_ptr mFbo; + + osg::ref_ptr mSceneTex; + osg::ref_ptr mDepthTex; + + void createTexturesAndCamera(int width, int height); + }; +} + +#endif \ No newline at end of file diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 81687a712a..dc439f00b8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include @@ -68,6 +70,7 @@ #include "objectpaging.hpp" #include "screenshotmanager.hpp" #include "groundcover.hpp" +#include "postprocessor.hpp" namespace MWRender { @@ -198,6 +201,17 @@ namespace MWRender , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) { + auto ext = osg::GLExtensions::Get(0, false); + bool reverseZ = ext && ext->isClipControlSupported; + + if (getenv("OPENMW_DISABLE_REVERSEZ") != nullptr) + reverseZ = false; + + if (reverseZ) + Log(Debug::Info) << "Using reverse-z depth buffer"; + else + Log(Debug::Info) << "Using standard depth buffer"; + auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders")); resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); @@ -216,6 +230,7 @@ namespace MWRender resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders")); resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1); + resourceSystem->getSceneManager()->setReverseZ(reverseZ); // Let LightManager choose which backend to use based on our hint. For methods besides legacy lighting, this depends on support for various OpenGL extensions. osg::ref_ptr sceneRoot = new SceneUtil::LightManager(lightingMethod == SceneUtil::LightingMethod::FFP); @@ -242,7 +257,7 @@ namespace MWRender if (Settings::Manager::getBool("object shadows", "Shadows")) shadowCastingTraversalMask |= (Mask_Object|Mask_Static); - mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager())); + mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager(), reverseZ)); Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines(); Shader::ShaderManager::DefineMap lightDefines = sceneRoot->getLightDefines(); @@ -267,6 +282,8 @@ namespace MWRender globalDefines["groundcoverStompMode"] = std::to_string(std::clamp(Settings::Manager::getInt("stomp mode", "Groundcover"), 0, 2)); globalDefines["groundcoverStompIntensity"] = std::to_string(std::clamp(Settings::Manager::getInt("stomp intensity", "Groundcover"), 0, 2)); + globalDefines["reverseZ"] = reverseZ ? "1" : "0"; + // It is unnecessary to stop/start the viewer as no frames are being rendered yet. mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines); @@ -413,6 +430,7 @@ namespace MWRender mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater)); NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor); NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect); + NifOsg::Loader::setReverseZ(reverseZ); Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models")); mNearClip = Settings::Manager::getFloat("near clip", "Camera"); @@ -434,6 +452,18 @@ namespace MWRender mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near"); mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far"); + + if (reverseZ) + { + auto depth = SceneUtil::createDepth(reverseZ); + osg::ref_ptr clipcontrol = new osg::ClipControl(osg::ClipControl::LOWER_LEFT, osg::ClipControl::ZERO_TO_ONE); + mViewer->getCamera()->setClearDepth(0.0); + mRootNode->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); + mRootNode->getOrCreateStateSet()->setAttributeAndModes(clipcontrol, osg::StateAttribute::ON); + } + + mPostProcessor.reset(new PostProcessor(viewer, mRootNode)); + updateProjectionMatrix(); } @@ -1066,7 +1096,20 @@ namespace MWRender float fov = mFieldOfView; if (mFieldOfViewOverridden) fov = mFieldOfViewOverride; - mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); + + if (mResourceSystem->getSceneManager()->getReverseZ()) + { + mViewer->getCamera()->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspectiveInf(fov, aspect, mNearClip)); + float linearFac = -mNearClip / (mViewDistance - mNearClip) - 1.0; + mRootNode->getOrCreateStateSet()->getOrCreateUniform("linearFac", osg::Uniform::FLOAT, 1)->set(linearFac); + + osg::Matrix shadowProj = osg::Matrix::perspective(fov, aspect, mNearClip, mViewDistance); + mViewer->getCamera()->setUserValue("shadowProj", shadowProj); + mViewer->getCamera()->setUserValue("near", static_cast(mNearClip)); + mViewer->getCamera()->setUserValue("far", static_cast(mViewDistance)); + } + else + mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); mUniformNear->set(mNearClip); mUniformFar->set(mViewDistance); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index a0a74bd5c4..b63841494b 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -89,6 +89,7 @@ namespace MWRender class RecastMesh; class ObjectPaging; class Groundcover; + class PostProcessor; class RenderingManager : public MWRender::RenderingInterface { @@ -287,6 +288,7 @@ namespace MWRender std::unique_ptr mScreenshotManager; std::unique_ptr mEffectManager; std::unique_ptr mShadowManager; + std::unique_ptr mPostProcessor; osg::ref_ptr mPlayerAnimation; osg::ref_ptr mPlayerNode; std::unique_ptr mCamera; diff --git a/apps/openmw/mwrender/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 6788f53f44..c7deac51de 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "vismask.hpp" @@ -55,7 +56,7 @@ namespace stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON); - osg::ref_ptr depth (new osg::Depth); + auto depth = SceneUtil::createDepth(resourceSystem->getSceneManager()->getReverseZ()); depth->setWriteMask(false); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 8bac90604b..604e417a45 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -473,7 +473,7 @@ const float CelestialBody::mDistance = 1000.0f; class Sun : public CelestialBody { public: - Sun(osg::Group* parentNode, Resource::ImageManager& imageManager) + Sun(osg::Group* parentNode, Resource::ImageManager& imageManager, bool reverseZ) : CelestialBody(parentNode, 1.0f, 1, Mask_Sun) , mUpdater(new Updater) { @@ -502,8 +502,8 @@ public: mTransform->addChild(queryNode); - mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true); - mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false); + mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true, reverseZ); + mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false, reverseZ); createSunFlash(imageManager); createSunGlare(); @@ -556,7 +556,7 @@ private: }; /// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels. - osg::ref_ptr createOcclusionQueryNode(osg::Group* parent, bool queryVisible) + osg::ref_ptr createOcclusionQueryNode(osg::Group* parent, bool queryVisible, bool reverseZ) { osg::ref_ptr oqn = new osg::OcclusionQueryNode; oqn->setQueriesEnabled(true); @@ -594,13 +594,13 @@ private: osg::StateSet* queryStateSet = new osg::StateSet; if (queryVisible) { - osg::ref_ptr depth (new osg::Depth); - depth->setFunction(osg::Depth::LEQUAL); + auto depth = SceneUtil::createDepth(reverseZ); // This is a trick to make fragments written by the query always use the maximum depth value, // without having to retrieve the current far clipping distance. // We want the sun glare to be "infinitely" far away. - depth->setZNear(1.0); - depth->setZFar(1.0); + float far = reverseZ ? 0.0 : 1.0; + depth->setZNear(far); + depth->setZFar(far); depth->setWriteMask(false); queryStateSet->setAttributeAndModes(depth, osg::StateAttribute::ON); } @@ -1188,7 +1188,8 @@ void SkyManager::create() mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getImageManager()); atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater); - mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager())); + bool reverseZ = mSceneManager->getReverseZ(); + mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), reverseZ)); mMasser.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat("Moons_Masser_Size")/125, Moon::Type_Masser)); mSecunda.reset(new Moon(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), Fallback::Map::getFloat("Moons_Secunda_Size")/125, Moon::Type_Secunda)); @@ -1209,7 +1210,7 @@ void SkyManager::create() mCloudMesh2->addUpdateCallback(mCloudUpdater2); mCloudMesh2->setNodeMask(0); - osg::ref_ptr depth = new osg::Depth; + auto depth = SceneUtil::createDepth(reverseZ); depth->setWriteMask(false); mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 621a5e7c25..1318921688 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -273,6 +273,7 @@ public: void setDefaults(osg::Camera* camera) override { + camera->setClearDepth(1.0); camera->setReferenceFrame(osg::Camera::RELATIVE_RF); camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); camera->setName("RefractionCamera"); @@ -338,6 +339,7 @@ public: void setDefaults(osg::Camera* camera) override { + camera->setClearDepth(1.0); camera->setReferenceFrame(osg::Camera::RELATIVE_RF); camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); camera->setName("ReflectionCamera"); @@ -544,7 +546,7 @@ osg::Node* Water::getRefractionNode() void Water::createSimpleWaterStateSet(osg::Node* node, float alpha) { - osg::ref_ptr stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water); + osg::ref_ptr stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water, mResourceSystem->getSceneManager()->getReverseZ()); node->setStateSet(stateset); node->setUpdateCallback(nullptr); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 7c32bb04f8..384c640388 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -226,6 +226,18 @@ namespace NifOsg return sIntersectionDisabledNodeMask; } + bool Loader::sReverseZ = false; + + void Loader::setReverseZ(bool reverseZ) + { + sReverseZ = reverseZ; + } + + bool Loader::getReverseZ() + { + return sReverseZ; + } + class LoaderImpl { public: @@ -1823,7 +1835,7 @@ namespace NifOsg // Depth test flag stateset->setMode(GL_DEPTH_TEST, zprop->flags&1 ? osg::StateAttribute::ON : osg::StateAttribute::OFF); - osg::ref_ptr depth = new osg::Depth; + auto depth = SceneUtil::createDepth(Loader::getReverseZ()); // Depth write flag depth->setWriteMask((zprop->flags>>1)&1); // Morrowind ignores depth test function diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index 8ee6b41674..cee75de6d2 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -51,10 +51,14 @@ namespace NifOsg static void setIntersectionDisabledNodeMask(unsigned int mask); static unsigned int getIntersectionDisabledNodeMask(); + static void setReverseZ(bool reverseZ); + static bool getReverseZ(); + private: static unsigned int sHiddenNodeMask; static unsigned int sIntersectionDisabledNodeMask; static bool sShowMarkers; + static bool sReverseZ; }; } diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index f6035a47dd..70e849b234 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -275,6 +275,16 @@ namespace Resource return mClampLighting; } + void SceneManager::setReverseZ(bool reverseZ) + { + mReverseZ = reverseZ; + } + + bool SceneManager::getReverseZ() const + { + return mReverseZ; + } + void SceneManager::setAutoUseNormalMaps(bool use) { mAutoUseNormalMaps = use; diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index de014165bf..260fd4717f 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -92,6 +92,9 @@ namespace Resource void setClampLighting(bool clamp); bool getClampLighting() const; + void setReverseZ(bool reverseZ); + bool getReverseZ() const; + /// @see ShaderVisitor::setAutoUseNormalMaps void setAutoUseNormalMaps(bool use); @@ -202,6 +205,7 @@ namespace Resource SceneUtil::LightingMethod mLightingMethod; SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods; bool mConvertAlphaTestToAlphaToCoverage; + bool mReverseZ; osg::ref_ptr mInstanceCache; diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index ad3fc5fd65..bbda5442bf 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -23,10 +23,13 @@ #include #include #include +#include #include #include "shadowsbin.hpp" +#include + namespace { using namespace osgShadow; @@ -347,6 +350,11 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) _projectionMatrix = cv->getProjectionMatrix(); } +bool isOrthographicViewFrustum(const osg::Matrix& m) +{ + return m(0,3)==0.0 && m(1,3)==0.0 && m(2,3)==0.0; +} + } // namespace MWShadowTechnique::ComputeLightSpaceBounds::ComputeLightSpaceBounds(osg::Viewport* viewport, const osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix) : @@ -890,6 +898,16 @@ void SceneUtil::MWShadowTechnique::disableFrontFaceCulling() } } +void SceneUtil::MWShadowTechnique::enableReverseZ() +{ + _reverseZ = true; +} + +void SceneUtil::MWShadowTechnique::disableReverseZ() +{ + _reverseZ = false; +} + void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & shaderManager) { // This can't be part of the constructor as OSG mandates that there be a trivial constructor available @@ -970,6 +988,9 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) return; } + osg::Matrix shadowProj; + cv.getCurrentCamera()->getUserValue("shadowProj", shadowProj); + ViewDependentData* vdd = getViewDependentData(&cv); if (!vdd) @@ -985,34 +1006,41 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) osg::CullSettings::ComputeNearFarMode cachedNearFarMode = cv.getComputeNearFarMode(); - osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix(); - - // check whether this main views projection is perspective or orthographic - bool orthographicViewFrustum = viewProjectionMatrix(0,3)==0.0 && - viewProjectionMatrix(1,3)==0.0 && - viewProjectionMatrix(2,3)==0.0; - double minZNear = 0.0; double maxZFar = dbl_max; + bool orthographicViewFrustum; - if (cachedNearFarMode==osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) + if (_reverseZ) { - double left, right, top, bottom; - if (orthographicViewFrustum) - { - viewProjectionMatrix.getOrtho(left, right, bottom, top, minZNear, maxZFar); - } - else - { - viewProjectionMatrix.getFrustum(left, right, bottom, top, minZNear, maxZFar); - } - OSG_INFO<<"minZNear="<getUserValue("far", maxZFar); + orthographicViewFrustum = isOrthographicViewFrustum(shadowProj); } - - // set the compute near/far mode to the highest quality setting to ensure we push the near plan out as far as possible - if (settings->getComputeNearFarModeOverride()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) + else { - cv.setComputeNearFarMode(settings->getComputeNearFarModeOverride()); + osg::RefMatrix& viewProjectionMatrix = *cv.getProjectionMatrix(); + + // check whether this main views projection is perspective or orthographic + orthographicViewFrustum = isOrthographicViewFrustum(viewProjectionMatrix); + + if (cachedNearFarMode==osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) + { + double left, right, top, bottom; + if (orthographicViewFrustum) + { + viewProjectionMatrix.getOrtho(left, right, bottom, top, minZNear, maxZFar); + } + else + { + viewProjectionMatrix.getFrustum(left, right, bottom, top, minZNear, maxZFar); + } + } + + // set the compute near/far mode to the highest quality setting to ensure we push the near plan out as far as possible + if (settings->getComputeNearFarModeOverride()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) + { + cv.setComputeNearFarMode(settings->getComputeNearFarModeOverride()); + } } // 1. Traverse main scene graph @@ -1024,6 +1052,9 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) cv.popStateSet(); + if (_reverseZ) + cv.pushProjectionMatrix(new osg::RefMatrix(shadowProj)); + if (cv.getComputeNearFarMode()!=osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { OSG_INFO<<"Just done main subgraph traversak"<addUniform(new osg::Uniform("alphaTestShadows", false)); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(true); + osg::ref_ptr clipcontrol = new osg::ClipControl(osg::ClipControl::LOWER_LEFT, osg::ClipControl::NEGATIVE_ONE_TO_ONE); + if (_reverseZ) + _shadowCastingStateSet->setAttribute(clipcontrol, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON); diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index de57bf1fdc..7fde0d7dbb 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -85,6 +85,10 @@ namespace SceneUtil { virtual void disableFrontFaceCulling(); + virtual void enableReverseZ(); + + virtual void disableReverseZ(); + virtual void setupCastingShader(Shader::ShaderManager &shaderManager); class ComputeLightSpaceBounds : public osg::NodeVisitor, public osg::CullStack @@ -266,6 +270,8 @@ namespace SceneUtil { float _shadowFadeStart = 0.0; + bool _reverseZ = false; + class DebugHUD final : public osg::Referenced { public: diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index b48ceda409..6bde81c12d 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -40,6 +40,8 @@ #include +#include + using namespace osgUtil; namespace SceneUtil @@ -107,6 +109,7 @@ void Optimizer::optimize(osg::Node* node, unsigned int options) MergeGeometryVisitor mgv(this); mgv.setTargetMaximumNumberOfVertices(1000000); mgv.setMergeAlphaBlending(_mergeAlphaBlending); + mgv.setReverseZ(_reverseZ); mgv.setViewPoint(_viewPoint); node->accept(mgv); @@ -1560,7 +1563,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) } if (_alphaBlendingActive && _mergeAlphaBlending && !geom->getStateSet()) { - osg::Depth* d = new osg::Depth; + auto d = createDepth(_reverseZ); d->setWriteMask(0); geom->getOrCreateStateSet()->setAttribute(d); } diff --git a/components/sceneutil/optimizer.hpp b/components/sceneutil/optimizer.hpp index 2d6293e231..9f9c788445 100644 --- a/components/sceneutil/optimizer.hpp +++ b/components/sceneutil/optimizer.hpp @@ -65,7 +65,7 @@ class Optimizer public: - Optimizer() : _mergeAlphaBlending(false) {} + Optimizer() : _mergeAlphaBlending(false), _reverseZ(false) {} virtual ~Optimizer() {} enum OptimizationOptions @@ -119,6 +119,7 @@ class Optimizer }; void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; } + void setReverseZ(bool reverseZ) { _reverseZ = reverseZ; } void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; } /** Reset internal data to initial state - the getPermissibleOptionsMap is cleared.*/ @@ -257,6 +258,7 @@ class Optimizer osg::Vec3f _viewPoint; bool _mergeAlphaBlending; + bool _reverseZ; public: @@ -377,12 +379,18 @@ class Optimizer /// default to traversing all children. MergeGeometryVisitor(Optimizer* optimizer=0) : BaseOptimizerVisitor(optimizer, MERGE_GEOMETRY), - _targetMaximumNumberOfVertices(10000), _alphaBlendingActive(false), _mergeAlphaBlending(false) {} + _targetMaximumNumberOfVertices(10000), _alphaBlendingActive(false), _mergeAlphaBlending(false), _reverseZ(false) {} void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; } + + void setReverseZ(bool reverseZ) + { + _reverseZ = reverseZ; + } + void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; @@ -421,6 +429,7 @@ class Optimizer std::vector _stateSetStack; bool _alphaBlendingActive; bool _mergeAlphaBlending; + bool _reverseZ; osg::Vec3f _viewPoint; }; diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index 3244dc672a..32c2edeccd 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -94,10 +94,12 @@ namespace SceneUtil } } - ShadowManager::ShadowManager(osg::ref_ptr sceneRoot, osg::ref_ptr rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager) : mShadowedScene(new osgShadow::ShadowedScene), - mShadowTechnique(new MWShadowTechnique), - mOutdoorShadowCastingMask(outdoorShadowCastingMask), - mIndoorShadowCastingMask(indoorShadowCastingMask) + ShadowManager::ShadowManager(osg::ref_ptr sceneRoot, osg::ref_ptr rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager, bool reverseZ) + : mShadowedScene(new osgShadow::ShadowedScene) + , mReverseZ(reverseZ) + , mShadowTechnique(new MWShadowTechnique) + , mOutdoorShadowCastingMask(outdoorShadowCastingMask) + , mIndoorShadowCastingMask(indoorShadowCastingMask) { mShadowedScene->setShadowTechnique(mShadowTechnique); @@ -108,6 +110,9 @@ namespace SceneUtil mShadowSettings = mShadowedScene->getShadowSettings(); setupShadowSettings(); + if (mReverseZ) + mShadowTechnique->enableReverseZ(); + mShadowTechnique->setupCastingShader(shaderManager); enableOutdoorMode(); @@ -180,4 +185,9 @@ namespace SceneUtil mShadowTechnique->enableShadows(); mShadowSettings->setCastsShadowTraversalMask(mOutdoorShadowCastingMask); } + + bool ShadowManager::getReverseZ() const + { + return mReverseZ; + } } diff --git a/components/sceneutil/shadow.hpp b/components/sceneutil/shadow.hpp index c823ecf860..8092f77f93 100644 --- a/components/sceneutil/shadow.hpp +++ b/components/sceneutil/shadow.hpp @@ -17,7 +17,7 @@ namespace SceneUtil static Shader::ShaderManager::DefineMap getShadowsDisabledDefines(); - ShadowManager(osg::ref_ptr sceneRoot, osg::ref_ptr rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager); + ShadowManager(osg::ref_ptr sceneRoot, osg::ref_ptr rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager, bool reverseZ); void setupShadowSettings(); @@ -26,8 +26,11 @@ namespace SceneUtil void enableIndoorMode(); void enableOutdoorMode(); + + bool getReverseZ() const; protected: bool mEnableShadows; + bool mReverseZ; osg::ref_ptr mShadowedScene; osg::ref_ptr mShadowSettings; diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index 9b4fb9d3fb..2e37260eb7 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -285,4 +285,43 @@ bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg:: return addMSAAIntermediateTarget; } +osg::ref_ptr createDepth(bool reverseZ) +{ + static osg::Depth::Function func = reverseZ ? osg::Depth::GEQUAL : osg::Depth::LEQUAL; + return new osg::Depth(func); +} + +osg::Matrix getReversedZProjectionMatrixAsPerspectiveInf(double fov, double aspect, double near) +{ + double A = 1.0/std::tan(osg::DegreesToRadians(fov)/2.0); + return osg::Matrix( + A/aspect, 0, 0, 0, + 0, A, 0, 0, + 0, 0, 0, -1, + 0, 0, near, 0 + ); +} + +osg::Matrix getReversedZProjectionMatrixAsPerspective(double fov, double aspect, double near, double far) +{ + double A = 1.0/std::tan(osg::DegreesToRadians(fov)/2.0); + return osg::Matrix( + A/aspect, 0, 0, 0, + 0, A, 0, 0, + 0, 0, far/(far-near)-1.0, -1, + 0, 0, -(far*near)/(far - near), 0 + ); +} + +osg::Matrix getReversedZProjectionMatrixAsOrtho(double left, double right, double bottom, double top, double near, double far) +{ + return osg::Matrix( + 2/(right-left), 0, 0, 0, + 0, 2/(top-bottom), 0, 0, + 0, 0, 1/(far-near), 0, + (right+left)/(left-right), (top+bottom)/(bottom-top), far/(far-near), 1 + ); +} + + } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index f297c42d2e..0164e0c99e 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -64,6 +65,23 @@ namespace SceneUtil // Alpha-to-coverage requires a multisampled framebuffer, so we need to set that up for RTTs bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg::Camera::BufferComponent buffer, osg::Texture* texture, unsigned int level = 0, unsigned int face = 0, bool mipMapGeneration = false); + + // Returns a suitable depth state attribute dependent on whether a reverse-z + // depth buffer is in use. + osg::ref_ptr createDepth(bool reverseZ); + + // Returns a perspective projection matrix for use with a reversed z-buffer + // and an infinite far plane. This is derived by mapping the default z-range + // of [0,1] to [1,0], then taking the limit as far plane approaches + // infinity. + osg::Matrix getReversedZProjectionMatrixAsPerspectiveInf(double fov, double aspect, double near); + + // Returns a perspective projection matrix for use with a reversed z-buffer. + osg::Matrix getReversedZProjectionMatrixAsPerspective(double fov, double aspect, double near, double far); + + // Returns an orthographic projection matrix for use with a reversed z-buffer. + osg::Matrix getReversedZProjectionMatrixAsOrtho(double left, double right, double bottom, double top, double near, double far); + } #endif diff --git a/components/sceneutil/waterutil.cpp b/components/sceneutil/waterutil.cpp index 8a434105c8..dccc534ecc 100644 --- a/components/sceneutil/waterutil.cpp +++ b/components/sceneutil/waterutil.cpp @@ -5,6 +5,8 @@ #include #include +#include "util.hpp" + namespace SceneUtil { // disable nonsense test against a worldsize bb what will always pass @@ -62,7 +64,7 @@ namespace SceneUtil return waterGeom; } - osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin) + osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin, bool reverseZ) { osg::ref_ptr stateset (new osg::StateSet); @@ -76,7 +78,7 @@ namespace SceneUtil stateset->setMode(GL_BLEND, osg::StateAttribute::ON); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); - osg::ref_ptr depth (new osg::Depth); + auto depth = createDepth(reverseZ); depth->setWriteMask(false); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); diff --git a/components/sceneutil/waterutil.hpp b/components/sceneutil/waterutil.hpp index 7b8c380109..7f08dbcfa9 100644 --- a/components/sceneutil/waterutil.hpp +++ b/components/sceneutil/waterutil.hpp @@ -13,7 +13,7 @@ namespace SceneUtil { osg::ref_ptr createWaterGeometry(float size, int segments, float textureRepeats); - osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin); + osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin, bool reverseZ); } #endif diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index a744471de5..5354b9d8a2 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -163,7 +163,7 @@ std::vector > ChunkManager::createPasses(float chunk float blendmapScale = mStorage->getBlendmapScale(chunkSize); - return ::Terrain::createPasses(useShaders, &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale); + return ::Terrain::createPasses(useShaders, mSceneManager, layers, blendmapTextures, blendmapScale, blendmapScale); } osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile) @@ -219,7 +219,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve layer.mDiffuseMap = compositeMap->mTexture; layer.mParallax = false; layer.mSpecular = false; - geometry->setPasses(::Terrain::createPasses(mSceneManager->getForceShaders() || !mSceneManager->getClampLighting(), &mSceneManager->getShaderManager(), std::vector(1, layer), std::vector >(), 1.f, 1.f)); + geometry->setPasses(::Terrain::createPasses(mSceneManager->getForceShaders() || !mSceneManager->getClampLighting(), mSceneManager, std::vector(1, layer), std::vector >(), 1.f, 1.f)); } else { diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index e662f4439f..dc74b58560 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -7,7 +7,9 @@ #include #include +#include #include +#include #include @@ -95,19 +97,18 @@ namespace class LequalDepth { public: - static const osg::ref_ptr& value() + static const osg::ref_ptr& value(bool reverseZ) { - static LequalDepth instance; + static LequalDepth instance(reverseZ); return instance.mValue; } private: osg::ref_ptr mValue; - LequalDepth() - : mValue(new osg::Depth) + LequalDepth(bool reverseZ) + : mValue(SceneUtil::createDepth(reverseZ)) { - mValue->setFunction(osg::Depth::LEQUAL); } }; @@ -170,9 +171,10 @@ namespace namespace Terrain { - std::vector > createPasses(bool useShaders, Shader::ShaderManager* shaderManager, const std::vector &layers, + std::vector > createPasses(bool useShaders, Resource::SceneManager* sceneManager, const std::vector &layers, const std::vector > &blendmaps, int blendmapScale, float layerTileSize) { + Shader::ShaderManager* shaderManager = &sceneManager->getShaderManager(); std::vector > passes; unsigned int blendmapIndex = 0; @@ -195,7 +197,7 @@ namespace Terrain else { stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); - stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); + stateset->setAttributeAndModes(LequalDepth::value(sceneManager->getReverseZ()), osg::StateAttribute::ON); } } @@ -238,7 +240,7 @@ namespace Terrain if (!vertexShader || !fragmentShader) { // Try again without shader. Error already logged by above - return createPasses(false, shaderManager, layers, blendmaps, blendmapScale, layerTileSize); + return createPasses(false, sceneManager, layers, blendmaps, blendmapScale, layerTileSize); } stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader)); diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index 5f78af6a06..d5ef40a29e 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -10,9 +10,9 @@ namespace osg class Texture2D; } -namespace Shader +namespace Resource { - class ShaderManager; + class SceneManager; } namespace Terrain @@ -26,7 +26,7 @@ namespace Terrain bool mSpecular; }; - std::vector > createPasses(bool useShaders, Shader::ShaderManager* shaderManager, + std::vector > createPasses(bool useShaders, Resource::SceneManager* sceneManager, const std::vector& layers, const std::vector >& blendmaps, int blendmapScale, float layerTileSize); diff --git a/files/shaders/CMakeLists.txt b/files/shaders/CMakeLists.txt index 5297b2e752..5e049b8a3f 100644 --- a/files/shaders/CMakeLists.txt +++ b/files/shaders/CMakeLists.txt @@ -13,6 +13,7 @@ set(SHADER_FILES water_fragment.glsl water_nm.png alpha.glsl + depth.glsl objects_vertex.glsl objects_fragment.glsl terrain_vertex.glsl diff --git a/files/shaders/depth.glsl b/files/shaders/depth.glsl new file mode 100644 index 0000000000..0adde7a2a7 --- /dev/null +++ b/files/shaders/depth.glsl @@ -0,0 +1,12 @@ +#if @reverseZ +uniform float linearFac; +#endif + +float getLinearDepth(in vec4 viewPos) +{ +#if @reverseZ + return linearFac*viewPos.z; +#else + return gl_Position.z; +#endif +} \ No newline at end of file diff --git a/files/shaders/groundcover_vertex.glsl b/files/shaders/groundcover_vertex.glsl index a12fb3d9d9..94586734b6 100644 --- a/files/shaders/groundcover_vertex.glsl +++ b/files/shaders/groundcover_vertex.glsl @@ -39,6 +39,7 @@ centroid varying vec3 shadowDiffuseLighting; #include "shadows_vertex.glsl" #include "lighting.glsl" +#include "depth.glsl" uniform float osg_SimulationTime; uniform mat4 osg_ViewMatrixInverse; @@ -143,7 +144,7 @@ void main(void) else gl_Position = gl_ProjectionMatrix * viewPos; - linearDepth = gl_Position.z; + linearDepth = getLinearDepth(viewPos); #if (!PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * rotation3(rotation) * gl_Normal).xyz); diff --git a/files/shaders/nv_default_vertex.glsl b/files/shaders/nv_default_vertex.glsl index 50f5daf25e..1c5483dcda 100644 --- a/files/shaders/nv_default_vertex.glsl +++ b/files/shaders/nv_default_vertex.glsl @@ -32,6 +32,7 @@ varying vec3 passNormal; #include "vertexcolors.glsl" #include "shadows_vertex.glsl" #include "lighting.glsl" +#include "depth.glsl" void main(void) { @@ -40,7 +41,7 @@ void main(void) vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); - linearDepth = gl_Position.z; + linearDepth = getLinearDepth(viewPos); #if @diffuseMap diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy; diff --git a/files/shaders/nv_nolighting_vertex.glsl b/files/shaders/nv_nolighting_vertex.glsl index 275f1e5730..02c28eb5e3 100644 --- a/files/shaders/nv_nolighting_vertex.glsl +++ b/files/shaders/nv_nolighting_vertex.glsl @@ -17,6 +17,7 @@ varying vec3 passViewPos; varying float passFalloff; #include "vertexcolors.glsl" +#include "depth.glsl" void main(void) { @@ -27,7 +28,7 @@ void main(void) #if @radialFog euclideanDepth = length(viewPos.xyz); #else - linearDepth = gl_Position.z; + linearDepth = getLinearDepth(viewPos); #endif #if @diffuseMap diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index 64c35a21cc..34484ebd41 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -62,6 +62,7 @@ varying vec3 passNormal; #include "shadows_vertex.glsl" #include "lighting.glsl" +#include "depth.glsl" void main(void) { @@ -70,7 +71,7 @@ void main(void) vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); - linearDepth = gl_Position.z; + linearDepth = getLinearDepth(viewPos); #if (@envMap || !PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); diff --git a/files/shaders/terrain_vertex.glsl b/files/shaders/terrain_vertex.glsl index 638d6cca0b..221e0c27a9 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -25,6 +25,7 @@ varying vec3 passNormal; #include "shadows_vertex.glsl" #include "lighting.glsl" +#include "depth.glsl" void main(void) { @@ -33,7 +34,7 @@ void main(void) vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); - linearDepth = gl_Position.z; + linearDepth = getLinearDepth(viewPos); #if (!PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index d1e795eca7..ed0ebdc913 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -158,11 +158,14 @@ uniform float rainIntensity; float frustumDepth; float linearizeDepth(float depth) - { - float z_n = 2.0 * depth - 1.0; - depth = 2.0 * near * far / (far + near - z_n * frustumDepth); - return depth; - } +{ +#if @reverseZ + depth = 1.0 - depth; +#endif + float z_n = 2.0 * depth - 1.0; + depth = 2.0 * near * far / (far + near - z_n * frustumDepth); + return depth; +} void main(void) { diff --git a/files/shaders/water_vertex.glsl b/files/shaders/water_vertex.glsl index 02a395f95d..9513001847 100644 --- a/files/shaders/water_vertex.glsl +++ b/files/shaders/water_vertex.glsl @@ -5,6 +5,7 @@ varying vec4 position; varying float linearDepth; #include "shadows_vertex.glsl" +#include "depth.glsl" void main(void) { @@ -20,7 +21,8 @@ void main(void) position = gl_Vertex; - linearDepth = gl_Position.z; + vec4 viewPos = gl_ModelViewMatrix * gl_Vertex; + linearDepth = getLinearDepth(viewPos); - setupShadowCoords(gl_ModelViewMatrix * gl_Vertex, normalize((gl_NormalMatrix * gl_Normal).xyz)); + setupShadowCoords(viewPos, normalize((gl_NormalMatrix * gl_Normal).xyz)); }