1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-04 12:39:55 +00:00

Merge branch 'to_infinity_and_beyond' into 'master'

Reverse-Z depth buffer

See merge request OpenMW/openmw!889
This commit is contained in:
AnyOldName3 2021-08-05 20:22:51 +00:00
commit b8878cb5f9
51 changed files with 871 additions and 64 deletions

View File

@ -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
------

View File

@ -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);

View File

@ -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

View File

@ -8,6 +8,7 @@
#include "MyGUI_FactoryManager.h"
#include <components/misc/utf8stream.hpp>
#include <components/sceneutil/util.hpp>
#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<float>(mCoord.left), static_cast<float>(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));

View File

@ -450,6 +450,7 @@ namespace MWGui
osg::ref_ptr<osg::Texture2D> 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);

View File

@ -2,9 +2,13 @@
#include "vismask.hpp"
#include <components/sceneutil/agentpath.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <osg/PositionAttitudeTransform>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
namespace MWRender
{
ActorsPaths::ActorsPaths(const osg::ref_ptr<osg::Group>& 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;

View File

@ -4,6 +4,7 @@
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Material>
#include <components/debug/debuglog.hpp>
#include <components/misc/convert.hpp>
@ -14,6 +15,12 @@
#include "bulletdebugdraw.hpp"
#include "vismask.hpp"
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#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<osg::Material> 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");
}
}

View File

@ -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<osg::Matrixf>(mCamera->getProjectionMatrix())));
osg::ref_ptr<osg::Depth> depth = new osg::Depth;
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);

View File

@ -15,6 +15,7 @@
#include <components/debug/debuglog.hpp>
#include <components/sceneutil/workqueue.hpp>
#include <components/sceneutil/util.hpp>
#include <components/esm/globalmap.hpp>
@ -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<osg::Geometry> geom = createTexturedQuad(srcLeft, srcTop, srcRight, srcBottom);
osg::ref_ptr<osg::Depth> depth = new osg::Depth;
auto depth = SceneUtil::createDepth();
depth->setWriteMask(0);
osg::StateSet* stateset = geom->getOrCreateStateSet();
stateset->setAttribute(depth);

View File

@ -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<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax)
{
osg::ref_ptr<osg::Camera> 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<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
osg::ref_ptr<osg::StateSet> 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<osg::Matrixf>(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<osg::Fog> fog (new osg::Fog);

View File

@ -2,9 +2,14 @@
#include "vismask.hpp"
#include <components/sceneutil/navmesh.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <osg/PositionAttitudeTransform>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
namespace MWRender
{
NavMesh::NavMesh(const osg::ref_ptr<osg::Group>& 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);
}

View File

@ -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);
}

View File

@ -8,7 +8,10 @@
#include <components/esm/loadpgrd.hpp>
#include <components/sceneutil/pathgridutil.hpp>
#include <components/sceneutil/util.hpp>
#include <components/misc/coordinateconverter.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#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<osg::Geometry> geometry = SceneUtil::createPathgridGeometry(*pathgrid);
MWBase::Environment::get().getResourceSystem()->getSceneManager()->recreateShaders(geometry, "debug");
cellPathGrid->addChild(geometry);
mPathGridRoot->addChild(cellPathGrid);

View File

@ -0,0 +1,266 @@
#include "postprocessor.hpp"
#include <osg/Group>
#include <osg/Camera>
#include <osg/Callback>
#include <osg/Texture2D>
#include <osg/FrameBufferObject>
#include <osgViewer/Viewer>
#include <components/settings/settings.hpp>
#include <components/sceneutil/util.hpp>
#include <components/debug/debuglog.hpp>
#include "vismask.hpp"
#include "renderingmanager.hpp"
namespace
{
osg::ref_ptr<osg::Geometry> createFullScreenTri()
{
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> 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<MWRender::PostProcessor*>(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<osg::GLExtensions>();
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<osg::RenderBuffer> colorRB = new osg::RenderBuffer(width, height, mSceneTex->getInternalFormat(), samples);
osg::ref_ptr<osg::RenderBuffer> 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<osg::Shader> vertShader = new osg::Shader(osg::Shader::VERTEX, vertSrc);
osg::ref_ptr<osg::Shader> fragShader = new osg::Shader(osg::Shader::FRAGMENT, fragSrc);
osg::ref_ptr<osg::Program> 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);
}
}

View File

