diff --git a/CHANGELOG.md b/CHANGELOG.md index a9ec5a946e..801d05d357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,9 @@ 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 + 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/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..2f12b83572 100644 --- a/apps/openmw/mwgui/bookpage.cpp +++ b/apps/openmw/mwgui/bookpage.cpp @@ -8,6 +8,7 @@ #include "MyGUI_FactoryManager.h" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -1217,8 +1218,10 @@ public: RenderXform renderXform (mCroppedParent, textFormat.mRenderItem->getRenderTarget()->getInfo()); + float z = SceneUtil::getReverseZ() ? 1.f : -1.f; + 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/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index c4d608443c..086135d035 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_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/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..b008ebc6d9 100644 --- a/apps/openmw/mwrender/bulletdebugdraw.cpp +++ b/apps/openmw/mwrender/bulletdebugdraw.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -14,6 +15,12 @@ #include "bulletdebugdraw.hpp" #include "vismask.hpp" +#include +#include + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + namespace MWRender { @@ -43,6 +50,10 @@ void DebugDrawer::createGeometry() mGeometry->setDataVariance(osg::Object::DYNAMIC); mGeometry->addPrimitiveSet(mDrawArrays); + osg::ref_ptr material = new osg::Material; + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + mGeometry->getOrCreateStateSet()->setAttribute(material); + mParentNode->addChild(mGeometry); auto* stateSet = new osg::StateSet; @@ -52,6 +63,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 e88c4cee32..580451ff8f 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -187,6 +187,10 @@ 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); SceneUtil::ShadowManager::disableShadowsForStateSet(stateset); diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index dcaf9c1612..7d7c6e45df 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -15,6 +15,7 @@ #include #include +#include #include @@ -24,6 +25,7 @@ #include "../mwworld/esmstore.hpp" #include "vismask.hpp" +#include "util.hpp" namespace { @@ -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(); 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 858d577335..02f59e0ed0 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 { @@ -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 (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); + 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 (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); + // 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/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 d338d70873..12ed88e55d 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 { @@ -372,7 +373,7 @@ class DepthClearCallback : public osgUtil::RenderBin::DrawCallback public: DepthClearCallback() { - mDepth = new osg::Depth; + mDepth = SceneUtil::createDepth(); mDepth->setWriteMask(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 new file mode 100644 index 0000000000..eff7d46892 --- /dev/null +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -0,0 +1,266 @@ +#include "postprocessor.hpp" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "vismask.hpp" +#include "renderingmanager.hpp" + +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() + : 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; + + MWRender::PostProcessor* postProcessor = dynamic_cast(nv->asCullVisitor()->getCurrentCamera()->getUserData()); + + if (!postProcessor) + { + 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(postProcessor->getFbo()); + renderStage->setFrameBufferObject(postProcessor->getMsaaFbo()); + } + } + + traverse(node, nv); + } + + private: + 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 + { + gc->resizedImplementation(x, y, width, height); + mPostProcessor->resize(width, height); + } + + MWRender::PostProcessor* mPostProcessor; + }; +} + +namespace MWRender +{ + 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, true); + + mRootNode->addChild(mHUDCamera); + mRootNode->addChild(rootNode); + mViewer->setSceneData(mRootNode); + + // 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); + 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) + { + 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)); + } + + if (init) + return; + + mViewer->getCamera()->resize(width, height); + mHUDCamera->resize(width, height); + + mRendering.updateProjectionMatrix(); + } + + void PostProcessor::createTexturesAndCamera(int width, int height) + { + mDepthTex = new osg::Texture2D; + mDepthTex->setTextureSize(width, height); + mDepthTex->setSourceFormat(GL_DEPTH_COMPONENT); + 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); + 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()); + mHUDCamera->setNodeMask(Mask_RenderToTexture); + + 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..ad922c1cbc --- /dev/null +++ b/apps/openmw/mwrender/postprocessor.hpp @@ -0,0 +1,50 @@ +#ifndef OPENMW_MWRENDER_POSTPROCESSOR_H +#define OPENMW_MWRENDER_POSTPROCESSOR_H + +#include +#include +#include +#include +#include + +namespace osgViewer +{ + class Viewer; +} + +namespace MWRender +{ + class RenderingManager; + + class PostProcessor : public osg::Referenced + { + public: + PostProcessor(RenderingManager& rendering, osgViewer::Viewer* viewer, osg::Group* rootNode); + + auto getMsaaFbo() { return mMsaaFbo; } + auto getFbo() { return mFbo; } + + int getDepthFormat() { return mDepthFormat; } + + 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; + + osg::ref_ptr mMsaaFbo; + osg::ref_ptr mFbo; + + osg::ref_ptr mSceneTex; + osg::ref_ptr mDepthTex; + + int mDepthFormat; + + RenderingManager& mRendering; + }; +} + +#endif \ No newline at end of file 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 81687a712a..5ffc54f3f8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include @@ -68,9 +70,74 @@ #include "objectpaging.hpp" #include "screenshotmanager.hpp" #include "groundcover.hpp" +#include "postprocessor.hpp" namespace MWRender { + class SharedUniformStateUpdater : public SceneUtil::StateSetUpdater + { + public: + SharedUniformStateUpdater() + : mLinearFac(0.f) + , mNear(0.f) + , mFar(0.f) + { + } + + void setDefaults(osg::StateSet *stateset) override + { + 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 + { + auto* uProjectionMatrix = stateset->getUniform("projectionMatrix"); + if (uProjectionMatrix) + uProjectionMatrix->set(mProjectionMatrix); + + 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) + { + mProjectionMatrix = projectionMatrix; + } + + void setLinearFac(float linearFac) + { + 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 { @@ -198,6 +265,13 @@ namespace MWRender , mFieldOfViewOverridden(false) , mFieldOfViewOverride(0.f) { + bool reverseZ = SceneUtil::getReverseZ(); + + 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); @@ -205,7 +279,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")); @@ -267,6 +342,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); @@ -353,6 +430,13 @@ 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 = 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)); @@ -395,6 +479,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")) @@ -415,7 +502,9 @@ namespace MWRender NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect); 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); @@ -423,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 @@ -432,8 +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) + { + osg::ref_ptr clipcontrol = new osg::ClipControl(osg::ClipControl::LOWER_LEFT, osg::ClipControl::ZERO_TO_ONE); + mRootNode->getOrCreateStateSet()->setAttributeAndModes(SceneUtil::createDepth(), osg::StateAttribute::ON); + mRootNode->getOrCreateStateSet()->setAttributeAndModes(clipcontrol, osg::StateAttribute::ON); + } + + SceneUtil::setCameraClearDepth(mViewer->getCamera()); + updateProjectionMatrix(); } @@ -1066,10 +1160,19 @@ namespace MWRender float fov = mFieldOfView; if (mFieldOfViewOverridden) fov = mFieldOfViewOverride; + mViewer->getCamera()->setProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance); - mUniformNear->set(mNearClip); - mUniformFar->set(mViewDistance); + if (SceneUtil::getReverseZ()) + { + mSharedUniformStateUpdater->setLinearFac(-mNearClip / (mViewDistance - mNearClip) - 1.f); + mSharedUniformStateUpdater->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance)); + } + else + mSharedUniformStateUpdater->setProjectionMatrix(mViewer->getCamera()->getProjectionMatrix()); + + 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 a0a74bd5c4..d6d7e74634 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; @@ -89,6 +90,7 @@ namespace MWRender class RecastMesh; class ObjectPaging; class Groundcover; + class PostProcessor; class RenderingManager : public MWRender::RenderingInterface { @@ -108,9 +110,6 @@ namespace MWRender SceneUtil::UnrefQueue* getUnrefQueue(); Terrain::World* getTerrain(); - osg::Uniform* mUniformNear; - osg::Uniform* mUniformFar; - void preloadCommonAssets(); double getReferenceTime() const; @@ -240,8 +239,9 @@ namespace MWRender 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); @@ -287,6 +287,7 @@ namespace MWRender std::unique_ptr mScreenshotManager; std::unique_ptr mEffectManager; std::unique_ptr mShadowManager; + osg::ref_ptr mPostProcessor; osg::ref_ptr mPlayerAnimation; osg::ref_ptr mPlayerNode; std::unique_ptr mCamera; @@ -294,6 +295,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/ripplesimulation.cpp b/apps/openmw/mwrender/ripplesimulation.cpp index 6788f53f44..fdfc3db63d 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,13 +56,13 @@ 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(); 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 b24572dbaa..96a8a9450a 100644 --- a/apps/openmw/mwrender/screenshotmanager.cpp +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -22,6 +23,7 @@ #include "util.hpp" #include "vismask.hpp" #include "water.hpp" +#include "postprocessor.hpp" namespace MWRender { @@ -88,6 +90,18 @@ 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. + PostProcessor* postProcessor = dynamic_cast(renderInfo.getCurrentCamera()->getUserData()); + + if (postProcessor && postProcessor->getFbo() && postProcessor->getMsaaFbo()) + { + osg::GLExtensions* ext = osg::GLExtensions::Get(renderInfo.getContextID(), false); + if (ext) + ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, postProcessor->getFbo()->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; @@ -282,9 +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); - camera->setViewport(0, 0, w, h); + SceneUtil::setCameraClearDepth(camera); + osg::ref_ptr texture (new osg::Texture2D); texture->setInternalFormat(GL_RGB); texture->setTextureSize(w,h); @@ -318,7 +334,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 (SceneUtil::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); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 8bac90604b..2b58730fe1 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 { @@ -594,13 +595,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(); // 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); + double far = SceneUtil::getReverseZ() ? 0.0 : 1.0; + depth->setZNear(far); + depth->setZFar(far); depth->setWriteMask(false); queryStateSet->setAttributeAndModes(depth, osg::StateAttribute::ON); } @@ -1209,7 +1210,7 @@ void SkyManager::create() mCloudMesh2->addUpdateCallback(mCloudUpdater2); mCloudMesh2->setNodeMask(0); - osg::ref_ptr depth = new osg::Depth; + 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/water.cpp b/apps/openmw/mwrender/water.cpp index 621a5e7c25..da26595c8c 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -273,6 +273,7 @@ public: void setDefaults(osg::Camera* camera) override { + SceneUtil::setCameraClearDepth(camera); 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 { + SceneUtil::setCameraClearDepth(camera); camera->setReferenceFrame(osg::Camera::RELATIVE_RF); camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); camera->setName("ReflectionCamera"); @@ -614,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 7c32bb04f8..83b9fb61c2 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1823,7 +1823,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(); // Depth write flag depth->setWriteMask((zprop->flags>>1)&1); // Morrowind ignores depth test function diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index f6035a47dd..ffcd4c3308 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -275,6 +275,16 @@ namespace Resource return mClampLighting; } + 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 de014165bf..9d798177a1 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 setDepthFormat(GLenum format); + GLenum getDepthFormat() const; + /// @see ShaderVisitor::setAutoUseNormalMaps void setAutoUseNormalMaps(bool use); @@ -202,6 +205,7 @@ namespace Resource SceneUtil::LightingMethod mLightingMethod; SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods; bool mConvertAlphaTestToAlphaToCoverage; + 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 ad3fc5fd65..19222eda23 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -23,8 +23,12 @@ #include #include #include +#include #include + +#include + #include "shadowsbin.hpp" namespace { @@ -1636,6 +1640,11 @@ void MWShadowTechnique::createShaders() _shadowCastingStateSet->addUniform(new osg::Uniform("alphaTestShadows", false)); osg::ref_ptr depth = new osg::Depth; depth->setWriteMask(true); + 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); + } _shadowCastingStateSet->setAttribute(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); _shadowCastingStateSet->setMode(GL_DEPTH_CLAMP, osg::StateAttribute::ON); 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 b48ceda409..5fbeb681fc 100644 --- a/components/sceneutil/optimizer.cpp +++ b/components/sceneutil/optimizer.cpp @@ -40,6 +40,8 @@ #include +#include + using namespace osgUtil; namespace SceneUtil @@ -1560,7 +1562,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group) } if (_alphaBlendingActive && _mergeAlphaBlending && !geom->getStateSet()) { - osg::Depth* d = new osg::Depth; + auto d = createDepth(); d->setWriteMask(0); geom->getOrCreateStateSet()->setAttribute(d); } 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/util.cpp b/components/sceneutil/util.cpp index 9b4fb9d3fb..02cd9295e7 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include #include @@ -13,6 +15,20 @@ #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 { @@ -150,6 +166,43 @@ void GlowUpdater::setDuration(float duration) mDuration = duration; } +// 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 +{ +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); + + 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 operator()(osg::Node* node, osg::NodeVisitor* nv) override + { + osgUtil::RenderStage* renderStage = nv->asCullVisitor()->getCurrentRenderStage(); + + renderStage->setMultisampleResolveFramebufferObject(mFbo); + renderStage->setFrameBufferObject(mMsaaFbo); + + traverse(node, nv); + } + +private: + osg::ref_ptr mFbo; + osg::ref_ptr mMsaaFbo; +}; + void transformBoundingSphere (const osg::Matrixf& matrix, osg::BoundingSphere& bsphere) { osg::BoundingSphere::vec_type xdash = bsphere._center; @@ -285,4 +338,81 @@ bool attachAlphaToCoverageFriendlyFramebufferToCamera(osg::Camera* camera, osg:: return addMSAAIntermediateTarget; } +void attachAlphaToCoverageFriendlyDepthColor(osg::Camera* camera, osg::Texture2D* colorTex, osg::Texture2D* depthTex, GLenum depthFormat) +{ + 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) +{ + 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, near/(far-near), -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 + ); +} + +bool isFloatingPointDepthFormat(GLenum format) +{ + 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 f297c42d2e..0bca32c325 100644 --- a/components/sceneutil/util.hpp +++ b/components/sceneutil/util.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -64,6 +65,31 @@ 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(); + + // 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); + + // Returns true if the GL format is a floating point depth format + bool isFloatingPointDepthFormat(GLenum format); } #endif diff --git a/components/sceneutil/waterutil.cpp b/components/sceneutil/waterutil.cpp index 8a434105c8..ac171005ae 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 @@ -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(); depth->setWriteMask(false); stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); 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/material.cpp b/components/terrain/material.cpp index e662f4439f..2fbd0a7fc0 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -8,6 +8,7 @@ #include #include +#include #include @@ -105,9 +106,8 @@ namespace osg::ref_ptr mValue; LequalDepth() - : mValue(new osg::Depth) + : mValue(SceneUtil::createDepth()) { - mValue->setFunction(osg::Depth::LEQUAL); } }; 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..2c287a889c 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, reduces z-fighting of distant objects and terrain +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 5297b2e752..04446d2982 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 @@ -33,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..1b25510d66 --- /dev/null +++ b/files/shaders/debug_fragment.glsl @@ -0,0 +1,8 @@ +#version 120 + +#include "vertexcolors.glsl" + +void main() +{ + gl_FragData[0] = getDiffuseColor(); +} 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 new file mode 100644 index 0000000000..aa6f54b99d --- /dev/null +++ b/files/shaders/depth.glsl @@ -0,0 +1,12 @@ +#if @reverseZ +uniform float linearFac; +#endif + +float getLinearDepth(in float z, in float viewZ) +{ +#if @reverseZ + return linearFac*viewZ; +#else + return z; +#endif +} \ No newline at end of file diff --git a/files/shaders/groundcover_vertex.glsl b/files/shaders/groundcover_vertex.glsl index a12fb3d9d9..c8db4be000 100644 --- a/files/shaders/groundcover_vertex.glsl +++ b/files/shaders/groundcover_vertex.glsl @@ -39,12 +39,14 @@ centroid varying vec3 shadowDiffuseLighting; #include "shadows_vertex.glsl" #include "lighting.glsl" +#include "depth.glsl" uniform float osg_SimulationTime; uniform mat4 osg_ViewMatrixInverse; uniform mat4 osg_ViewMatrix; uniform float windSpeed; uniform vec3 playerPos; +uniform mat4 projectionMatrix; #if @groundcoverStompMode == 0 #else @@ -141,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 = gl_Position.z; + 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 50f5daf25e..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 @@ -32,15 +34,16 @@ varying vec3 passNormal; #include "vertexcolors.glsl" #include "shadows_vertex.glsl" #include "lighting.glsl" +#include "depth.glsl" 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 = gl_Position.z; + 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 275f1e5730..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 @@ -17,17 +19,18 @@ varying vec3 passViewPos; varying float passFalloff; #include "vertexcolors.glsl" +#include "depth.glsl" 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 = gl_Position.z; + 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 64c35a21cc..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 @@ -62,15 +64,17 @@ varying vec3 passNormal; #include "shadows_vertex.glsl" #include "lighting.glsl" +#include "depth.glsl" 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 = gl_Position.z; + 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 638d6cca0b..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; @@ -25,15 +27,16 @@ varying vec3 passNormal; #include "shadows_vertex.glsl" #include "lighting.glsl" +#include "depth.glsl" 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 = gl_Position.z; + 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_fragment.glsl b/files/shaders/water_fragment.glsl index d1e795eca7..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; @@ -158,11 +160,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) { @@ -267,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; diff --git a/files/shaders/water_vertex.glsl b/files/shaders/water_vertex.glsl index 02a395f95d..8e506a57f8 100644 --- a/files/shaders/water_vertex.glsl +++ b/files/shaders/water_vertex.glsl @@ -1,14 +1,17 @@ #version 120 - + +uniform mat4 projectionMatrix; + varying vec3 screenCoordsPassthrough; varying vec4 position; varying float linearDepth; #include "shadows_vertex.glsl" +#include "depth.glsl" 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, @@ -20,7 +23,8 @@ void main(void) position = gl_Vertex; - linearDepth = gl_Position.z; + vec4 viewPos = gl_ModelViewMatrix * gl_Vertex; + linearDepth = getLinearDepth(gl_Position.z, viewPos.z); - setupShadowCoords(gl_ModelViewMatrix * gl_Vertex, normalize((gl_NormalMatrix * gl_Normal).xyz)); + setupShadowCoords(viewPos, normalize((gl_NormalMatrix * gl_Normal).xyz)); }