From 70fac33940f11096ee1a8448f8dc9dd03b012b1c Mon Sep 17 00:00:00 2001 From: "glassmancody.info" Date: Tue, 1 Jun 2021 12:15:25 -0700 Subject: [PATCH 1/5] initial reverse-z depth implementation --- apps/opencs/view/render/cellwater.cpp | 2 +- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/bookpage.cpp | 8 +- apps/openmw/mwgui/mapwindow.cpp | 4 +- apps/openmw/mwgui/mapwindow.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +- apps/openmw/mwrender/characterpreview.cpp | 5 +- apps/openmw/mwrender/globalmap.cpp | 6 +- apps/openmw/mwrender/globalmap.hpp | 4 +- apps/openmw/mwrender/localmap.cpp | 17 +- apps/openmw/mwrender/localmap.hpp | 3 +- apps/openmw/mwrender/npcanimation.cpp | 6 +- apps/openmw/mwrender/objectpaging.cpp | 1 + apps/openmw/mwrender/postprocessor.cpp | 216 +++++++++++++++++++++ apps/openmw/mwrender/postprocessor.hpp | 46 +++++ apps/openmw/mwrender/renderingmanager.cpp | 47 ++++- apps/openmw/mwrender/renderingmanager.hpp | 2 + apps/openmw/mwrender/ripplesimulation.cpp | 3 +- apps/openmw/mwrender/sky.cpp | 21 +- apps/openmw/mwrender/water.cpp | 4 +- components/nifosg/nifloader.cpp | 14 +- components/nifosg/nifloader.hpp | 4 + components/resource/scenemanager.cpp | 10 + components/resource/scenemanager.hpp | 4 + components/sceneutil/mwshadowtechnique.cpp | 83 +++++--- components/sceneutil/mwshadowtechnique.hpp | 6 + components/sceneutil/optimizer.cpp | 5 +- components/sceneutil/optimizer.hpp | 13 +- components/sceneutil/shadow.cpp | 18 +- components/sceneutil/shadow.hpp | 5 +- components/sceneutil/util.cpp | 39 ++++ components/sceneutil/util.hpp | 18 ++ components/sceneutil/waterutil.cpp | 6 +- components/sceneutil/waterutil.hpp | 2 +- components/terrain/chunkmanager.cpp | 4 +- components/terrain/material.cpp | 18 +- components/terrain/material.hpp | 6 +- files/shaders/CMakeLists.txt | 1 + files/shaders/depth.glsl | 12 ++ files/shaders/groundcover_vertex.glsl | 3 +- files/shaders/nv_default_vertex.glsl | 3 +- files/shaders/nv_nolighting_vertex.glsl | 3 +- files/shaders/objects_vertex.glsl | 3 +- files/shaders/terrain_vertex.glsl | 3 +- files/shaders/water_fragment.glsl | 13 +- files/shaders/water_vertex.glsl | 6 +- 46 files changed, 614 insertions(+), 92 deletions(-) create mode 100644 apps/openmw/mwrender/postprocessor.cpp create mode 100644 apps/openmw/mwrender/postprocessor.hpp create mode 100644 files/shaders/depth.glsl 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)); } From d89e37d6895c19b558668b6d4f193250807bc36d Mon Sep 17 00:00:00 2001 From: "glassmancody.info" Date: Wed, 2 Jun 2021 14:46:26 -0700 Subject: [PATCH 2/5] add framebuffer extension check and fix issues with manual screenshots --- apps/openmw/mwgui/savegamedialog.cpp | 1 + apps/openmw/mwrender/postprocessor.cpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 5 ++++- apps/openmw/mwrender/screenshotmanager.cpp | 23 +++++++++++++++++++++- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index c4d608443c..987d27c9a6 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -450,6 +450,7 @@ namespace MWGui osg::ref_ptr texture (new osg::Texture2D); texture->setImage(result.getImage()); + texture->setInternalFormat(GL_RGBA); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index ed8171bd91..43765a25ba 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -116,6 +116,8 @@ namespace MWRender mFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(mSceneTex)); mFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(mDepthTex)); + mViewer->getCamera()->setUserData(mFbo); + // 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. diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index dc439f00b8..65aa4a5a6b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -462,7 +462,10 @@ namespace MWRender mRootNode->getOrCreateStateSet()->setAttributeAndModes(clipcontrol, osg::StateAttribute::ON); } - mPostProcessor.reset(new PostProcessor(viewer, mRootNode)); + if (ext && ext->isFrameBufferObjectSupported) + mPostProcessor.reset(new PostProcessor(viewer, mRootNode)); + else + Log(Debug::Warning) << "Disabling postprocessing and using default framebuffer for rendering: FrameBufferObjects not supported"; updateProjectionMatrix(); } diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index b24572dbaa..ba4282e5b6 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -88,6 +89,19 @@ namespace MWRender int topPadding = std::max(0, static_cast(screenH - screenW / imageaspect) / 2); int width = screenW - leftPadding*2; int height = screenH - topPadding*2; + + // Ensure we are reading from the resolved framebuffer and not the multisampled render buffer when in use. + // glReadPixel() cannot read from multisampled targets. + + osg::FrameBufferObject* fbo = dynamic_cast(renderInfo.getCurrentCamera()->getUserData()); + + if (fbo) + { + osg::GLExtensions* ext = osg::GLExtensions::Get(renderInfo.getContextID(), false); + if (ext) + ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo->getHandle(renderInfo.getContextID())); + } + mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE); mImage->scaleImage(mWidth, mHeight, 1); } @@ -236,6 +250,7 @@ namespace MWRender osg::ref_ptr screenshotCamera(new osg::Camera); osg::ref_ptr quad(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0,0,0), 2.0))); + quad->getOrCreateStateSet()->setRenderBinDetails(100, "RenderBin", osg::StateSet::USE_RENDERBIN_DETAILS); std::map defineMap; @@ -283,6 +298,9 @@ namespace MWRender camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT,osg::Camera::PIXEL_BUFFER_RTT); + if (MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ()) + camera->setClearDepth(0.0); + camera->setViewport(0, 0, w, h); osg::ref_ptr texture (new osg::Texture2D); @@ -318,7 +336,10 @@ namespace MWRender float nearClip = Settings::Manager::getFloat("near clip", "Camera"); float viewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); // each cubemap side sees 90 degrees - rttCamera->setProjectionMatrixAsPerspective(90.0, w/float(h), nearClip, viewDistance); + if (MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ()) + rttCamera->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspectiveInf(90.0, w/float(h), nearClip)); + else + rttCamera->setProjectionMatrixAsPerspective(90.0, w/float(h), nearClip, viewDistance); rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix() * cameraTransform); rttCamera->setUpdateCallback(new NoTraverseCallback); From b457dfd8b838ad63613c4c19edf51f43af4943a5 Mon Sep 17 00:00:00 2001 From: "glassmancody.info" Date: Fri, 4 Jun 2021 12:13:52 -0700 Subject: [PATCH 3/5] fix water RTTs and minor math error in non-infinite projection matrix --- components/sceneutil/util.cpp | 31 +++++++++++++++++++++++++++++-- components/sceneutil/util.hpp | 17 +++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index 2e37260eb7..79fe5d13c5 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -150,6 +150,33 @@ void GlowUpdater::setDuration(float duration) mDuration = duration; } +AttachMultisampledDepthColorCallback::AttachMultisampledDepthColorCallback(const osg::ref_ptr& colorTex, const osg::ref_ptr& depthTex, int samples, int colorSamples) +{ + int width = colorTex->getTextureWidth(); + int height = colorTex->getTextureHeight(); + + osg::ref_ptr rbColor = new osg::RenderBuffer(width, height, colorTex->getInternalFormat(), samples, colorSamples); + osg::ref_ptr rbDepth = new osg::RenderBuffer(width, height, depthTex->getInternalFormat(), samples, colorSamples); + + mMsaaFbo = new osg::FrameBufferObject; + mMsaaFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(rbColor)); + mMsaaFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(rbDepth)); + + mFbo = new osg::FrameBufferObject; + mFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(colorTex)); + mFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(depthTex)); +} + +void AttachMultisampledDepthColorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + osgUtil::RenderStage* renderStage = nv->asCullVisitor()->getCurrentRenderStage(); + + renderStage->setMultisampleResolveFramebufferObject(mFbo); + renderStage->setFrameBufferObject(mMsaaFbo); + + traverse(node, nv); +} + void transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere) { osg::BoundingSphere::vec_type xdash = bsphere._center; @@ -308,8 +335,8 @@ osg::Matrix getReversedZProjectionMatrixAsPerspective(double fov, double aspect, 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 + 0, 0, near/(far-near), -1, + 0, 0, (far*near)/(far - near), 0 ); } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index 0164e0c99e..69e66bb8b3 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -48,6 +49,22 @@ namespace SceneUtil bool mDone; }; + // Allows camera to render to a color and floating point depth texture with a multisampled framebuffer. + // Must be set on a cameras cull callback. + // When the depth texture isn't needed as a sampler, use osg::Camera::attach(osg::Camera::DEPTH_COMPONENT, GL_DEPTH_COMPONENT32F) instead. + // If multisampling is not being used on the color buffer attachment, use the osg::Camera::attach() method. + class AttachMultisampledDepthColorCallback : public osg::NodeCallback + { + public: + AttachMultisampledDepthColorCallback(const osg::ref_ptr& colorTex, const osg::ref_ptr& depthTex, int samples, int colorSamples); + + void operator()(osg::Node* node, osg::NodeVisitor* nv) override; + + private: + osg::ref_ptr mFbo; + osg::ref_ptr mMsaaFbo; + }; + // Transform a bounding sphere by a matrix // based off private code in osg::Transform // TODO: patch osg to make public From cad0b151cb915556f9e30fcc36c193009c37f818 Mon Sep 17 00:00:00 2001 From: "glassmancody.info" Date: Fri, 9 Jul 2021 10:05:27 -0700 Subject: [PATCH 4/5] enable shaders path and dehardcode depth formats --- CHANGELOG.md | 1 + apps/openmw/mwgui/bookpage.cpp | 8 +- apps/openmw/mwgui/mapwindow.cpp | 4 +- apps/openmw/mwgui/mapwindow.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 6 +- apps/openmw/mwrender/actorspaths.cpp | 5 ++ apps/openmw/mwrender/bulletdebugdraw.cpp | 14 +++- apps/openmw/mwrender/characterpreview.cpp | 3 +- apps/openmw/mwrender/globalmap.cpp | 6 +- apps/openmw/mwrender/globalmap.hpp | 4 +- apps/openmw/mwrender/localmap.cpp | 12 +-- apps/openmw/mwrender/localmap.hpp | 3 +- apps/openmw/mwrender/navmesh.cpp | 6 ++ apps/openmw/mwrender/npcanimation.cpp | 7 +- apps/openmw/mwrender/pathgrid.cpp | 5 ++ apps/openmw/mwrender/postprocessor.cpp | 72 +++++++++++++---- apps/openmw/mwrender/postprocessor.hpp | 12 ++- apps/openmw/mwrender/recastmesh.cpp | 6 ++ apps/openmw/mwrender/renderingmanager.cpp | 77 ++++++++++++++----- apps/openmw/mwrender/renderingmanager.hpp | 6 +- apps/openmw/mwrender/screenshotmanager.cpp | 4 +- apps/openmw/mwrender/sky.cpp | 18 ++--- apps/openmw/mwrender/util.cpp | 8 ++ apps/openmw/mwrender/util.hpp | 2 + components/resource/scenemanager.cpp | 10 +++ components/resource/scenemanager.hpp | 4 + components/sceneutil/mwshadowtechnique.cpp | 73 +++++++----------- components/sceneutil/shadow.cpp | 4 +- components/sceneutil/util.cpp | 8 +- components/sceneutil/util.hpp | 4 +- components/terrain/cellborder.cpp | 13 +++- components/terrain/cellborder.hpp | 8 +- components/terrain/world.cpp | 3 +- .../reference/modding/settings/camera.rst | 24 +++++- files/settings-default.cfg | 5 +- files/shaders/CMakeLists.txt | 2 + files/shaders/debug_fragment.glsl | 8 ++ files/shaders/debug_vertex.glsl | 12 +++ files/shaders/depth.glsl | 6 +- files/shaders/groundcover_vertex.glsl | 5 +- files/shaders/nv_default_vertex.glsl | 6 +- files/shaders/nv_nolighting_vertex.glsl | 6 +- files/shaders/objects_vertex.glsl | 7 +- files/shaders/terrain_vertex.glsl | 6 +- files/shaders/water_vertex.glsl | 8 +- 45 files changed, 355 insertions(+), 158 deletions(-) create mode 100644 files/shaders/debug_fragment.glsl create mode 100644 files/shaders/debug_vertex.glsl diff --git a/CHANGELOG.md b/CHANGELOG.md index a9ec5a946e..7e136d707a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ Feature #5489: MCP: Telekinesis fix for activators Feature #5996: Support Lua scripts in OpenMW Feature #6017: Separate persistent and temporary cell references when saving + Feature #6032: Reverse-z depth buffer Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly 0.47.0 diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index c902ceb88a..e4ce2a9593 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -8,14 +8,12 @@ #include "MyGUI_FactoryManager.h" #include -#include -#include -#include -#include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwrender/util.hpp" + namespace MWGui { struct TypesetBookImpl; @@ -1221,7 +1219,7 @@ public: RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); - float z = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ() ? 1.0 : -1.0; + float z = MWRender::getReverseZ() ? 1.f : -1.f; GlyphStream glyphStream(textFormat.mFont, static_cast(mCoord.left), static_cast(mCoord.top - mViewTop), z /*mNode->getNodeDepth()*/, vertices, renderXform); diff --git a/apps/openmw/mwgui/mapwindow.cpp b/apps/openmw/mwgui/mapwindow.cpp index 538b866cac..efb434d3bc 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, bool reverseZ) + MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue) : 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, reverseZ)) + , mGlobalMapRender(new MWRender::GlobalMap(localMapRender->getRoot(), workQueue)) , mEditNoteDialog() { static bool registered = false; diff --git a/apps/openmw/mwgui/mapwindow.hpp b/apps/openmw/mwgui/mapwindow.hpp index b7664505af..cb0d368b30 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, bool reverseZ); + MapWindow(CustomMarkerCollection& customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue); virtual ~MapWindow(); void setCellName(const std::string& cellName); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5feed41864..e42d606966 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -35,7 +35,6 @@ #include #include -#include #include @@ -305,9 +304,8 @@ namespace MWGui mGuiModeStates[GM_MainMenu] = GuiModeState(menu); mWindows.push_back(menu); - bool reverseZ = mResourceSystem->getSceneManager()->getReverseZ(); - mLocalMapRender = new MWRender::LocalMap(mViewer->getSceneData()->asGroup(), reverseZ); - mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender, mWorkQueue, reverseZ); + mLocalMapRender = new MWRender::LocalMap(mViewer->getSceneData()->asGroup()); + mMap = new MapWindow(mCustomMarkers, mDragAndDrop, mLocalMapRender, mWorkQueue); mWindows.push_back(mMap); mMap->renderGlobalMap(); trackWindow(mMap, "map"); diff --git a/apps/openmw/mwrender/actorspaths.cpp b/apps/openmw/mwrender/actorspaths.cpp index 35b2553555..4e3bfd79a6 100644 --- a/apps/openmw/mwrender/actorspaths.cpp +++ b/apps/openmw/mwrender/actorspaths.cpp @@ -2,9 +2,13 @@ #include "vismask.hpp" #include +#include +#include #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" namespace MWRender { ActorsPaths::ActorsPaths(const osg::ref_ptr& root, bool enabled) @@ -43,6 +47,7 @@ namespace MWRender const auto newGroup = SceneUtil::createAgentPathGroup(path, halfExtents, start, end, settings); if (newGroup) { + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(newGroup, "debug"); newGroup->setNodeMask(Mask_Debug); mRootNode->addChild(newGroup); mGroups[actor] = newGroup; diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index 207b0ee504..9039ec53ce 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -10,10 +10,19 @@ #include #include #include +#include #include "bulletdebugdraw.hpp" #include "vismask.hpp" +#include +#include + +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + namespace MWRender { @@ -30,7 +39,6 @@ void DebugDrawer::createGeometry() { mGeometry = new osg::Geometry; mGeometry->setNodeMask(Mask_Debug); - mVertices = new osg::Vec3Array; mColors = new osg::Vec4Array; @@ -42,6 +50,8 @@ void DebugDrawer::createGeometry() mGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); mGeometry->setDataVariance(osg::Object::DYNAMIC); mGeometry->addPrimitiveSet(mDrawArrays); + // make this friendly to recreateShaders() + mGeometry->setStateSet(new osg::StateSet); mParentNode->addChild(mGeometry); @@ -52,6 +62,8 @@ void DebugDrawer::createGeometry() mShapesRoot->setDataVariance(osg::Object::DYNAMIC); mShapesRoot->setNodeMask(Mask_Debug); mParentNode->addChild(mShapesRoot); + + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(mGeometry, "debug"); } } diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c4c628e04d..580451ff8f 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::DO_NOT_COMPUTE_NEAR_FAR); + mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES); mCamera->setCullMask(~(Mask_UpdateVisitor)); mCamera->setNodeMask(Mask_RenderToTexture); @@ -187,6 +187,7 @@ namespace MWRender defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); stateset->setAttribute(defaultMat); + stateset->addUniform(new osg::Uniform("projectionMatrix", static_cast(mCamera->getProjectionMatrix()))); osg::ref_ptr depth = new osg::Depth; stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index f9bbb3d2f4..2f160933fd 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -25,6 +25,7 @@ #include "../mwworld/esmstore.hpp" #include "vismask.hpp" +#include "util.hpp" namespace { @@ -220,14 +221,13 @@ namespace MWRender osg::ref_ptr mOverlayTexture; }; - GlobalMap::GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue, bool reverseZ) + GlobalMap::GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue) : 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"); @@ -325,7 +325,7 @@ namespace MWRender if (texture) { osg::ref_ptr geom = createTexturedQuad(srcLeft, srcTop, srcRight, srcBottom); - auto depth = SceneUtil::createDepth(mReverseZ); + auto depth = SceneUtil::createDepth(getReverseZ()); 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 a27621c9f4..fd8a8d1016 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, bool reverseZ); + GlobalMap(osg::Group* root, SceneUtil::WorkQueue* workQueue); ~GlobalMap(); void render(); @@ -126,8 +126,6 @@ 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 e0b3d6c69b..72f2828ace 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -30,6 +30,7 @@ #include "../mwworld/cellstore.hpp" #include "vismask.hpp" +#include "util.hpp" namespace { @@ -83,14 +84,13 @@ namespace namespace MWRender { -LocalMap::LocalMap(osg::Group* root, bool reverseZ) +LocalMap::LocalMap(osg::Group* root) : 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(); @@ -178,7 +178,7 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f { osg::ref_ptr camera (new osg::Camera); - if (mReverseZ) + if (getReverseZ()) 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); @@ -201,13 +201,15 @@ 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) + if (getReverseZ()) { camera->setClearDepth(0.0); - auto depth = SceneUtil::createDepth(mReverseZ); + auto depth = SceneUtil::createDepth(true); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } + stateset->addUniform(new osg::Uniform("projectionMatrix", static_cast(camera->getProjectionMatrix())), 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 316cbd53d3..f9ccd5a011 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, bool reverseZ); + LocalMap(osg::Group* root); ~LocalMap(); /** @@ -156,7 +156,6 @@ 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/navmesh.cpp b/apps/openmw/mwrender/navmesh.cpp index 791c41a1a0..523f7531af 100644 --- a/apps/openmw/mwrender/navmesh.cpp +++ b/apps/openmw/mwrender/navmesh.cpp @@ -2,9 +2,14 @@ #include "vismask.hpp" #include +#include +#include #include +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + namespace MWRender { NavMesh::NavMesh(const osg::ref_ptr& root, bool enabled) @@ -45,6 +50,7 @@ namespace MWRender mGroup = SceneUtil::createNavMeshGroup(navMesh, settings); if (mGroup) { + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(mGroup, "debug"); mGroup->setNodeMask(Mask_Debug); mRootNode->addChild(mGroup); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d103234f31..64dc9ecfe0 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -43,6 +43,7 @@ #include "rotatecontroller.hpp" #include "renderbin.hpp" #include "vismask.hpp" +#include "util.hpp" namespace { @@ -370,9 +371,9 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) class DepthClearCallback : public osgUtil::RenderBin::DrawCallback { public: - DepthClearCallback(bool reverseZ) + DepthClearCallback() { - mDepth = SceneUtil::createDepth(reverseZ); + mDepth = SceneUtil::createDepth(getReverseZ()); mDepth->setWriteMask(true); } @@ -432,7 +433,7 @@ void NpcAnimation::setRenderBin() if (!prototypeAdded) { osg::ref_ptr depthClearBin (new osgUtil::RenderBin); - depthClearBin->setDrawCallback(new DepthClearCallback(mResourceSystem->getSceneManager()->getReverseZ())); + depthClearBin->setDrawCallback(new DepthClearCallback); osgUtil::RenderBin::addRenderBinPrototype("DepthClear", depthClearBin); prototypeAdded = true; } diff --git a/apps/openmw/mwrender/pathgrid.cpp b/apps/openmw/mwrender/pathgrid.cpp index c20e81bb2d..42d9150811 100644 --- a/apps/openmw/mwrender/pathgrid.cpp +++ b/apps/openmw/mwrender/pathgrid.cpp @@ -8,7 +8,10 @@ #include #include +#include #include +#include +#include #include "../mwbase/world.hpp" // these includes can be removed once the static-hack is gone #include "../mwbase/environment.hpp" @@ -112,6 +115,8 @@ void Pathgrid::enableCellPathgrid(const MWWorld::CellStore *store) osg::ref_ptr geometry = SceneUtil::createPathgridGeometry(*pathgrid); + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(geometry, "debug"); + cellPathGrid->addChild(geometry); mPathGridRoot->addChild(cellPathGrid); diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 43765a25ba..a412ca8cfa 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -9,6 +9,11 @@ #include #include +#include +#include + +#include "vismask.hpp" +#include "renderingmanager.hpp" namespace { @@ -57,6 +62,8 @@ namespace traverse(node, nv); } + + private: MWRender::PostProcessor* mPostProcessor; unsigned int mLastFrameNumber; }; @@ -70,6 +77,7 @@ namespace void resizedImplementation(osg::GraphicsContext* gc, int x, int y, int width, int height) override { + gc->resizedImplementation(x, y, width, height); mPostProcessor->resize(width, height); } @@ -79,22 +87,55 @@ namespace namespace MWRender { - PostProcessor::PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode) + PostProcessor::PostProcessor(RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode) : mViewer(viewer) , mRootNode(new osg::Group) + , mRendering(rendering) { + osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext(); + unsigned int contextID = gc->getState()->getContextID(); + osg::GLExtensions* ext = gc->getState()->get(); + + constexpr char errPreamble[] = "Postprocessing and floating point depth buffers disabled: "; + + if (!ext->isFrameBufferObjectSupported) + { + Log(Debug::Warning) << errPreamble << "FrameBufferObject unsupported."; + return; + } + + if (Settings::Manager::getInt("antialiasing", "Video") > 1 && !ext->isRenderbufferMultisampleSupported()) + { + Log(Debug::Warning) << errPreamble << "RenderBufferMultiSample unsupported. Disabling antialiasing will resolve this issue."; + return; + } + + if (osg::isGLExtensionSupported(contextID, "GL_ARB_depth_buffer_float")) + mDepthFormat = GL_DEPTH_COMPONENT32F; + else if (osg::isGLExtensionSupported(contextID, "GL_NV_depth_buffer_float")) + mDepthFormat = GL_DEPTH_COMPONENT32F_NV; + else + { + // TODO: Once we have post-processing implemented we want to skip this return and continue with setup. + // Rendering to a FBO to fullscreen geometry has overhead (especially when MSAA is enabled) and there are no + // benefits if no floating point depth formats are supported. + mDepthFormat = GL_DEPTH_COMPONENT24; + Log(Debug::Warning) << errPreamble << "'GL_ARB_depth_buffer_float' and 'GL_NV_depth_buffer_float' unsupported."; + return; + } + int width = viewer->getCamera()->getViewport()->width(); int height = viewer->getCamera()->getViewport()->height(); createTexturesAndCamera(width, height); - resize(width, height); + resize(width, height, true); 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. + // We need to manually set the FBO and resolve FBO during the cull callback. If we were using a separate + // RTT camera this would not be needed. mViewer->getCamera()->addCullCallback(new CullCallback(this)); mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); mViewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, mSceneTex); @@ -103,12 +144,12 @@ namespace MWRender mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this)); } - void PostProcessor::resize(int width, int height) + void PostProcessor::resize(int width, int height, bool init) { mDepthTex->setTextureSize(width, height); mSceneTex->setTextureSize(width, height); - mDepthTex->dirtyTextureObject(); - mSceneTex->dirtyTextureObject(); + mDepthTex->dirtyTextureObject(); + mSceneTex->dirtyTextureObject(); int samples = Settings::Manager::getInt("antialiasing", "Video"); @@ -130,15 +171,13 @@ namespace MWRender 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; + if (init) + return; - mViewer->getCamera()->resize(width,height); - mHUDCamera->resize(width,height); + mViewer->getCamera()->resize(width, height); + mHUDCamera->resize(width, height); - mViewer->getCamera()->getProjectionMatrix() *= osg::Matrix::scale(scaleX, scaleY, 1.0); + mRendering.updateProjectionMatrix(); } void PostProcessor::createTexturesAndCamera(int width, int height) @@ -146,8 +185,8 @@ namespace MWRender mDepthTex = new osg::Texture2D; mDepthTex->setTextureSize(width, height); mDepthTex->setSourceFormat(GL_DEPTH_COMPONENT); - mDepthTex->setSourceType(GL_FLOAT); - mDepthTex->setInternalFormat(GL_DEPTH_COMPONENT32F); + mDepthTex->setSourceType(SceneUtil::isFloatingPointDepthFormat(getDepthFormat()) ? GL_FLOAT : GL_UNSIGNED_INT); + mDepthTex->setInternalFormat(mDepthFormat); 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); @@ -206,6 +245,7 @@ namespace MWRender program->addShader(fragShader); mHUDCamera->addChild(createFullScreenTri()); + mHUDCamera->setNodeMask(Mask_RenderToTexture); auto* stateset = mHUDCamera->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, mSceneTex, osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/postprocessor.hpp b/apps/openmw/mwrender/postprocessor.hpp index c9d353f8cb..5be58f33ac 100644 --- a/apps/openmw/mwrender/postprocessor.hpp +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -18,15 +18,19 @@ namespace osgViewer namespace MWRender { + class RenderingManager; + class PostProcessor { public: - PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode); + PostProcessor(RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode); auto getMsaaFbo() { return mMsaaFbo; } auto getFbo() { return mFbo; } - void resize(int width, int height); + int getDepthFormat() { return mDepthFormat; } + + void resize(int width, int height, bool init=false); private: osgViewer::Viewer* mViewer; @@ -39,7 +43,11 @@ namespace MWRender osg::ref_ptr mSceneTex; osg::ref_ptr mDepthTex; + int mDepthFormat; + void createTexturesAndCamera(int width, int height); + + RenderingManager& mRendering; }; } diff --git a/apps/openmw/mwrender/recastmesh.cpp b/apps/openmw/mwrender/recastmesh.cpp index 7c7c6b8eb1..5afa78cd93 100644 --- a/apps/openmw/mwrender/recastmesh.cpp +++ b/apps/openmw/mwrender/recastmesh.cpp @@ -1,11 +1,15 @@ #include "recastmesh.hpp" #include +#include +#include #include #include "vismask.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" namespace MWRender { RecastMesh::RecastMesh(const osg::ref_ptr& root, bool enabled) @@ -49,6 +53,7 @@ namespace MWRender || it->second.mRevision != tile->second->getRevision()) { const auto group = SceneUtil::createRecastMeshGroup(*tile->second, settings); + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(group, "debug"); group->setNodeMask(Mask_Debug); mRootNode->removeChild(it->second.mValue); mRootNode->addChild(group); @@ -66,6 +71,7 @@ namespace MWRender if (mGroups.count(tile.first)) continue; const auto group = SceneUtil::createRecastMeshGroup(*tile.second, settings); + MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(group, "debug"); group->setNodeMask(Mask_Debug); mGroups.emplace(tile.first, Group {tile.second->getGeneration(), tile.second->getRevision(), group}); mRootNode->addChild(group); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 65aa4a5a6b..c64bcd3383 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -74,6 +74,45 @@ namespace MWRender { + class SharedUniformStateUpdater : public SceneUtil::StateSetUpdater + { + public: + SharedUniformStateUpdater() + : mLinearFac(0.f) + { + } + + void setDefaults(osg::StateSet *stateset) override + { + stateset->addUniform(new osg::Uniform("projectionMatrix", osg::Matrixf{})); + stateset->addUniform(new osg::Uniform("linearFac", 0.f)); + } + + void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override + { + auto* uProjectionMatrix = stateset->getUniform("projectionMatrix"); + if (uProjectionMatrix) + uProjectionMatrix->set(mProjectionMatrix); + + auto* uLinearFac = stateset->getUniform("linearFac"); + if (uLinearFac) + uLinearFac->set(mLinearFac); + } + + void setProjectionMatrix(const osg::Matrixf& projectionMatrix) + { + mProjectionMatrix = projectionMatrix; + } + + void setLinearFac(float linearFac) + { + mLinearFac = linearFac; + } + + private: + osg::Matrixf mProjectionMatrix; + float mLinearFac; + }; class StateUpdater : public SceneUtil::StateSetUpdater { @@ -202,10 +241,7 @@ namespace MWRender , mFieldOfViewOverride(0.f) { auto ext = osg::GLExtensions::Get(0, false); - bool reverseZ = ext && ext->isClipControlSupported; - - if (getenv("OPENMW_DISABLE_REVERSEZ") != nullptr) - reverseZ = false; + bool reverseZ = Settings::Manager::getBool("reverse z", "Camera") && ext && ext->isClipControlSupported; if (reverseZ) Log(Debug::Info) << "Using reverse-z depth buffer"; @@ -219,7 +255,8 @@ namespace MWRender bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows") - || lightingMethod != SceneUtil::LightingMethod::FFP; + || lightingMethod != SceneUtil::LightingMethod::FFP + || reverseZ; resourceSystem->getSceneManager()->setForceShaders(forceShaders); // FIXME: calling dummy method because terrain needs to know whether lighting is clamped resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders")); @@ -370,6 +407,10 @@ namespace MWRender // Use a stub grid to avoid splitting between chunks for active grid and chunks for distant cells. mGroundcoverWorld->setActiveGrid(osg::Vec4i(0, 0, 0, 0)); } + + mPostProcessor.reset(new PostProcessor(*this, viewer, mRootNode)); + resourceSystem->getSceneManager()->setDepthFormat(mPostProcessor->getDepthFormat()); + // water goes after terrain for correct waterculling order mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); @@ -412,6 +453,9 @@ namespace MWRender mStateUpdater = new StateUpdater; sceneRoot->addUpdateCallback(mStateUpdater); + mSharedUniformStateUpdater = new SharedUniformStateUpdater; + rootNode->addUpdateCallback(mSharedUniformStateUpdater); + osg::Camera::CullingMode cullingMode = osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING; if (!Settings::Manager::getBool("small feature culling", "Camera")) @@ -433,7 +477,9 @@ namespace MWRender NifOsg::Loader::setReverseZ(reverseZ); Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models")); - mNearClip = Settings::Manager::getFloat("near clip", "Camera"); + // TODO: Near clip should not need to be bounded like this, but too small values break OSG shadow calculations CPU-side. + // See issue: #6072 + mNearClip = std::max(0.005f, Settings::Manager::getFloat("near clip", "Camera")); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); float fov = Settings::Manager::getFloat("field of view", "Camera"); mFieldOfView = std::min(std::max(1.f, fov), 179.f); @@ -462,11 +508,6 @@ namespace MWRender mRootNode->getOrCreateStateSet()->setAttributeAndModes(clipcontrol, osg::StateAttribute::ON); } - if (ext && ext->isFrameBufferObjectSupported) - mPostProcessor.reset(new PostProcessor(viewer, mRootNode)); - else - Log(Debug::Warning) << "Disabling postprocessing and using default framebuffer for rendering: FrameBufferObjects not supported"; - updateProjectionMatrix(); } @@ -1100,19 +1141,15 @@ namespace MWRender 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)); + mSharedUniformStateUpdater->setLinearFac(-mNearClip / (mViewDistance - mNearClip) - 1.f); + mSharedUniformStateUpdater->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance)); } else - mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); + mSharedUniformStateUpdater->setProjectionMatrix(mViewer->getCamera()->getProjectionMatrix()); mUniformNear->set(mNearClip); mUniformFar->set(mViewDistance); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b63841494b..cbe25addf1 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -72,6 +72,7 @@ namespace MWRender { class GroundcoverUpdater; class StateUpdater; + class SharedUniformStateUpdater; class EffectManager; class ScreenshotManager; @@ -240,9 +241,9 @@ namespace MWRender void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); bool pagingUnlockCache(); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); - - private: + void updateProjectionMatrix(); + private: void updateTextureFiltering(); void updateAmbient(); void setFogColor(const osg::Vec4f& color); @@ -296,6 +297,7 @@ namespace MWRender osg::Vec3f mCurrentCameraPos; osg::ref_ptr mStateUpdater; + osg::ref_ptr mSharedUniformStateUpdater; osg::Vec4f mAmbientColor; float mMinimumAmbientLuminance; diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index ba4282e5b6..967e282f30 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -298,7 +298,7 @@ namespace MWRender camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT,osg::Camera::PIXEL_BUFFER_RTT); - if (MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ()) + if (getReverseZ()) camera->setClearDepth(0.0); camera->setViewport(0, 0, w, h); @@ -336,7 +336,7 @@ namespace MWRender float nearClip = Settings::Manager::getFloat("near clip", "Camera"); float viewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); // each cubemap side sees 90 degrees - if (MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ()) + if (getReverseZ()) rttCamera->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspectiveInf(90.0, w/float(h), nearClip)); else rttCamera->setProjectionMatrixAsPerspective(90.0, w/float(h), nearClip, viewDistance); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 604e417a45..dd97f79eda 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -54,6 +54,7 @@ #include "vismask.hpp" #include "renderbin.hpp" +#include "util.hpp" namespace { @@ -473,7 +474,7 @@ const float CelestialBody::mDistance = 1000.0f; class Sun : public CelestialBody { public: - Sun(osg::Group* parentNode, Resource::ImageManager& imageManager, bool reverseZ) + Sun(osg::Group* parentNode, Resource::ImageManager& imageManager) : CelestialBody(parentNode, 1.0f, 1, Mask_Sun) , mUpdater(new Updater) { @@ -502,8 +503,8 @@ public: mTransform->addChild(queryNode); - mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true, reverseZ); - mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false, reverseZ); + mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryNode, true); + mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryNode, false); createSunFlash(imageManager); createSunGlare(); @@ -556,7 +557,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, bool reverseZ) + osg::ref_ptr createOcclusionQueryNode(osg::Group* parent, bool queryVisible) { osg::ref_ptr oqn = new osg::OcclusionQueryNode; oqn->setQueriesEnabled(true); @@ -594,11 +595,11 @@ private: osg::StateSet* queryStateSet = new osg::StateSet; if (queryVisible) { - auto depth = SceneUtil::createDepth(reverseZ); + auto depth = SceneUtil::createDepth(getReverseZ()); // 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. - float far = reverseZ ? 0.0 : 1.0; + double far = getReverseZ() ? 0.0 : 1.0; depth->setZNear(far); depth->setZFar(far); depth->setWriteMask(false); @@ -1188,8 +1189,7 @@ void SkyManager::create() mAtmosphereNightUpdater = new AtmosphereNightUpdater(mSceneManager->getImageManager()); atmosphereNight->addUpdateCallback(mAtmosphereNightUpdater); - bool reverseZ = mSceneManager->getReverseZ(); - mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager(), reverseZ)); + mSun.reset(new Sun(mEarlyRenderBinRoot, *mSceneManager->getImageManager())); 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)); @@ -1210,7 +1210,7 @@ void SkyManager::create() mCloudMesh2->addUpdateCallback(mCloudUpdater2); mCloudMesh2->setNodeMask(0); - auto depth = SceneUtil::createDepth(reverseZ); + auto depth = SceneUtil::createDepth(mSceneManager->getReverseZ()); depth->setWriteMask(false); mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/util.cpp b/apps/openmw/mwrender/util.cpp index e3fc48040f..5740f7df6c 100644 --- a/apps/openmw/mwrender/util.cpp +++ b/apps/openmw/mwrender/util.cpp @@ -4,10 +4,13 @@ #include #include +#include #include #include #include +#include "../mwbase/environment.hpp" + namespace MWRender { @@ -64,4 +67,9 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou node->setStateSet(stateset); } +bool getReverseZ() +{ + return MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ(); +} + } diff --git a/apps/openmw/mwrender/util.hpp b/apps/openmw/mwrender/util.hpp index a89baa22b1..c005dd9b23 100644 --- a/apps/openmw/mwrender/util.hpp +++ b/apps/openmw/mwrender/util.hpp @@ -32,6 +32,8 @@ namespace MWRender // no traverse() } }; + + bool getReverseZ(); } #endif diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 70e849b234..3c548ead23 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -285,6 +285,16 @@ namespace Resource return mReverseZ; } + void SceneManager::setDepthFormat(GLenum format) + { + mDepthFormat = format; + } + + GLenum SceneManager::getDepthFormat() const + { + return mDepthFormat; + } + void SceneManager::setAutoUseNormalMaps(bool use) { mAutoUseNormalMaps = use; diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 260fd4717f..780375f77e 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -95,6 +95,9 @@ namespace Resource void setReverseZ(bool reverseZ); bool getReverseZ() const; + void setDepthFormat(GLenum format); + GLenum getDepthFormat() const; + /// @see ShaderVisitor::setAutoUseNormalMaps void setAutoUseNormalMaps(bool use); @@ -206,6 +209,7 @@ namespace Resource SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods; bool mConvertAlphaTestToAlphaToCoverage; bool mReverseZ; + GLenum mDepthFormat; osg::ref_ptr mInstanceCache; diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index bbda5442bf..0004045dc8 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -28,8 +28,6 @@ #include #include "shadowsbin.hpp" -#include - namespace { using namespace osgShadow; @@ -350,11 +348,6 @@ 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) : @@ -988,9 +981,6 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv) return; } - osg::Matrix shadowProj; - cv.getCurrentCamera()->getUserValue("shadowProj", shadowProj); - ViewDependentData* vdd = getViewDependentData(&cv); if (!vdd) @@ -1006,41 +996,34 @@ 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 (_reverseZ) + if (cachedNearFarMode==osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { - cv.getCurrentCamera()->getUserValue("near", minZNear); - cv.getCurrentCamera()->getUserValue("far", maxZFar); - orthographicViewFrustum = isOrthographicViewFrustum(shadowProj); + 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="<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) + { + osg::ref_ptr clipcontrol = new osg::ClipControl(osg::ClipControl::LOWER_LEFT, osg::ClipControl::NEGATIVE_ONE_TO_ONE); _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/shadow.cpp b/components/sceneutil/shadow.cpp index 32c2edeccd..7ca15e612d 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -95,8 +95,8 @@ namespace SceneUtil } 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) + : mReverseZ(reverseZ) + , mShadowedScene(new osgShadow::ShadowedScene) , mShadowTechnique(new MWShadowTechnique) , mOutdoorShadowCastingMask(outdoorShadowCastingMask) , mIndoorShadowCastingMask(indoorShadowCastingMask) diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index 79fe5d13c5..1f79469a19 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -314,8 +315,7 @@ bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg:: osg::ref_ptr createDepth(bool reverseZ) { - static osg::Depth::Function func = reverseZ ? osg::Depth::GEQUAL : osg::Depth::LEQUAL; - return new osg::Depth(func); + return new osg::Depth(reverseZ ? osg::Depth::GEQUAL : osg::Depth::LEQUAL); } osg::Matrix getReversedZProjectionMatrixAsPerspectiveInf(double fov, double aspect, double near) @@ -350,5 +350,9 @@ osg::Matrix getReversedZProjectionMatrixAsOrtho(double left, double right, doubl ); } +bool isFloatingPointDepthFormat(GLenum format) +{ + return format == GL_DEPTH_COMPONENT32F || format == GL_DEPTH_COMPONENT32F_NV; +} } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index 69e66bb8b3..bd4df5441a 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -50,7 +50,7 @@ namespace SceneUtil }; // Allows camera to render to a color and floating point depth texture with a multisampled framebuffer. - // Must be set on a cameras cull callback. + // Must be set on a camera's cull callback. // When the depth texture isn't needed as a sampler, use osg::Camera::attach(osg::Camera::DEPTH_COMPONENT, GL_DEPTH_COMPONENT32F) instead. // If multisampling is not being used on the color buffer attachment, use the osg::Camera::attach() method. class AttachMultisampledDepthColorCallback : public osg::NodeCallback @@ -99,6 +99,8 @@ namespace SceneUtil // 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); + // Returns true if the GL format is a floating point depth format + bool isFloatingPointDepthFormat(GLenum format); } #endif diff --git a/components/terrain/cellborder.cpp b/components/terrain/cellborder.cpp index 47c567f544..c34ed9ab0a 100644 --- a/components/terrain/cellborder.cpp +++ b/components/terrain/cellborder.cpp @@ -8,13 +8,16 @@ #include "world.hpp" #include "../esm/loadland.hpp" +#include + namespace Terrain { -CellBorder::CellBorder(Terrain::World *world, osg::Group *root, int borderMask): - mWorld(world), - mRoot(root), - mBorderMask(borderMask) +CellBorder::CellBorder(Terrain::World *world, osg::Group *root, int borderMask, Resource::SceneManager* sceneManager) + : mWorld(world) + , mSceneManager(sceneManager) + , mRoot(root) + , mBorderMask(borderMask) { } @@ -73,6 +76,8 @@ void CellBorder::createCellBorderGeometry(int x, int y) polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); stateSet->setAttributeAndModes(polygonmode,osg::StateAttribute::ON); + mSceneManager->recreateShaders(borderGeode, "debug"); + borderGeode->setNodeMask(mBorderMask); mRoot->addChild(borderGeode); diff --git a/components/terrain/cellborder.hpp b/components/terrain/cellborder.hpp index 908cdea097..ee8fd72593 100644 --- a/components/terrain/cellborder.hpp +++ b/components/terrain/cellborder.hpp @@ -4,6 +4,11 @@ #include #include +namespace Resource +{ + class SceneManager; +} + namespace Terrain { class World; @@ -16,7 +21,7 @@ namespace Terrain public: typedef std::map, osg::ref_ptr > CellGrid; - CellBorder(Terrain::World *world, osg::Group *root, int borderMask); + CellBorder(Terrain::World *world, osg::Group *root, int borderMask, Resource::SceneManager* sceneManager); void createCellBorderGeometry(int x, int y); void destroyCellBorderGeometry(int x, int y); @@ -28,6 +33,7 @@ namespace Terrain protected: Terrain::World *mWorld; + Resource::SceneManager* mSceneManager; osg::Group *mRoot; CellGrid mCellBorderNodes; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index d1581724e0..17a51913e5 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "storage.hpp" #include "texturemanager.hpp" @@ -43,7 +44,7 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer)); mChunkManager->setNodeMask(nodeMask); - mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask)); + mCellBorder.reset(new CellBorder(this,mTerrainRoot.get(),borderMask,mResourceSystem->getSceneManager())); mResourceSystem->addResourceManager(mChunkManager.get()); mResourceSystem->addResourceManager(mTextureManager.get()); diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index a880a2b62d..fe6217b22b 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -5,8 +5,8 @@ near clip --------- :Type: floating point -:Range: > 0 -:Default: 3.0 +:Range: >= 0.005 +:Default: 1.0 This setting controls the distance to the near clipping plane. The value must be greater than zero. Values greater than approximately 18.0 will occasionally clip objects in the world in front of the character. @@ -235,3 +235,23 @@ Maximum roll angle in degrees. This setting can only be configured by editing the settings configuration file. +reverse z +--------- + +:Type: boolean +:Range: True/False +:Default: True + +Enables a reverse-z depth buffer in which the depth range is reversed. This +allows for small :ref:`near clip` values and removes almost all z-fighting with +terrain and even tightly coupled meshes at extreme view distances. For this to +be useful, a floating point depth buffer is required. These features require +driver and hardware support, but should work on any semi-modern desktop hardware +through OpenGL extensions. The exception is macOS, which has since dropped +development of OpenGL drivers. If unsupported, this setting has no effect. + +Note, this will force OpenMW to use shaders as if :ref:`force shaders` was enabled. +The performance impact of this feature should be negligible. + +This setting can only be configured by editing the settings configuration file. + diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 36eca7f1ab..4943ce969b 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -15,7 +15,7 @@ [Camera] # Near clipping plane (>0.0, e.g. 0.01 to 18.0). -near clip = 3 +near clip = 1 # Cull objects that occupy less than 'small feature culling pixel size' on the screen. small feature culling = true @@ -67,6 +67,9 @@ head bobbing height = 3.0 # Maximum camera roll angle (degrees) head bobbing roll = 0.2 +# Reverse the depth range from [0,1] to [1,0]. +reverse z = true + [Cells] # Preload cells in a background thread. All settings starting with 'preload' have no effect unless this is enabled. diff --git a/files/shaders/CMakeLists.txt b/files/shaders/CMakeLists.txt index 5e049b8a3f..04446d2982 100644 --- a/files/shaders/CMakeLists.txt +++ b/files/shaders/CMakeLists.txt @@ -34,6 +34,8 @@ set(SHADER_FILES nv_nolighting_fragment.glsl gui_vertex.glsl gui_fragment.glsl + debug_vertex.glsl + debug_fragment.glsl ) copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}") diff --git a/files/shaders/debug_fragment.glsl b/files/shaders/debug_fragment.glsl new file mode 100644 index 0000000000..263d4e1f24 --- /dev/null +++ b/files/shaders/debug_fragment.glsl @@ -0,0 +1,8 @@ +#version 120 + +varying vec4 passColor; + +void main() +{ + gl_FragData[0] = passColor; +} diff --git a/files/shaders/debug_vertex.glsl b/files/shaders/debug_vertex.glsl new file mode 100644 index 0000000000..f68c3d6459 --- /dev/null +++ b/files/shaders/debug_vertex.glsl @@ -0,0 +1,12 @@ +#version 120 + +uniform mat4 projectionMatrix; + +varying vec4 passColor; + +void main() +{ + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); + + passColor = gl_Color; +} diff --git a/files/shaders/depth.glsl b/files/shaders/depth.glsl index 0adde7a2a7..aa6f54b99d 100644 --- a/files/shaders/depth.glsl +++ b/files/shaders/depth.glsl @@ -2,11 +2,11 @@ uniform float linearFac; #endif -float getLinearDepth(in vec4 viewPos) +float getLinearDepth(in float z, in float viewZ) { #if @reverseZ - return linearFac*viewPos.z; + return linearFac*viewZ; #else - return gl_Position.z; + return z; #endif } \ No newline at end of file diff --git a/files/shaders/groundcover_vertex.glsl b/files/shaders/groundcover_vertex.glsl index 94586734b6..c8db4be000 100644 --- a/files/shaders/groundcover_vertex.glsl +++ b/files/shaders/groundcover_vertex.glsl @@ -46,6 +46,7 @@ uniform mat4 osg_ViewMatrixInverse; uniform mat4 osg_ViewMatrix; uniform float windSpeed; uniform vec3 playerPos; +uniform mat4 projectionMatrix; #if @groundcoverStompMode == 0 #else @@ -142,9 +143,9 @@ void main(void) if (length(gl_ModelViewMatrix * vec4(position, 1.0)) > @groundcoverFadeEnd) gl_Position = vec4(0.0, 0.0, 0.0, 1.0); else - gl_Position = gl_ProjectionMatrix * viewPos; + gl_Position = projectionMatrix * viewPos; - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #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 1c5483dcda..34d5415250 100644 --- a/files/shaders/nv_default_vertex.glsl +++ b/files/shaders/nv_default_vertex.glsl @@ -8,6 +8,8 @@ #extension GL_EXT_gpu_shader4: require #endif +uniform mat4 projectionMatrix; + #if @diffuseMap varying vec2 diffuseMapUV; #endif @@ -36,12 +38,12 @@ varying vec3 passNormal; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #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 02c28eb5e3..617f0489a4 100644 --- a/files/shaders/nv_nolighting_vertex.glsl +++ b/files/shaders/nv_nolighting_vertex.glsl @@ -1,5 +1,7 @@ #version 120 +uniform mat4 projectionMatrix; + #if @diffuseMap varying vec2 diffuseMapUV; #endif @@ -21,14 +23,14 @@ varying float passFalloff; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; #if @radialFog euclideanDepth = length(viewPos.xyz); #else - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #endif #if @diffuseMap diff --git a/files/shaders/objects_vertex.glsl b/files/shaders/objects_vertex.glsl index 34484ebd41..969fe5903e 100644 --- a/files/shaders/objects_vertex.glsl +++ b/files/shaders/objects_vertex.glsl @@ -8,6 +8,8 @@ #extension GL_EXT_gpu_shader4: require #endif +uniform mat4 projectionMatrix; + #if @diffuseMap varying vec2 diffuseMapUV; #endif @@ -66,12 +68,13 @@ varying vec3 passNormal; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); + gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #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 221e0c27a9..b46b299e2b 100644 --- a/files/shaders/terrain_vertex.glsl +++ b/files/shaders/terrain_vertex.glsl @@ -8,6 +8,8 @@ #extension GL_EXT_gpu_shader4: require #endif +uniform mat4 projectionMatrix; + varying vec2 uv; varying float euclideanDepth; varying float linearDepth; @@ -29,12 +31,12 @@ varying vec3 passNormal; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex); gl_ClipVertex = viewPos; euclideanDepth = length(viewPos.xyz); - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); #if (!PER_PIXEL_LIGHTING || @shadows_enabled) vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz); diff --git a/files/shaders/water_vertex.glsl b/files/shaders/water_vertex.glsl index 9513001847..8e506a57f8 100644 --- a/files/shaders/water_vertex.glsl +++ b/files/shaders/water_vertex.glsl @@ -1,5 +1,7 @@ #version 120 - + +uniform mat4 projectionMatrix; + varying vec3 screenCoordsPassthrough; varying vec4 position; varying float linearDepth; @@ -9,7 +11,7 @@ varying float linearDepth; void main(void) { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex); mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.0, @@ -22,7 +24,7 @@ void main(void) position = gl_Vertex; vec4 viewPos = gl_ModelViewMatrix * gl_Vertex; - linearDepth = getLinearDepth(viewPos); + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); setupShadowCoords(viewPos, normalize((gl_NormalMatrix * gl_Normal).xyz)); } From 09e03fde2e689fd1dcff054a16a761128d36ffff Mon Sep 17 00:00:00 2001 From: "glassmancody.info" Date: Wed, 4 Aug 2021 17:49:57 -0700 Subject: [PATCH 5/5] refactor and fix wobbly shores --- CHANGELOG.md | 1 + apps/opencs/model/world/data.cpp | 1 + apps/opencs/view/render/cellwater.cpp | 2 +- apps/openmw/mwgui/bookpage.cpp | 5 +- apps/openmw/mwgui/savegamedialog.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 1 + apps/openmw/mwrender/bulletdebugdraw.cpp | 11 ++- apps/openmw/mwrender/globalmap.cpp | 2 +- apps/openmw/mwrender/localmap.cpp | 12 +-- apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwrender/objectpaging.cpp | 1 - apps/openmw/mwrender/postprocessor.cpp | 34 ++++--- apps/openmw/mwrender/postprocessor.hpp | 18 ++-- apps/openmw/mwrender/renderingmanager.cpp | 54 +++++++---- apps/openmw/mwrender/renderingmanager.hpp | 8 +- apps/openmw/mwrender/ripplesimulation.cpp | 6 +- apps/openmw/mwrender/screenshotmanager.cpp | 16 ++-- apps/openmw/mwrender/sky.cpp | 6 +- apps/openmw/mwrender/util.cpp | 8 -- apps/openmw/mwrender/util.hpp | 2 - apps/openmw/mwrender/water.cpp | 8 +- components/nifosg/nifloader.cpp | 14 +-- components/nifosg/nifloader.hpp | 4 - components/resource/scenemanager.cpp | 10 -- components/resource/scenemanager.hpp | 4 - components/sceneutil/agentpath.cpp | 6 ++ components/sceneutil/mwshadowtechnique.cpp | 15 +-- components/sceneutil/mwshadowtechnique.hpp | 6 -- components/sceneutil/navmesh.cpp | 6 ++ components/sceneutil/optimizer.cpp | 3 +- components/sceneutil/optimizer.hpp | 13 +-- components/sceneutil/pathgridutil.cpp | 6 ++ components/sceneutil/recastmesh.cpp | 6 ++ components/sceneutil/shadow.cpp | 18 +--- components/sceneutil/shadow.hpp | 5 +- components/sceneutil/util.cpp | 106 ++++++++++++++++----- components/sceneutil/util.hpp | 27 ++---- components/sceneutil/waterutil.cpp | 4 +- components/sceneutil/waterutil.hpp | 2 +- components/terrain/chunkmanager.cpp | 4 +- components/terrain/material.cpp | 16 ++-- components/terrain/material.hpp | 6 +- files/settings-default.cfg | 2 +- files/shaders/debug_fragment.glsl | 4 +- files/shaders/water_fragment.glsl | 8 +- 45 files changed, 256 insertions(+), 239 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e136d707a..801d05d357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Feature #6017: Separate persistent and temporary cell references when saving Feature #6032: Reverse-z depth buffer Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly + Feature #6199: Support FBO Rendering 0.47.0 ------ diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 192602290d..5e8c548368 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -84,6 +84,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind defines["radialFog"] = "0"; defines["lightingModel"] = "0"; + defines["reverseZ"] = "0"; for (const auto& define : shadowDefines) defines[define.first] = define.second; mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines); diff --git a/apps/opencs/view/render/cellwater.cpp b/apps/opencs/view/render/cellwater.cpp index 485eed00fe..f8857c3afc 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, false)); + mWaterGeometry->setStateSet(SceneUtil::createSimpleWaterStateSet(Alpha, RenderBin)); // Add water texture std::string textureName = Fallback::Map::getString("Water_SurfaceTexture"); diff --git a/apps/openmw/mwgui/bookpage.cpp b/apps/openmw/mwgui/bookpage.cpp index e4ce2a9593..2f12b83572 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -8,12 +8,11 @@ #include "MyGUI_FactoryManager.h" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" -#include "../mwrender/util.hpp" - namespace MWGui { struct TypesetBookImpl; @@ -1219,7 +1218,7 @@ public: RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); - float z = MWRender::getReverseZ() ? 1.f : -1.f; + float z = SceneUtil::getReverseZ() ? 1.f : -1.f; GlyphStream glyphStream(textFormat.mFont, static_cast(mCoord.left), static_cast(mCoord.top - mViewTop), z /*mNode->getNodeDepth()*/, vertices, renderXform); diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 987d27c9a6..086135d035 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -450,7 +450,7 @@ namespace MWGui osg::ref_ptr texture (new osg::Texture2D); texture->setImage(result.getImage()); - texture->setInternalFormat(GL_RGBA); + texture->setInternalFormat(GL_RGB); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e42d606966..6eeb2d3654 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -35,6 +35,7 @@ #include #include +#include #include diff --git a/apps/openmw/mwrender/bulletdebugdraw.cpp b/apps/openmw/mwrender/bulletdebugdraw.cpp index 9039ec53ce..b008ebc6d9 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -4,13 +4,13 @@ #include #include +#include #include #include #include #include #include -#include #include "bulletdebugdraw.hpp" #include "vismask.hpp" @@ -18,8 +18,6 @@ #include #include -#include - #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -39,6 +37,7 @@ void DebugDrawer::createGeometry() { mGeometry = new osg::Geometry; mGeometry->setNodeMask(Mask_Debug); + mVertices = new osg::Vec3Array; mColors = new osg::Vec4Array; @@ -50,8 +49,10 @@ void DebugDrawer::createGeometry() mGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); mGeometry->setDataVariance(osg::Object::DYNAMIC); mGeometry->addPrimitiveSet(mDrawArrays); - // make this friendly to recreateShaders() - mGeometry->setStateSet(new osg::StateSet); + + osg::ref_ptr material = new osg::Material; + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + mGeometry->getOrCreateStateSet()->setAttribute(material); mParentNode->addChild(mGeometry); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 2f160933fd..7d7c6e45df 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -325,7 +325,7 @@ namespace MWRender if (texture) { osg::ref_ptr geom = createTexturedQuad(srcLeft, srcTop, srcRight, srcBottom); - auto depth = SceneUtil::createDepth(getReverseZ()); + auto depth = SceneUtil::createDepth(); depth->setWriteMask(0); osg::StateSet* stateset = geom->getOrCreateStateSet(); stateset->setAttribute(depth); diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 72f2828ace..02f59e0ed0 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -178,7 +178,7 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f { osg::ref_ptr camera (new osg::Camera); - if (getReverseZ()) + if (SceneUtil::getReverseZ()) 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); @@ -201,12 +201,10 @@ 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 (getReverseZ()) - { - camera->setClearDepth(0.0); - auto depth = SceneUtil::createDepth(true); - stateset->setAttributeAndModes(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); - } + if (SceneUtil::getReverseZ()) + stateset->setAttributeAndModes(SceneUtil::createDepth(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); + + SceneUtil::setCameraClearDepth(camera); stateset->addUniform(new osg::Uniform("projectionMatrix", static_cast(camera->getProjectionMatrix())), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 64dc9ecfe0..12ed88e55d 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -373,7 +373,7 @@ class DepthClearCallback : public osgUtil::RenderBin::DrawCallback public: DepthClearCallback() { - mDepth = SceneUtil::createDepth(getReverseZ()); + mDepth = SceneUtil::createDepth(); mDepth->setWriteMask(true); } diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 709c67bc2d..aae8f2e4f1 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -661,7 +661,6 @@ 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 index a412ca8cfa..eff7d46892 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -36,10 +36,10 @@ namespace class CullCallback : public osg::NodeCallback { public: - CullCallback(MWRender::PostProcessor* postProcessor) - : mPostProcessor(postProcessor) - , mLastFrameNumber(0) - {} + CullCallback() + : mLastFrameNumber(0) + { + } void operator()(osg::Node* node, osg::NodeVisitor* nv) override { @@ -49,14 +49,24 @@ namespace if (frame != mLastFrameNumber) { mLastFrameNumber = frame; - if (!mPostProcessor->getMsaaFbo()) + + MWRender::PostProcessor* postProcessor = dynamic_cast(nv->asCullVisitor()->getCurrentCamera()->getUserData()); + + if (!postProcessor) { - renderStage->setFrameBufferObject(mPostProcessor->getFbo()); + Log(Debug::Error) << "Failed retrieving user data for master camera: FBO setup failed"; + traverse(node, nv); + return; + } + + if (!postProcessor->getMsaaFbo()) + { + renderStage->setFrameBufferObject(postProcessor->getFbo()); } else { - renderStage->setMultisampleResolveFramebufferObject(mPostProcessor->getFbo()); - renderStage->setFrameBufferObject(mPostProcessor->getMsaaFbo()); + renderStage->setMultisampleResolveFramebufferObject(postProcessor->getFbo()); + renderStage->setFrameBufferObject(postProcessor->getMsaaFbo()); } } @@ -64,7 +74,6 @@ namespace } private: - MWRender::PostProcessor* mPostProcessor; unsigned int mLastFrameNumber; }; @@ -117,7 +126,7 @@ namespace MWRender else { // TODO: Once we have post-processing implemented we want to skip this return and continue with setup. - // Rendering to a FBO to fullscreen geometry has overhead (especially when MSAA is enabled) and there are no + // Rendering to a FBO to fullscreen geometry has overhead (especially when MSAA is enabled) and there are no // benefits if no floating point depth formats are supported. mDepthFormat = GL_DEPTH_COMPONENT24; Log(Debug::Warning) << errPreamble << "'GL_ARB_depth_buffer_float' and 'GL_NV_depth_buffer_float' unsupported."; @@ -136,12 +145,13 @@ namespace MWRender // We need to manually set the FBO and resolve FBO during the cull callback. If we were using a separate // RTT camera this would not be needed. - mViewer->getCamera()->addCullCallback(new CullCallback(this)); + mViewer->getCamera()->addCullCallback(new CullCallback); 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)); + mViewer->getCamera()->setUserData(this); } void PostProcessor::resize(int width, int height, bool init) @@ -157,8 +167,6 @@ namespace MWRender mFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(mSceneTex)); mFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(mDepthTex)); - mViewer->getCamera()->setUserData(mFbo); - // 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. diff --git a/apps/openmw/mwrender/postprocessor.hpp b/apps/openmw/mwrender/postprocessor.hpp index 5be58f33ac..ad922c1cbc 100644 --- a/apps/openmw/mwrender/postprocessor.hpp +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -1,16 +1,12 @@ #ifndef OPENMW_MWRENDER_POSTPROCESSOR_H #define OPENMW_MWRENDER_POSTPROCESSOR_H +#include +#include +#include +#include #include -namespace osg -{ - class Texture2D; - class Group; - class FrameBufferObject; - class Camera; -} - namespace osgViewer { class Viewer; @@ -20,7 +16,7 @@ namespace MWRender { class RenderingManager; - class PostProcessor + class PostProcessor : public osg::Referenced { public: PostProcessor(RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode); @@ -33,6 +29,8 @@ namespace MWRender void resize(int width, int height, bool init=false); private: + void createTexturesAndCamera(int width, int height); + osgViewer::Viewer* mViewer; osg::ref_ptr mRootNode; osg::ref_ptr mHUDCamera; @@ -45,8 +43,6 @@ namespace MWRender int mDepthFormat; - void createTexturesAndCamera(int width, int height); - RenderingManager& mRendering; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c64bcd3383..5ffc54f3f8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -79,6 +79,8 @@ namespace MWRender public: SharedUniformStateUpdater() : mLinearFac(0.f) + , mNear(0.f) + , mFar(0.f) { } @@ -86,6 +88,8 @@ namespace MWRender { stateset->addUniform(new osg::Uniform("projectionMatrix", osg::Matrixf{})); stateset->addUniform(new osg::Uniform("linearFac", 0.f)); + stateset->addUniform(new osg::Uniform("near", 0.f)); + stateset->addUniform(new osg::Uniform("far", 0.f)); } void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override @@ -97,6 +101,15 @@ namespace MWRender auto* uLinearFac = stateset->getUniform("linearFac"); if (uLinearFac) uLinearFac->set(mLinearFac); + + auto* uNear = stateset->getUniform("near"); + if (uNear) + uNear->set(mNear); + + auto* uFar = stateset->getUniform("far"); + if (uFar) + uFar->set(mFar); + } void setProjectionMatrix(const osg::Matrixf& projectionMatrix) @@ -109,9 +122,21 @@ namespace MWRender mLinearFac = linearFac; } + void setNear(float near) + { + mNear = near; + } + + void setFar(float far) + { + mFar = far; + } + private: osg::Matrixf mProjectionMatrix; float mLinearFac; + float mNear; + float mFar; }; class StateUpdater : public SceneUtil::StateSetUpdater @@ -240,8 +265,7 @@ namespace MWRender , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) { - auto ext = osg::GLExtensions::Get(0, false); - bool reverseZ = Settings::Manager::getBool("reverse z", "Camera") && ext && ext->isClipControlSupported; + bool reverseZ = SceneUtil::getReverseZ(); if (reverseZ) Log(Debug::Info) << "Using reverse-z depth buffer"; @@ -267,7 +291,6 @@ 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); @@ -294,7 +317,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(), reverseZ)); + mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager())); Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines(); Shader::ShaderManager::DefineMap lightDefines = sceneRoot->getLightDefines(); @@ -408,9 +431,12 @@ namespace MWRender mGroundcoverWorld->setActiveGrid(osg::Vec4i(0, 0, 0, 0)); } - mPostProcessor.reset(new PostProcessor(*this, viewer, mRootNode)); + mPostProcessor = new PostProcessor(*this, viewer, mRootNode); resourceSystem->getSceneManager()->setDepthFormat(mPostProcessor->getDepthFormat()); + if (reverseZ && !SceneUtil::isFloatingPointDepthFormat(mPostProcessor->getDepthFormat())) + Log(Debug::Warning) << "Floating point depth format not in use but reverse-z buffer is enabled, consider disabling it."; + // water goes after terrain for correct waterculling order mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath)); @@ -474,7 +500,6 @@ 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")); // TODO: Near clip should not need to be bounded like this, but too small values break OSG shadow calculations CPU-side. @@ -487,8 +512,6 @@ namespace MWRender mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f); mStateUpdater->setFogEnd(mViewDistance); - mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip)); - mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance)); mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("simpleWater", false)); // Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it @@ -496,18 +519,15 @@ namespace MWRender // The transparent renderbin sets alpha testing on because that was faster on old GPUs. It's now slower and breaks things. mRootNode->getOrCreateStateSet()->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF); - 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(SceneUtil::createDepth(), osg::StateAttribute::ON); mRootNode->getOrCreateStateSet()->setAttributeAndModes(clipcontrol, osg::StateAttribute::ON); } + SceneUtil::setCameraClearDepth(mViewer->getCamera()); + updateProjectionMatrix(); } @@ -1143,7 +1163,7 @@ namespace MWRender mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); - if (mResourceSystem->getSceneManager()->getReverseZ()) + if (SceneUtil::getReverseZ()) { mSharedUniformStateUpdater->setLinearFac(-mNearClip / (mViewDistance - mNearClip) - 1.f); mSharedUniformStateUpdater->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance)); @@ -1151,8 +1171,8 @@ namespace MWRender else mSharedUniformStateUpdater->setProjectionMatrix(mViewer->getCamera()->getProjectionMatrix()); - mUniformNear->set(mNearClip); - mUniformFar->set(mViewDistance); + mSharedUniformStateUpdater->setNear(mNearClip); + mSharedUniformStateUpdater->setFar(mViewDistance); // Since our fog is not radial yet, we should take FOV in account, otherwise terrain near viewing distance may disappear. // Limit FOV here just for sure, otherwise viewing distance can be too high. diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index cbe25addf1..d6d7e74634 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -110,9 +110,6 @@ namespace MWRender SceneUtil::UnrefQueue* getUnrefQueue(); Terrain::World* getTerrain(); - osg::Uniform* mUniformNear; - osg::Uniform* mUniformFar; - void preloadCommonAssets(); double getReferenceTime() const; @@ -241,8 +238,9 @@ namespace MWRender void pagingBlacklistObject(int type, const MWWorld::ConstPtr &ptr); bool pagingUnlockCache(); void getPagedRefnums(const osg::Vec4i &activeGrid, std::set &out); - + void updateProjectionMatrix(); + private: void updateTextureFiltering(); void updateAmbient(); @@ -289,7 +287,7 @@ namespace MWRender std::unique_ptr mScreenshotManager; std::unique_ptr mEffectManager; std::unique_ptr mShadowManager; - std::unique_ptr mPostProcessor; + osg::ref_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 c7deac51de..fdfc3db63d 100644 --- a/apps/openmw/mwrender/ripplesimulation.cpp +++ b/apps/openmw/mwrender/ripplesimulation.cpp @@ -56,13 +56,13 @@ namespace stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); stateset->setTextureAttributeAndModes(0, textures[0], osg::StateAttribute::ON); - auto depth = SceneUtil::createDepth(resourceSystem->getSceneManager()->getReverseZ()); + auto depth = SceneUtil::createDepth(); depth->setWriteMask(false); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); osg::ref_ptr polygonOffset (new osg::PolygonOffset); - polygonOffset->setUnits(-1); - polygonOffset->setFactor(-1); + polygonOffset->setUnits(SceneUtil::getReverseZ() ? 1 : -1); + polygonOffset->setFactor(SceneUtil::getReverseZ() ? 1 : -1); stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp index 967e282f30..96a8a9450a 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -23,6 +23,7 @@ #include "util.hpp" #include "vismask.hpp" #include "water.hpp" +#include "postprocessor.hpp" namespace MWRender { @@ -92,14 +93,13 @@ namespace MWRender // Ensure we are reading from the resolved framebuffer and not the multisampled render buffer when in use. // glReadPixel() cannot read from multisampled targets. + PostProcessor* postProcessor = dynamic_cast(renderInfo.getCurrentCamera()->getUserData()); - osg::FrameBufferObject* fbo = dynamic_cast(renderInfo.getCurrentCamera()->getUserData()); - - if (fbo) + if (postProcessor && postProcessor->getFbo() && postProcessor->getMsaaFbo()) { osg::GLExtensions* ext = osg::GLExtensions::Get(renderInfo.getContextID(), false); if (ext) - ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo->getHandle(renderInfo.getContextID())); + ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, postProcessor->getFbo()->getHandle(renderInfo.getContextID())); } mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE); @@ -297,12 +297,10 @@ namespace MWRender camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT,osg::Camera::PIXEL_BUFFER_RTT); - - if (getReverseZ()) - camera->setClearDepth(0.0); - camera->setViewport(0, 0, w, h); + SceneUtil::setCameraClearDepth(camera); + osg::ref_ptr texture (new osg::Texture2D); texture->setInternalFormat(GL_RGB); texture->setTextureSize(w,h); @@ -336,7 +334,7 @@ namespace MWRender float nearClip = Settings::Manager::getFloat("near clip", "Camera"); float viewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); // each cubemap side sees 90 degrees - if (getReverseZ()) + if (SceneUtil::getReverseZ()) rttCamera->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspectiveInf(90.0, w/float(h), nearClip)); else rttCamera->setProjectionMatrixAsPerspective(90.0, w/float(h), nearClip, viewDistance); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index dd97f79eda..2b58730fe1 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -595,11 +595,11 @@ private: osg::StateSet* queryStateSet = new osg::StateSet; if (queryVisible) { - auto depth = SceneUtil::createDepth(getReverseZ()); + auto depth = SceneUtil::createDepth(); // 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. - double far = getReverseZ() ? 0.0 : 1.0; + double far = SceneUtil::getReverseZ() ? 0.0 : 1.0; depth->setZNear(far); depth->setZFar(far); depth->setWriteMask(false); @@ -1210,7 +1210,7 @@ void SkyManager::create() mCloudMesh2->addUpdateCallback(mCloudUpdater2); mCloudMesh2->setNodeMask(0); - auto depth = SceneUtil::createDepth(mSceneManager->getReverseZ()); + auto depth = SceneUtil::createDepth(); depth->setWriteMask(false); mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(depth, osg::StateAttribute::ON); mEarlyRenderBinRoot->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON); diff --git a/apps/openmw/mwrender/util.cpp b/apps/openmw/mwrender/util.cpp index 5740f7df6c..e3fc48040f 100644 --- a/apps/openmw/mwrender/util.cpp +++ b/apps/openmw/mwrender/util.cpp @@ -4,13 +4,10 @@ #include #include -#include #include #include #include -#include "../mwbase/environment.hpp" - namespace MWRender { @@ -67,9 +64,4 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou node->setStateSet(stateset); } -bool getReverseZ() -{ - return MWBase::Environment::get().getResourceSystem()->getSceneManager()->getReverseZ(); -} - } diff --git a/apps/openmw/mwrender/util.hpp b/apps/openmw/mwrender/util.hpp index c005dd9b23..a89baa22b1 100644 --- a/apps/openmw/mwrender/util.hpp +++ b/apps/openmw/mwrender/util.hpp @@ -32,8 +32,6 @@ namespace MWRender // no traverse() } }; - - bool getReverseZ(); } #endif diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 1318921688..da26595c8c 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -273,7 +273,7 @@ public: void setDefaults(osg::Camera* camera) override { - camera->setClearDepth(1.0); + SceneUtil::setCameraClearDepth(camera); camera->setReferenceFrame(osg::Camera::RELATIVE_RF); camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); camera->setName("RefractionCamera"); @@ -339,7 +339,7 @@ public: void setDefaults(osg::Camera* camera) override { - camera->setClearDepth(1.0); + SceneUtil::setCameraClearDepth(camera); camera->setReferenceFrame(osg::Camera::RELATIVE_RF); camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); camera->setName("ReflectionCamera"); @@ -546,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, mResourceSystem->getSceneManager()->getReverseZ()); + osg::ref_ptr stateset = SceneUtil::createSimpleWaterStateSet(alpha, MWRender::RenderBin_Water); node->setStateSet(stateset); node->setUpdateCallback(nullptr); @@ -616,7 +616,7 @@ public: { stateset->setMode(GL_BLEND, osg::StateAttribute::ON); stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin"); - osg::ref_ptr depth(new osg::Depth); + osg::ref_ptr depth = SceneUtil::createDepth(); depth->setWriteMask(false); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 384c640388..83b9fb61c2 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -226,18 +226,6 @@ namespace NifOsg return sIntersectionDisabledNodeMask; } - bool Loader::sReverseZ = false; - - void Loader::setReverseZ(bool reverseZ) - { - sReverseZ = reverseZ; - } - - bool Loader::getReverseZ() - { - return sReverseZ; - } - class LoaderImpl { public: @@ -1835,7 +1823,7 @@ namespace NifOsg // Depth test flag stateset->setMode(GL_DEPTH_TEST, zprop->flags&1 ? osg::StateAttribute::ON : osg::StateAttribute::OFF); - auto depth = SceneUtil::createDepth(Loader::getReverseZ()); + auto depth = SceneUtil::createDepth(); // 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 cee75de6d2..8ee6b41674 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -51,14 +51,10 @@ 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 3c548ead23..ffcd4c3308 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -275,16 +275,6 @@ namespace Resource return mClampLighting; } - void SceneManager::setReverseZ(bool reverseZ) - { - mReverseZ = reverseZ; - } - - bool SceneManager::getReverseZ() const - { - return mReverseZ; - } - void SceneManager::setDepthFormat(GLenum format) { mDepthFormat = format; diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 780375f77e..9d798177a1 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -92,9 +92,6 @@ namespace Resource void setClampLighting(bool clamp); bool getClampLighting() const; - void setReverseZ(bool reverseZ); - bool getReverseZ() const; - void setDepthFormat(GLenum format); GLenum getDepthFormat() const; @@ -208,7 +205,6 @@ namespace Resource SceneUtil::LightingMethod mLightingMethod; SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods; bool mConvertAlphaTestToAlphaToCoverage; - bool mReverseZ; GLenum mDepthFormat; osg::ref_ptr mInstanceCache; diff --git a/components/sceneutil/agentpath.cpp b/components/sceneutil/agentpath.cpp index abe332f758..5f9b574e7d 100644 --- a/components/sceneutil/agentpath.cpp +++ b/components/sceneutil/agentpath.cpp @@ -1,6 +1,8 @@ #include "agentpath.hpp" #include "detourdebugdraw.hpp" +#include + #include #include @@ -65,6 +67,10 @@ namespace SceneUtil debugDraw.depthMask(true); + osg::ref_ptr material = new osg::Material; + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + group->getOrCreateStateSet()->setAttribute(material); + return group; } } diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 0004045dc8..19222eda23 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -26,6 +26,9 @@ #include #include + +#include + #include "shadowsbin.hpp" namespace { @@ -891,16 +894,6 @@ 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 @@ -1647,7 +1640,7 @@ void MWShadowTechnique::createShaders() _shadowCastingStateSet->addUniform(new osg::Uniform("alphaTestShadows", false)); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(true); - if (_reverseZ) + if (SceneUtil::getReverseZ()) { osg::ref_ptr clipcontrol = new osg::ClipControl(osg::ClipControl::LOWER_LEFT, osg::ClipControl::NEGATIVE_ONE_TO_ONE); _shadowCastingStateSet->setAttribute(clipcontrol, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index 7fde0d7dbb..de57bf1fdc 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -85,10 +85,6 @@ 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 @@ -270,8 +266,6 @@ namespace SceneUtil { float _shadowFadeStart = 0.0; - bool _reverseZ = false; - class DebugHUD final : public osg::Referenced { public: diff --git a/components/sceneutil/navmesh.cpp b/components/sceneutil/navmesh.cpp index aeb1779bd6..b0f356f089 100644 --- a/components/sceneutil/navmesh.cpp +++ b/components/sceneutil/navmesh.cpp @@ -6,6 +6,7 @@ #include #include +#include namespace SceneUtil { @@ -17,6 +18,11 @@ namespace SceneUtil navMeshQuery.init(&navMesh, settings.mMaxNavMeshQueryNodes); duDebugDrawNavMeshWithClosedList(&debugDraw, navMesh, navMeshQuery, DU_DRAWNAVMESH_OFFMESHCONS | DU_DRAWNAVMESH_CLOSEDLIST); + + osg::ref_ptr material = new osg::Material; + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + group->getOrCreateStateSet()->setAttribute(material); + return group; } } diff --git a/components/sceneutil/optimizer.cpp b/components/sceneutil/optimizer.cpp index 6bde81c12d..5fbeb681fc 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -109,7 +109,6 @@ 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); @@ -1563,7 +1562,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) } if (_alphaBlendingActive && _mergeAlphaBlending && !geom->getStateSet()) { - auto d = createDepth(_reverseZ); + auto d = createDepth(); d->setWriteMask(0); geom->getOrCreateStateSet()->setAttribute(d); } diff --git a/components/sceneutil/optimizer.hpp b/components/sceneutil/optimizer.hpp index 9f9c788445..2d6293e231 100644 --- a/components/sceneutil/optimizer.hpp +++ b/components/sceneutil/optimizer.hpp @@ -65,7 +65,7 @@ class Optimizer public: - Optimizer() : _mergeAlphaBlending(false), _reverseZ(false) {} + Optimizer() : _mergeAlphaBlending(false) {} virtual ~Optimizer() {} enum OptimizationOptions @@ -119,7 +119,6 @@ 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.*/ @@ -258,7 +257,6 @@ class Optimizer osg::Vec3f _viewPoint; bool _mergeAlphaBlending; - bool _reverseZ; public: @@ -379,18 +377,12 @@ class Optimizer /// default to traversing all children. MergeGeometryVisitor(Optimizer* optimizer=0) : BaseOptimizerVisitor(optimizer, MERGE_GEOMETRY), - _targetMaximumNumberOfVertices(10000), _alphaBlendingActive(false), _mergeAlphaBlending(false), _reverseZ(false) {} + _targetMaximumNumberOfVertices(10000), _alphaBlendingActive(false), _mergeAlphaBlending(false) {} void setMergeAlphaBlending(bool merge) { _mergeAlphaBlending = merge; } - - void setReverseZ(bool reverseZ) - { - _reverseZ = reverseZ; - } - void setViewPoint(const osg::Vec3f& viewPoint) { _viewPoint = viewPoint; @@ -429,7 +421,6 @@ class Optimizer std::vector _stateSetStack; bool _alphaBlendingActive; bool _mergeAlphaBlending; - bool _reverseZ; osg::Vec3f _viewPoint; }; diff --git a/components/sceneutil/pathgridutil.cpp b/components/sceneutil/pathgridutil.cpp index f37a8ba597..9ebdef61b0 100644 --- a/components/sceneutil/pathgridutil.cpp +++ b/components/sceneutil/pathgridutil.cpp @@ -1,6 +1,7 @@ #include "pathgridutil.hpp" #include +#include #include @@ -174,6 +175,11 @@ namespace SceneUtil gridGeometry->addPrimitiveSet(lineIndices); gridGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); } + + osg::ref_ptr material = new osg::Material; + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + gridGeometry->getOrCreateStateSet()->setAttribute(material); + return gridGeometry; } diff --git a/components/sceneutil/recastmesh.cpp b/components/sceneutil/recastmesh.cpp index 85f3e7177b..8d6d47c2b6 100644 --- a/components/sceneutil/recastmesh.cpp +++ b/components/sceneutil/recastmesh.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -65,6 +66,11 @@ namespace SceneUtil const auto texScale = 1.0f / (settings.mCellSize * 10.0f); duDebugDrawTriMesh(&debugDraw, vertices.data(), static_cast(vertices.size() / 3), indices.data(), normals.data(), static_cast(indices.size() / 3), nullptr, texScale); + + osg::ref_ptr material = new osg::Material; + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + group->getOrCreateStateSet()->setAttribute(material); + return group; } } diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index 7ca15e612d..3244dc672a 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -94,12 +94,10 @@ namespace SceneUtil } } - ShadowManager::ShadowManager(osg::ref_ptr sceneRoot, osg::ref_ptr rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager, bool reverseZ) - : mReverseZ(reverseZ) - , 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) : mShadowedScene(new osgShadow::ShadowedScene), + mShadowTechnique(new MWShadowTechnique), + mOutdoorShadowCastingMask(outdoorShadowCastingMask), + mIndoorShadowCastingMask(indoorShadowCastingMask) { mShadowedScene->setShadowTechnique(mShadowTechnique); @@ -110,9 +108,6 @@ namespace SceneUtil mShadowSettings = mShadowedScene->getShadowSettings(); setupShadowSettings(); - if (mReverseZ) - mShadowTechnique->enableReverseZ(); - mShadowTechnique->setupCastingShader(shaderManager); enableOutdoorMode(); @@ -185,9 +180,4 @@ 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 8092f77f93..c823ecf860 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, bool reverseZ); + ShadowManager(osg::ref_ptr sceneRoot, osg::ref_ptr rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager); void setupShadowSettings(); @@ -26,11 +26,8 @@ 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 1f79469a19..02cd9295e7 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -4,16 +4,31 @@ #include #include +#include + #include #include #include #include #include -#include #include #include #include +#include + +namespace +{ + +bool isReverseZSupported() +{ + if (!Settings::Manager::mDefaultSettings.count({"Camera", "reverse z"})) + return false; + auto ext = osg::GLExtensions::Get(0, false); + return Settings::Manager::getBool("reverse z", "Camera") && ext && ext->isClipControlSupported; +} + +} namespace SceneUtil { @@ -151,32 +166,42 @@ void GlowUpdater::setDuration(float duration) mDuration = duration; } -AttachMultisampledDepthColorCallback::AttachMultisampledDepthColorCallback(const osg::ref_ptr& colorTex, const osg::ref_ptr& depthTex, int samples, int colorSamples) +// Allows camera to render to a color and floating point depth texture with a multisampled framebuffer. +// Must be set on a camera's cull callback. +class AttachMultisampledDepthColorCallback : public osg::NodeCallback { - int width = colorTex->getTextureWidth(); - int height = colorTex->getTextureHeight(); +public: + AttachMultisampledDepthColorCallback(osg::Texture2D* colorTex, osg::Texture2D* depthTex, int samples, int colorSamples) + { + int width = colorTex->getTextureWidth(); + int height = colorTex->getTextureHeight(); - osg::ref_ptr rbColor = new osg::RenderBuffer(width, height, colorTex->getInternalFormat(), samples, colorSamples); - osg::ref_ptr rbDepth = new osg::RenderBuffer(width, height, depthTex->getInternalFormat(), samples, colorSamples); + osg::ref_ptr rbColor = new osg::RenderBuffer(width, height, colorTex->getInternalFormat(), samples, colorSamples); + osg::ref_ptr rbDepth = new osg::RenderBuffer(width, height, depthTex->getInternalFormat(), samples, colorSamples); - mMsaaFbo = new osg::FrameBufferObject; - mMsaaFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(rbColor)); - mMsaaFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(rbDepth)); + mMsaaFbo = new osg::FrameBufferObject; + mMsaaFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(rbColor)); + mMsaaFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(rbDepth)); - mFbo = new osg::FrameBufferObject; - mFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(colorTex)); - mFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(depthTex)); -} + mFbo = new osg::FrameBufferObject; + mFbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment(colorTex)); + mFbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(depthTex)); + } -void AttachMultisampledDepthColorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) -{ - osgUtil::RenderStage* renderStage = nv->asCullVisitor()->getCurrentRenderStage(); + void operator()(osg::Node* node, osg::NodeVisitor* nv) override + { + osgUtil::RenderStage* renderStage = nv->asCullVisitor()->getCurrentRenderStage(); - renderStage->setMultisampleResolveFramebufferObject(mFbo); - renderStage->setFrameBufferObject(mMsaaFbo); + renderStage->setMultisampleResolveFramebufferObject(mFbo); + renderStage->setFrameBufferObject(mMsaaFbo); - traverse(node, nv); -} + traverse(node, nv); + } + +private: + osg::ref_ptr mFbo; + osg::ref_ptr mMsaaFbo; +}; void transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere) { @@ -313,9 +338,37 @@ bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg:: return addMSAAIntermediateTarget; } -osg::ref_ptr createDepth(bool reverseZ) +void attachAlphaToCoverageFriendlyDepthColor(osg::Camera* camera, osg::Texture2D* colorTex, osg::Texture2D* depthTex, GLenum depthFormat) { - return new osg::Depth(reverseZ ? osg::Depth::GEQUAL : osg::Depth::LEQUAL); + bool addMSAAIntermediateTarget = Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1; + + if (isFloatingPointDepthFormat(depthFormat) && addMSAAIntermediateTarget) + { + camera->attach(osg::Camera::COLOR_BUFFER0, colorTex); + camera->attach(osg::Camera::DEPTH_BUFFER, depthTex); + camera->addCullCallback(new AttachMultisampledDepthColorCallback(colorTex, depthTex, 2, 1)); + } + else + { + attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, colorTex); + camera->attach(osg::Camera::DEPTH_BUFFER, depthTex); + } +} + +bool getReverseZ() +{ + static bool reverseZ = isReverseZSupported(); + return reverseZ; +} + +void setCameraClearDepth(osg::Camera* camera) +{ + camera->setClearDepth(getReverseZ() ? 0.0 : 1.0); +} + +osg::ref_ptr createDepth() +{ + return new osg::Depth(getReverseZ() ? osg::Depth::GEQUAL : osg::Depth::LEQUAL); } osg::Matrix getReversedZProjectionMatrixAsPerspectiveInf(double fov, double aspect, double near) @@ -352,7 +405,14 @@ osg::Matrix getReversedZProjectionMatrixAsOrtho(double left, double right, doubl bool isFloatingPointDepthFormat(GLenum format) { - return format == GL_DEPTH_COMPONENT32F || format == GL_DEPTH_COMPONENT32F_NV; + constexpr std::array formats = { + GL_DEPTH_COMPONENT32F, + GL_DEPTH_COMPONENT32F_NV, + GL_DEPTH32F_STENCIL8, + GL_DEPTH32F_STENCIL8_NV, + }; + + return std::find(formats.cbegin(), formats.cend(), format) != formats.cend(); } } diff --git a/components/sceneutil/util.hpp b/components/sceneutil/util.hpp index bd4df5441a..0bca32c325 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -8,7 +8,6 @@ #include #include #include -#include #include @@ -49,22 +48,6 @@ namespace SceneUtil bool mDone; }; - // Allows camera to render to a color and floating point depth texture with a multisampled framebuffer. - // Must be set on a camera's cull callback. - // When the depth texture isn't needed as a sampler, use osg::Camera::attach(osg::Camera::DEPTH_COMPONENT, GL_DEPTH_COMPONENT32F) instead. - // If multisampling is not being used on the color buffer attachment, use the osg::Camera::attach() method. - class AttachMultisampledDepthColorCallback : public osg::NodeCallback - { - public: - AttachMultisampledDepthColorCallback(const osg::ref_ptr& colorTex, const osg::ref_ptr& depthTex, int samples, int colorSamples); - - void operator()(osg::Node* node, osg::NodeVisitor* nv) override; - - private: - osg::ref_ptr mFbo; - osg::ref_ptr mMsaaFbo; - }; - // Transform a bounding sphere by a matrix // based off private code in osg::Transform // TODO: patch osg to make public @@ -83,9 +66,15 @@ 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); + void attachAlphaToCoverageFriendlyDepthColor(osg::Camera* camera, osg::Texture2D* colorTex, osg::Texture2D* depthTex, GLenum depthFormat); + + bool getReverseZ(); + + void setCameraClearDepth(osg::Camera* camera); + // Returns a suitable depth state attribute dependent on whether a reverse-z // depth buffer is in use. - osg::ref_ptr createDepth(bool reverseZ); + osg::ref_ptr createDepth(); // 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 @@ -100,7 +89,7 @@ namespace SceneUtil osg::Matrix getReversedZProjectionMatrixAsOrtho(double left, double right, double bottom, double top, double near, double far); // Returns true if the GL format is a floating point depth format - bool isFloatingPointDepthFormat(GLenum format); + bool isFloatingPointDepthFormat(GLenum format); } #endif diff --git a/components/sceneutil/waterutil.cpp b/components/sceneutil/waterutil.cpp index dccc534ecc..ac171005ae 100644 --- a/components/sceneutil/waterutil.cpp +++ b/components/sceneutil/waterutil.cpp @@ -64,7 +64,7 @@ namespace SceneUtil return waterGeom; } - osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin, bool reverseZ) + osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin) { osg::ref_ptr stateset (new osg::StateSet); @@ -78,7 +78,7 @@ namespace SceneUtil stateset->setMode(GL_BLEND, osg::StateAttribute::ON); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); - auto depth = createDepth(reverseZ); + auto depth = createDepth(); depth->setWriteMask(false); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); diff --git a/components/sceneutil/waterutil.hpp b/components/sceneutil/waterutil.hpp index 7f08dbcfa9..7b8c380109 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, bool reverseZ); + osg::ref_ptr createSimpleWaterStateSet(float alpha, int renderBin); } #endif diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 5354b9d8a2..a744471de5 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, layers, blendmapTextures, blendmapScale, blendmapScale); + return ::Terrain::createPasses(useShaders, &mSceneManager->getShaderManager(), 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, std::vector(1, layer), std::vector >(), 1.f, 1.f)); + geometry->setPasses(::Terrain::createPasses(mSceneManager->getForceShaders() || !mSceneManager->getClampLighting(), &mSceneManager->getShaderManager(), std::vector(1, layer), std::vector >(), 1.f, 1.f)); } else { diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index dc74b58560..2fbd0a7fc0 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -97,17 +96,17 @@ namespace class LequalDepth { public: - static const osg::ref_ptr& value(bool reverseZ) + static const osg::ref_ptr& value() { - static LequalDepth instance(reverseZ); + static LequalDepth instance; return instance.mValue; } private: osg::ref_ptr mValue; - LequalDepth(bool reverseZ) - : mValue(SceneUtil::createDepth(reverseZ)) + LequalDepth() + : mValue(SceneUtil::createDepth()) { } }; @@ -171,10 +170,9 @@ namespace namespace Terrain { - std::vector > createPasses(bool useShaders, Resource::SceneManager* sceneManager, const std::vector &layers, + std::vector > createPasses(bool useShaders, Shader::ShaderManager* shaderManager, const std::vector &layers, const std::vector > &blendmaps, int blendmapScale, float layerTileSize) { - Shader::ShaderManager* shaderManager = &sceneManager->getShaderManager(); std::vector > passes; unsigned int blendmapIndex = 0; @@ -197,7 +195,7 @@ namespace Terrain else { stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); - stateset->setAttributeAndModes(LequalDepth::value(sceneManager->getReverseZ()), osg::StateAttribute::ON); + stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); } } @@ -240,7 +238,7 @@ namespace Terrain if (!vertexShader || !fragmentShader) { // Try again without shader. Error already logged by above - return createPasses(false, sceneManager, layers, blendmaps, blendmapScale, layerTileSize); + return createPasses(false, shaderManager, layers, blendmaps, blendmapScale, layerTileSize); } stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader)); diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index d5ef40a29e..5f78af6a06 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -10,9 +10,9 @@ namespace osg class Texture2D; } -namespace Resource +namespace Shader { - class SceneManager; + class ShaderManager; } namespace Terrain @@ -26,7 +26,7 @@ namespace Terrain bool mSpecular; }; - std::vector > createPasses(bool useShaders, Resource::SceneManager* sceneManager, + std::vector > createPasses(bool useShaders, Shader::ShaderManager* shaderManager, const std::vector& layers, const std::vector >& blendmaps, int blendmapScale, float layerTileSize); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 4943ce969b..2c287a889c 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -67,7 +67,7 @@ head bobbing height = 3.0 # Maximum camera roll angle (degrees) head bobbing roll = 0.2 -# Reverse the depth range from [0,1] to [1,0]. +# Reverse the depth range, reduces z-fighting of distant objects and terrain reverse z = true [Cells] diff --git a/files/shaders/debug_fragment.glsl b/files/shaders/debug_fragment.glsl index 263d4e1f24..1b25510d66 100644 --- a/files/shaders/debug_fragment.glsl +++ b/files/shaders/debug_fragment.glsl @@ -1,8 +1,8 @@ #version 120 -varying vec4 passColor; +#include "vertexcolors.glsl" void main() { - gl_FragData[0] = passColor; + gl_FragData[0] = getDiffuseColor(); } diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index ed0ebdc913..26f83e052c 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -51,6 +51,8 @@ const float WIND_SPEED = 0.2f; const vec3 WATER_COLOR = vec3(0.090195, 0.115685, 0.12745); +const float WOBBLY_SHORE_FADE_DISTANCE = 6200.0; // fade out wobbly shores to mask precision errors, the effect is almost impossible to see at a distance + // ---------------- rain ripples related stuff --------------------- const float RAIN_RIPPLE_GAPS = 5.0; @@ -270,10 +272,10 @@ void main(void) vec3 normalShoreRippleRain = texture2D(normalMap,normalCoords(UV, 2.0, 2.7, -1.0*waterTimer, 0.05, 0.1, normal3)).rgb - 0.5 + texture2D(normalMap,normalCoords(UV, 2.0, 2.7, waterTimer, 0.04, -0.13, normal4)).rgb - 0.5; float verticalWaterDepth = realWaterDepth * mix(abs(vVec.z), 1.0, 0.2); // an estimate - float shoreOffset = verticalWaterDepth - (normal2.r + mix(0, normalShoreRippleRain.r, rainIntensity) + 0.15)*8; - float fuzzFactor = min(1.0, 1000.0/surfaceDepth) * mix(abs(vVec.z), 1, 0.2); + float shoreOffset = verticalWaterDepth - (normal2.r + mix(0.0, normalShoreRippleRain.r, rainIntensity) + 0.15)*8.0; + float fuzzFactor = min(1.0, 1000.0/surfaceDepth) * mix(abs(vVec.z), 1.0, 0.2); shoreOffset *= fuzzFactor; - shoreOffset = clamp(shoreOffset, 0, 1); + shoreOffset = clamp(mix(shoreOffset, 1.0, clamp(linearDepth / WOBBLY_SHORE_FADE_DISTANCE, 0.0, 1.0)), 0.0, 1.0); gl_FragData[0].xyz = mix(rawRefraction, gl_FragData[0].xyz, shoreOffset); #else gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * sunSpec.xyz + vec3(rainRipple.w) * 0.7;