@ -0,0 +1,50 @@
#ifndef OPENMW_MWRENDER_POSTPROCESSOR_H
#define OPENMW_MWRENDER_POSTPROCESSOR_H
#include <osg/Texture2D>
#include <osg/Group>
#include <osg/FrameBufferObject>
#include <osg/Camera>
#include <osg/ref_ptr>
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<osg::Group> mRootNode;
osg::ref_ptr<osg::Camera> mHUDCamera;
osg::ref_ptr<osg::FrameBufferObject> mMsaaFbo;
osg::ref_ptr<osg::FrameBufferObject> mFbo;
osg::ref_ptr<osg::Texture2D> mSceneTex;
osg::ref_ptr<osg::Texture2D> mDepthTex;
int mDepthFormat;
RenderingManager& mRendering;
};
}
#endif

View File

@ -1,11 +1,15 @@
#include "recastmesh.hpp"
#include <components/sceneutil/recastmesh.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <osg/PositionAttitudeTransform>
#include "vismask.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
namespace MWRender
{
RecastMesh::RecastMesh(const osg::ref_ptr<osg::Group>& 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);

View File

@ -11,6 +11,8 @@
#include <osg/Group>
#include <osg/UserDataContainer>
#include <osg/ComputeBoundsVisitor>
#include <osg/Depth>
#include <osg/ClipControl>
#include <osgUtil/LineSegmentIntersector>
@ -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<osg::ClipControl> 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.

View File

@ -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<ESM::RefNum> &out);
private:
void updateProjectionMatrix();
private:
void updateTextureFiltering();
void updateAmbient();
void setFogColor(const osg::Vec4f& color);
@ -287,6 +287,7 @@ namespace MWRender
std::unique_ptr<ScreenshotManager> mScreenshotManager;
std::unique_ptr<EffectManager> mEffectManager;
std::unique_ptr<SceneUtil::ShadowManager> mShadowManager;
osg::ref_ptr<PostProcessor> mPostProcessor;
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;
std::unique_ptr<Camera> mCamera;
@ -294,6 +295,7 @@ namespace MWRender
osg::Vec3f mCurrentCameraPos;
osg::ref_ptr<StateUpdater> mStateUpdater;
osg::ref_ptr<SharedUniformStateUpdater> mSharedUniformStateUpdater;
osg::Vec4f mAmbientColor;
float mMinimumAmbientLuminance;

View File

@ -16,6 +16,7 @@
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/fallback/fallback.hpp>
#include <components/sceneutil/util.hpp>
#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<osg::Depth> depth (new osg::Depth);
auto depth = SceneUtil::createDepth();
depth->setWriteMask(false);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
osg::ref_ptr<osg::PolygonOffset> 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);

View File

@ -12,6 +12,7 @@
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/sceneutil/util.hpp>
#include <components/settings/settings.hpp>
@ -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<int>(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<PostProcessor*>(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<osg::Camera> screenshotCamera(new osg::Camera);
osg::ref_ptr<osg::ShapeDrawable> 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<std::string, std::string> 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<osg::Texture2D> 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);

View File

@ -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<osg::Depth> 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<osg::Depth> 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);

View File

@ -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<osg::Depth> depth(new osg::Depth);
osg::ref_ptr<osg::Depth> depth = SceneUtil::createDepth();
depth->setWriteMask(false);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
}

View File

@ -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<osg::Depth> depth = new osg::Depth;
auto depth = SceneUtil::createDepth();
// Depth write flag
depth->setWriteMask((zprop->flags>>1)&1);
// Morrowind ignores depth test function

View File

@ -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;

View File

@ -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<MultiObjectCache> mInstanceCache;

View File

@ -1,6 +1,8 @@
#include "agentpath.hpp"
#include "detourdebugdraw.hpp"
#include <osg/Material>
#include <components/detournavigator/settings.hpp>
#include <algorithm>
@ -65,6 +67,10 @@ namespace SceneUtil
debugDraw.depthMask(true);
osg::ref_ptr<osg::Material> material = new osg::Material;
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
group->getOrCreateStateSet()->setAttribute(material);
return group;
}
}

View File

@ -23,8 +23,12 @@
#include <osg/Geometry>
#include <osg/io_utils>
#include <osg/Depth>
#include <osg/ClipControl>
#include <sstream>
#include <components/sceneutil/util.hpp>
#include "shadowsbin.hpp"
namespace {
@ -1636,6 +1640,11 @@ void MWShadowTechnique::createShaders()
_shadowCastingStateSet->addUniform(new osg::Uniform("alphaTestShadows", false));
osg::ref_ptr<osg::Depth> depth = new osg::Depth;
depth->setWriteMask(true);
if (SceneUtil::getReverseZ())
{
osg::ref_ptr<osg::ClipControl> 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);

View File

@ -6,6 +6,7 @@
#include <DetourDebugDraw.h>
#include <osg/Group>
#include <osg/Material>
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<osg::Material> material = new osg::Material;
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
group->getOrCreateStateSet()->setAttribute(material);
return group;
}
}

View File

@ -40,6 +40,8 @@
#include <iterator>
#include <components/sceneutil/util.hpp>
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);
}

View File

@ -1,6 +1,7 @@
#include "pathgridutil.hpp"
#include <osg/Geometry>
#include <osg/Material>
#include <components/esm/loadpgrd.hpp>
@ -174,6 +175,11 @@ namespace SceneUtil
gridGeometry->addPrimitiveSet(lineIndices);
gridGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
}
osg::ref_ptr<osg::Material> material = new osg::Material;
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
gridGeometry->getOrCreateStateSet()->setAttribute(material);
return gridGeometry;
}

View File

@ -8,6 +8,7 @@
#include <RecastDebugDraw.h>
#include <osg/Group>
#include <osg/Material>
#include <algorithm>
#include <vector>
@ -65,6 +66,11 @@ namespace SceneUtil
const auto texScale = 1.0f / (settings.mCellSize * 10.0f);
duDebugDrawTriMesh(&debugDraw, vertices.data(), static_cast<int>(vertices.size() / 3),
indices.data(), normals.data(), static_cast<int>(indices.size() / 3), nullptr, texScale);
osg::ref_ptr<osg::Material> material = new osg::Material;
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
group->getOrCreateStateSet()->setAttribute(material);
return group;
}
}

View File

@ -4,6 +4,8 @@
#include <sstream>
#include <iomanip>
#include <SDL_opengl_glext.h>
#include <osg/Node>
#include <osg/NodeVisitor>
#include <osg/TexGen>
@ -13,6 +15,20 @@
#include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/settings/settings.hpp>
#include <components/debug/debuglog.hpp>
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<osg::RenderBuffer> rbColor = new osg::RenderBuffer(width, height, colorTex->getInternalFormat(), samples, colorSamples);
osg::ref_ptr<osg::RenderBuffer> 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<osg::FrameBufferObject> mFbo;
osg::ref_ptr<osg::FrameBufferObject> 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<osg::Depth> 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<GLenum, 4> 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();
}
}

View File

@ -7,6 +7,7 @@
#include <osg/NodeCallback>
#include <osg/Texture2D>
#include <osg/Vec4f>
#include <osg/Depth>
#include <components/resource/resourcesystem.hpp>
@ -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<osg::Depth> 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

View File

@ -5,6 +5,8 @@
#include <osg/Material>
#include <osg/StateSet>
#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<osg::Depth> depth (new osg::Depth);
auto depth = createDepth();
depth->setWriteMask(false);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);

View File

@ -8,13 +8,16 @@
#include "world.hpp"
#include "../esm/loadland.hpp"
#include <components/resource/scenemanager.hpp>
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);

View File

@ -4,6 +4,11 @@
#include <map>
#include <osg/Group>
namespace Resource
{
class SceneManager;
}
namespace Terrain
{
class World;
@ -16,7 +21,7 @@ namespace Terrain
public:
typedef std::map<std::pair<int, int>, osg::ref_ptr<osg::Node> > 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;

View File

@ -8,6 +8,7 @@
#include <osg/BlendFunc>
#include <components/shader/shadermanager.hpp>
#include <components/sceneutil/util.hpp>
#include <mutex>
@ -105,9 +106,8 @@ namespace
osg::ref_ptr<osg::Depth> mValue;
LequalDepth()
: mValue(new osg::Depth)
: mValue(SceneUtil::createDepth())
{
mValue->setFunction(osg::Depth::LEQUAL);
}
};

View File

@ -4,6 +4,7 @@
#include <osg/Camera>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#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());

View File

@ -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.

View File

@ -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.

View File

@ -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}")

View File

@ -0,0 +1,8 @@
#version 120
#include "vertexcolors.glsl"
void main()
{
gl_FragData[0] = getDiffuseColor();
}

View File

@ -0,0 +1,12 @@
#version 120
uniform mat4 projectionMatrix;
varying vec4 passColor;
void main()
{
gl_Position = projectionMatrix * (gl_ModelViewMatrix * gl_Vertex);
passColor = gl_Color;
}

12
files/shaders/depth.glsl Normal file
View File

@ -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
}

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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));
}