mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-29 17:15:34 +00:00
support postprocess distortion
This commit is contained in:
parent
51cb3b08cb
commit
187f63d3d3
25 changed files with 311 additions and 65 deletions
|
@ -24,7 +24,7 @@ add_openmw_dir (mwrender
|
|||
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
|
||||
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging groundcover
|
||||
postprocessor pingpongcull luminancecalculator pingpongcanvas transparentpass precipitationocclusion ripples
|
||||
actorutil
|
||||
actorutil distortion
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
|
28
apps/openmw/mwrender/distortion.cpp
Normal file
28
apps/openmw/mwrender/distortion.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "distortion.hpp"
|
||||
|
||||
#include <osg/FrameBufferObject>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
void DistortionCallback::drawImplementation(
|
||||
osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous)
|
||||
{
|
||||
osg::State* state = renderInfo.getState();
|
||||
size_t frameId = state->getFrameStamp()->getFrameNumber() % 2;
|
||||
|
||||
mFBO[frameId]->apply(*state);
|
||||
|
||||
const osg::Texture* tex
|
||||
= mFBO[frameId]->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0).getTexture();
|
||||
|
||||
glViewport(0, 0, tex->getTextureWidth(), tex->getTextureHeight());
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
bin->drawImplementation(renderInfo, previous);
|
||||
|
||||
tex = mOriginalFBO[frameId]->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0).getTexture();
|
||||
glViewport(0, 0, tex->getTextureWidth(), tex->getTextureHeight());
|
||||
mOriginalFBO[frameId]->apply(*state);
|
||||
}
|
||||
}
|
28
apps/openmw/mwrender/distortion.hpp
Normal file
28
apps/openmw/mwrender/distortion.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include <array>
|
||||
|
||||
#include <osgUtil/RenderBin>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class FrameBufferObject;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class DistortionCallback : public osgUtil::RenderBin::DrawCallback
|
||||
{
|
||||
public:
|
||||
void drawImplementation(
|
||||
osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous) override;
|
||||
|
||||
void setFBO(const osg::ref_ptr<osg::FrameBufferObject>& fbo, size_t frameId) { mFBO[frameId] = fbo; }
|
||||
void setOriginalFBO(const osg::ref_ptr<osg::FrameBufferObject>& fbo, size_t frameId)
|
||||
{
|
||||
mOriginalFBO[frameId] = fbo;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<osg::ref_ptr<osg::FrameBufferObject>, 2> mFBO;
|
||||
std::array<osg::ref_ptr<osg::FrameBufferObject>, 2> mOriginalFBO;
|
||||
};
|
||||
}
|
|
@ -345,11 +345,9 @@ namespace MWRender
|
|||
bin->drawImplementation(renderInfo, previous);
|
||||
|
||||
auto primaryFBO = postProcessor->getPrimaryFbo(frameId);
|
||||
primaryFBO->apply(*state);
|
||||
|
||||
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
|
||||
postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)->apply(*state);
|
||||
else
|
||||
primaryFBO->apply(*state);
|
||||
postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId)->apply(*state);
|
||||
|
||||
// depth accumulation pass
|
||||
osg::ref_ptr<osg::StateSet> restore = bin->getStateSet();
|
||||
|
@ -357,8 +355,7 @@ namespace MWRender
|
|||
bin->drawImplementation(renderInfo, previous);
|
||||
bin->setStateSet(restore);
|
||||
|
||||
if (postProcessor->getFbo(PostProcessor::FBO_OpaqueDepth, frameId))
|
||||
primaryFBO->apply(*state);
|
||||
primaryFBO->apply(*state);
|
||||
|
||||
state->checkGLErrors("after DepthClearCallback::drawImplementation");
|
||||
}
|
||||
|
|
|
@ -242,6 +242,10 @@ namespace MWRender
|
|||
if (mTextureNormals)
|
||||
node.mRootStateSet->setTextureAttribute(PostProcessor::TextureUnits::Unit_Normals, mTextureNormals);
|
||||
|
||||
if (mTextureDistortion)
|
||||
node.mRootStateSet->setTextureAttribute(
|
||||
PostProcessor::TextureUnits::Unit_Distortion, mTextureDistortion);
|
||||
|
||||
state.pushStateSet(node.mRootStateSet);
|
||||
state.apply();
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ namespace MWRender
|
|||
|
||||
void setTextureNormals(osg::ref_ptr<osg::Texture> tex) { mTextureNormals = tex; }
|
||||
|
||||
void setTextureDistortion(osg::ref_ptr<osg::Texture> tex) { mTextureDistortion = tex; }
|
||||
|
||||
void setCalculateAvgLum(bool enabled) { mAvgLum = enabled; }
|
||||
|
||||
void setPostProcessing(bool enabled) { mPostprocessing = enabled; }
|
||||
|
@ -69,6 +71,7 @@ namespace MWRender
|
|||
osg::ref_ptr<osg::Texture> mTextureScene;
|
||||
osg::ref_ptr<osg::Texture> mTextureDepth;
|
||||
osg::ref_ptr<osg::Texture> mTextureNormals;
|
||||
osg::ref_ptr<osg::Texture> mTextureDistortion;
|
||||
|
||||
mutable bool mDirty = false;
|
||||
mutable std::vector<fx::Types::RenderTarget> mDirtyAttachments;
|
||||
|
|
|
@ -29,7 +29,9 @@
|
|||
|
||||
#include "../mwgui/postprocessorhud.hpp"
|
||||
|
||||
#include "distortion.hpp"
|
||||
#include "pingpongcull.hpp"
|
||||
#include "renderbin.hpp"
|
||||
#include "renderingmanager.hpp"
|
||||
#include "sky.hpp"
|
||||
#include "transparentpass.hpp"
|
||||
|
@ -103,6 +105,8 @@ namespace
|
|||
|
||||
return Stereo::createMultiviewCompatibleAttachment(texture);
|
||||
}
|
||||
|
||||
constexpr float DistortionRatio = 0.25;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
|
@ -118,6 +122,7 @@ namespace MWRender
|
|||
, mUsePostProcessing(Settings::postProcessing().mEnabled)
|
||||
, mSamples(Settings::video().mAntialiasing)
|
||||
, mPingPongCull(new PingPongCull(this))
|
||||
, mDistortionCallback(new DistortionCallback)
|
||||
{
|
||||
auto& shaderManager = mRendering.getResourceSystem()->getSceneManager()->getShaderManager();
|
||||
|
||||
|
@ -141,18 +146,45 @@ namespace MWRender
|
|||
mHUDCamera->setCullCallback(new HUDCullCallback);
|
||||
mViewer->getCamera()->addCullCallback(mPingPongCull);
|
||||
|
||||
if (Settings::shaders().mSoftParticles || Settings::postProcessing().mTransparentPostpass)
|
||||
{
|
||||
mTransparentDepthPostPass
|
||||
= new TransparentDepthBinCallback(shaderManager, Settings::postProcessing().mTransparentPostpass);
|
||||
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(mTransparentDepthPostPass);
|
||||
}
|
||||
// resolves the multisampled depth buffer and optionally draws an additional depth postpass
|
||||
mTransparentDepthPostPass
|
||||
= new TransparentDepthBinCallback(mRendering.getResourceSystem()->getSceneManager()->getShaderManager(),
|
||||
Settings::postProcessing().mTransparentPostpass);
|
||||
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(mTransparentDepthPostPass);
|
||||
|
||||
osg::ref_ptr<osgUtil::RenderBin> distortionRenderBin
|
||||
= new osgUtil::RenderBin(osgUtil::RenderBin::SORT_BACK_TO_FRONT);
|
||||
// This is silly to have to do, but if nothing is drawn then the drawcallback is never called and the distortion
|
||||
// texture will never be cleared
|
||||
osg::ref_ptr<osg::Node> dummyNodeToClear = new osg::Node;
|
||||
dummyNodeToClear->setCullingActive(false);
|
||||
dummyNodeToClear->getOrCreateStateSet()->setRenderBinDetails(RenderBin_Distortion, "Distortion");
|
||||
rootNode->addChild(dummyNodeToClear);
|
||||
distortionRenderBin->setDrawCallback(mDistortionCallback);
|
||||
distortionRenderBin->getStateSet()->setDefine("DISTORTION", "1", osg::StateAttribute::ON);
|
||||
|
||||
// Give the renderbin access to the opaque depth sampler so it can write its occlusion
|
||||
// Distorted geometry is drawn with ALWAYS depth function and depths writes disbled.
|
||||
const int unitSoftEffect
|
||||
= shaderManager.reserveGlobalTextureUnits(Shader::ShaderManager::Slot::OpaqueDepthTexture);
|
||||
distortionRenderBin->getStateSet()->addUniform(new osg::Uniform("opaqueDepthTex", unitSoftEffect));
|
||||
|
||||
osgUtil::RenderBin::addRenderBinPrototype("Distortion", distortionRenderBin);
|
||||
|
||||
auto defines = shaderManager.getGlobalDefines();
|
||||
defines["distorionRTRatio"] = std::to_string(DistortionRatio);
|
||||
shaderManager.setGlobalDefines(defines);
|
||||
|
||||
createObjectsForFrame(0);
|
||||
createObjectsForFrame(1);
|
||||
|
||||
populateTechniqueFiles();
|
||||
|
||||
auto distortion = loadTechnique("internal_distortion");
|
||||
distortion->setInternal(true);
|
||||
distortion->setLocked(true);
|
||||
mInternalTechniques.push_back(distortion);
|
||||
|
||||
osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext();
|
||||
osg::GLExtensions* ext = gc->getState()->get<osg::GLExtensions>();
|
||||
|
||||
|
@ -171,19 +203,6 @@ namespace MWRender
|
|||
else
|
||||
Log(Debug::Error) << "'glDisablei' unsupported, pass normals will not be available to shaders.";
|
||||
|
||||
if (Settings::shaders().mSoftParticles)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (Stereo::getMultiview())
|
||||
mTextures[i][Tex_OpaqueDepth] = new osg::Texture2DArray;
|
||||
else
|
||||
mTextures[i][Tex_OpaqueDepth] = new osg::Texture2D;
|
||||
mTextures[i][Tex_OpaqueDepth]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
mTextures[i][Tex_OpaqueDepth]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
}
|
||||
}
|
||||
|
||||
mGLSLVersion = ext->glslLanguageVersion * 100;
|
||||
mUBO = ext->isUniformBufferObjectSupported && mGLSLVersion >= 330;
|
||||
mStateUpdater = new fx::StateUpdater(mUBO);
|
||||
|
@ -281,17 +300,15 @@ namespace MWRender
|
|||
mCanvases[frameId]->setCalculateAvgLum(mHDR);
|
||||
|
||||
mCanvases[frameId]->setTextureScene(getTexture(Tex_Scene, frameId));
|
||||
if (mTransparentDepthPostPass)
|
||||
mCanvases[frameId]->setTextureDepth(getTexture(Tex_OpaqueDepth, frameId));
|
||||
else
|
||||
mCanvases[frameId]->setTextureDepth(getTexture(Tex_Depth, frameId));
|
||||
mCanvases[frameId]->setTextureDepth(getTexture(Tex_OpaqueDepth, frameId));
|
||||
mCanvases[frameId]->setTextureDistortion(getTexture(Tex_Distortion, frameId));
|
||||
|
||||
if (mTransparentDepthPostPass)
|
||||
{
|
||||
mTransparentDepthPostPass->mFbo[frameId] = mFbos[frameId][FBO_Primary];
|
||||
mTransparentDepthPostPass->mMsaaFbo[frameId] = mFbos[frameId][FBO_Multisample];
|
||||
mTransparentDepthPostPass->mOpaqueFbo[frameId] = mFbos[frameId][FBO_OpaqueDepth];
|
||||
}
|
||||
mTransparentDepthPostPass->mFbo[frameId] = mFbos[frameId][FBO_Primary];
|
||||
mTransparentDepthPostPass->mMsaaFbo[frameId] = mFbos[frameId][FBO_Multisample];
|
||||
mTransparentDepthPostPass->mOpaqueFbo[frameId] = mFbos[frameId][FBO_OpaqueDepth];
|
||||
|
||||
mDistortionCallback->setFBO(mFbos[frameId][FBO_Distortion], frameId);
|
||||
mDistortionCallback->setOriginalFBO(mFbos[frameId][FBO_Primary], frameId);
|
||||
|
||||
size_t frame = cv->getTraversalNumber();
|
||||
|
||||
|
@ -441,6 +458,13 @@ namespace MWRender
|
|||
textures[Tex_Normal]->setSourceFormat(GL_RGB);
|
||||
textures[Tex_Normal]->setInternalFormat(GL_RGB);
|
||||
|
||||
textures[Tex_Distortion]->setSourceFormat(GL_RGB);
|
||||
textures[Tex_Distortion]->setInternalFormat(GL_RGB);
|
||||
|
||||
Stereo::setMultiviewCompatibleTextureSize(
|
||||
textures[Tex_Distortion], width * DistortionRatio, height * DistortionRatio);
|
||||
textures[Tex_Distortion]->dirtyTextureObject();
|
||||
|
||||
auto setupDepth = [](osg::Texture* tex) {
|
||||
tex->setSourceFormat(GL_DEPTH_STENCIL_EXT);
|
||||
tex->setSourceType(SceneUtil::AutoDepth::depthSourceType());
|
||||
|
@ -448,16 +472,8 @@ namespace MWRender
|
|||
};
|
||||
|
||||
setupDepth(textures[Tex_Depth]);
|
||||
|
||||
if (!mTransparentDepthPostPass)
|
||||
{
|
||||
textures[Tex_OpaqueDepth] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
setupDepth(textures[Tex_OpaqueDepth]);
|
||||
textures[Tex_OpaqueDepth]->setName("opaqueTexMap");
|
||||
}
|
||||
setupDepth(textures[Tex_OpaqueDepth]);
|
||||
textures[Tex_OpaqueDepth]->setName("opaqueTexMap");
|
||||
|
||||
auto& fbos = mFbos[frameId];
|
||||
|
||||
|
@ -487,6 +503,7 @@ namespace MWRender
|
|||
auto normalRB = createFrameBufferAttachmentFromTemplate(
|
||||
Usage::RENDER_BUFFER, width, height, textures[Tex_Normal], mSamples);
|
||||
fbos[FBO_Multisample]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER1, normalRB);
|
||||
fbos[FBO_FirstPerson]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER1, normalRB);
|
||||
}
|
||||
auto depthRB = createFrameBufferAttachmentFromTemplate(
|
||||
Usage::RENDER_BUFFER, width, height, textures[Tex_Depth], mSamples);
|
||||
|
@ -510,12 +527,13 @@ namespace MWRender
|
|||
Stereo::createMultiviewCompatibleAttachment(textures[Tex_Normal]));
|
||||
}
|
||||
|
||||
if (textures[Tex_OpaqueDepth])
|
||||
{
|
||||
fbos[FBO_OpaqueDepth] = new osg::FrameBufferObject;
|
||||
fbos[FBO_OpaqueDepth]->setAttachment(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER,
|
||||
Stereo::createMultiviewCompatibleAttachment(textures[Tex_OpaqueDepth]));
|
||||
}
|
||||
fbos[FBO_OpaqueDepth] = new osg::FrameBufferObject;
|
||||
fbos[FBO_OpaqueDepth]->setAttachment(osg::FrameBufferObject::BufferComponent::PACKED_DEPTH_STENCIL_BUFFER,
|
||||
Stereo::createMultiviewCompatibleAttachment(textures[Tex_OpaqueDepth]));
|
||||
|
||||
fbos[FBO_Distortion] = new osg::FrameBufferObject;
|
||||
fbos[FBO_Distortion]->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
||||
Stereo::createMultiviewCompatibleAttachment(textures[Tex_Distortion]));
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (textures[Tex_OpaqueDepth])
|
||||
|
@ -575,6 +593,7 @@ namespace MWRender
|
|||
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerLastShader", Unit_LastShader));
|
||||
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerLastPass", Unit_LastPass));
|
||||
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerDepth", Unit_Depth));
|
||||
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerDistortion", Unit_Distortion));
|
||||
|
||||
if (mNormals)
|
||||
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerNormals", Unit_Normals));
|
||||
|
@ -582,6 +601,8 @@ namespace MWRender
|
|||
if (technique->getHDR())
|
||||
node.mRootStateSet->addUniform(new osg::Uniform("omw_EyeAdaptation", Unit_EyeAdaptation));
|
||||
|
||||
node.mRootStateSet->addUniform(new osg::Uniform("omw_SamplerDistortion", Unit_Distortion));
|
||||
|
||||
int texUnit = Unit_NextFree;
|
||||
|
||||
// user-defined samplers
|
||||
|
@ -681,7 +702,7 @@ namespace MWRender
|
|||
|
||||
disableTechnique(technique, false);
|
||||
|
||||
int pos = std::min<int>(location.value_or(mTechniques.size()), mTechniques.size());
|
||||
int pos = std::min<int>(location.value_or(mTechniques.size()) + mInternalTechniques.size(), mTechniques.size());
|
||||
|
||||
mTechniques.insert(mTechniques.begin() + pos, technique);
|
||||
dirtyTechniques(Settings::ShaderManager::get().getMode() == Settings::ShaderManager::Mode::Debug);
|
||||
|
@ -747,6 +768,11 @@ namespace MWRender
|
|||
{
|
||||
mTechniques.clear();
|
||||
|
||||
for (const auto& technique : mInternalTechniques)
|
||||
{
|
||||
mTechniques.push_back(technique);
|
||||
}
|
||||
|
||||
for (const std::string& techniqueName : Settings::postProcessing().mChain.get())
|
||||
{
|
||||
if (techniqueName.empty())
|
||||
|
@ -764,7 +790,7 @@ namespace MWRender
|
|||
|
||||
for (const auto& technique : mTechniques)
|
||||
{
|
||||
if (!technique || technique->getDynamic())
|
||||
if (!technique || technique->getDynamic() || technique->getInternal())
|
||||
continue;
|
||||
chain.push_back(technique->getName());
|
||||
}
|
||||
|
|
|
@ -50,12 +50,13 @@ namespace MWRender
|
|||
class PingPongCull;
|
||||
class PingPongCanvas;
|
||||
class TransparentDepthBinCallback;
|
||||
class DistortionCallback;
|
||||
|
||||
class PostProcessor : public osg::Group
|
||||
{
|
||||
public:
|
||||
using FBOArray = std::array<osg::ref_ptr<osg::FrameBufferObject>, 5>;
|
||||
using TextureArray = std::array<osg::ref_ptr<osg::Texture>, 5>;
|
||||
using FBOArray = std::array<osg::ref_ptr<osg::FrameBufferObject>, 6>;
|
||||
using TextureArray = std::array<osg::ref_ptr<osg::Texture>, 6>;
|
||||
using TechniqueList = std::vector<std::shared_ptr<fx::Technique>>;
|
||||
|
||||
enum TextureIndex
|
||||
|
@ -64,7 +65,8 @@ namespace MWRender
|
|||
Tex_Scene_LDR,
|
||||
Tex_Depth,
|
||||
Tex_OpaqueDepth,
|
||||
Tex_Normal
|
||||
Tex_Normal,
|
||||
Tex_Distortion,
|
||||
};
|
||||
|
||||
enum FBOIndex
|
||||
|
@ -73,7 +75,8 @@ namespace MWRender
|
|||
FBO_Multisample,
|
||||
FBO_FirstPerson,
|
||||
FBO_OpaqueDepth,
|
||||
FBO_Intercept
|
||||
FBO_Intercept,
|
||||
FBO_Distortion,
|
||||
};
|
||||
|
||||
enum TextureUnits
|
||||
|
@ -83,6 +86,7 @@ namespace MWRender
|
|||
Unit_Depth,
|
||||
Unit_EyeAdaptation,
|
||||
Unit_Normals,
|
||||
Unit_Distortion,
|
||||
Unit_NextFree
|
||||
};
|
||||
|
||||
|
@ -223,6 +227,7 @@ namespace MWRender
|
|||
TechniqueList mTechniques;
|
||||
TechniqueList mTemplates;
|
||||
TechniqueList mQueuedTemplates;
|
||||
TechniqueList mInternalTechniques;
|
||||
|
||||
std::unordered_map<std::string, std::filesystem::path> mTechniqueFileMap;
|
||||
|
||||
|
@ -258,6 +263,7 @@ namespace MWRender
|
|||
osg::ref_ptr<PingPongCull> mPingPongCull;
|
||||
std::array<osg::ref_ptr<PingPongCanvas>, 2> mCanvases;
|
||||
osg::ref_ptr<TransparentDepthBinCallback> mTransparentDepthPostPass;
|
||||
osg::ref_ptr<DistortionCallback> mDistortionCallback;
|
||||
|
||||
fx::DispatchArray mTemplateData;
|
||||
};
|
||||
|
|
|
@ -13,7 +13,8 @@ namespace MWRender
|
|||
RenderBin_DepthSorted = 10, // osg::StateSet::TRANSPARENT_BIN
|
||||
RenderBin_OcclusionQuery = 11,
|
||||
RenderBin_FirstPerson = 12,
|
||||
RenderBin_SunGlare = 13
|
||||
RenderBin_SunGlare = 13,
|
||||
RenderBin_Distortion = 14,
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -502,6 +502,7 @@ namespace MWRender
|
|||
sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat);
|
||||
sceneRoot->getOrCreateStateSet()->addUniform(new osg::Uniform("emissiveMult", 1.f));
|
||||
sceneRoot->getOrCreateStateSet()->addUniform(new osg::Uniform("specStrength", 1.f));
|
||||
sceneRoot->getOrCreateStateSet()->addUniform(new osg::Uniform("distortionStrength", 0.f));
|
||||
|
||||
mFog = std::make_unique<FogManager>();
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ uniform @builtinSampler omw_SamplerLastShader;
|
|||
uniform @builtinSampler omw_SamplerLastPass;
|
||||
uniform @builtinSampler omw_SamplerDepth;
|
||||
uniform @builtinSampler omw_SamplerNormals;
|
||||
uniform @builtinSampler omw_SamplerDistortion;
|
||||
|
||||
uniform vec4 omw_PointLights[@pointLightCount];
|
||||
uniform int omw_PointLightsCount;
|
||||
|
|
|
@ -175,6 +175,9 @@ namespace fx
|
|||
void setLocked(bool locked) { mLocked = locked; }
|
||||
bool getLocked() const { return mLocked; }
|
||||
|
||||
void setInternal(bool internal) { mInternal = internal; }
|
||||
bool getInternal() const { return mInternal; }
|
||||
|
||||
private:
|
||||
[[noreturn]] void error(const std::string& msg);
|
||||
|
||||
|
@ -295,6 +298,7 @@ namespace fx
|
|||
|
||||
bool mDynamic = false;
|
||||
bool mLocked = false;
|
||||
bool mInternal = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
|
|
|
@ -108,6 +108,8 @@ namespace Nif
|
|||
enum BSShaderFlags1
|
||||
{
|
||||
BSSFlag1_Specular = 0x00000001,
|
||||
BSSFlag1_Refraction = 0x00008000,
|
||||
BSSFlag1_FireRefraction = 0x00010000,
|
||||
BSSFlag1_Decal = 0x04000000,
|
||||
BSSFlag1_DepthTest = 0x80000000,
|
||||
};
|
||||
|
@ -148,6 +150,8 @@ namespace Nif
|
|||
bool decal() const { return mShaderFlags1 & BSSFlag1_Decal; }
|
||||
bool depthTest() const { return mShaderFlags1 & BSSFlag1_DepthTest; }
|
||||
bool depthWrite() const { return mShaderFlags2 & BSSFlag2_DepthWrite; }
|
||||
bool refraction() const { return mShaderFlags1 & BSSFlag1_Refraction; }
|
||||
bool fireRefraction() const { return mShaderFlags1 & BSSFlag1_FireRefraction; }
|
||||
};
|
||||
|
||||
struct BSShaderLightingProperty : BSShaderProperty
|
||||
|
|
|
@ -2381,6 +2381,8 @@ namespace NifOsg
|
|||
textureSet, texprop->mClamp, node->getName(), stateset, imageManager, boundTextures);
|
||||
}
|
||||
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
|
||||
if (texprop->refraction())
|
||||
SceneUtil::setupDistortion(*node, texprop->mRefraction.mStrength);
|
||||
break;
|
||||
}
|
||||
case Nif::RC_BSShaderNoLightingProperty:
|
||||
|
@ -2438,6 +2440,8 @@ namespace NifOsg
|
|||
if (texprop->treeAnim())
|
||||
stateset->addUniform(new osg::Uniform("useTreeAnim", true));
|
||||
handleDepthFlags(stateset, texprop->depthTest(), texprop->depthWrite());
|
||||
if (texprop->refraction())
|
||||
SceneUtil::setupDistortion(*node, texprop->mRefractionStrength);
|
||||
break;
|
||||
}
|
||||
case Nif::RC_BSEffectShaderProperty:
|
||||
|
|
|
@ -649,6 +649,7 @@ namespace Resource
|
|||
node->getOrCreateStateSet()->addUniform(new osg::Uniform("specStrength", 1.f));
|
||||
node->getOrCreateStateSet()->addUniform(new osg::Uniform("envMapColor", osg::Vec4f(1, 1, 1, 1)));
|
||||
node->getOrCreateStateSet()->addUniform(new osg::Uniform("useFalloff", false));
|
||||
node->getOrCreateStateSet()->addUniform(new osg::Uniform("distortionStrength", 0.f));
|
||||
}
|
||||
|
||||
node->setUserValue(Misc::OsgUserValues::sFileHash,
|
||||
|
|
|
@ -29,6 +29,19 @@ namespace SceneUtil
|
|||
node.setUserValue(Misc::OsgUserValues::sXSoftEffect, true);
|
||||
}
|
||||
|
||||
void setupDistortion(osg::Node& node, float distortionStrength)
|
||||
{
|
||||
static const osg::ref_ptr<SceneUtil::AutoDepth> depth
|
||||
= new SceneUtil::AutoDepth(osg::Depth::ALWAYS, 0, 1, false);
|
||||
|
||||
osg::StateSet* stateset = node.getOrCreateStateSet();
|
||||
|
||||
stateset->setRenderBinDetails(14, "Distortion", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS);
|
||||
stateset->addUniform(new osg::Uniform("distortionStrength", distortionStrength));
|
||||
|
||||
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
|
||||
}
|
||||
|
||||
void ProcessExtraDataVisitor::apply(osg::Node& node)
|
||||
{
|
||||
if (!mSceneMgr->getSoftParticles())
|
||||
|
@ -54,6 +67,12 @@ namespace SceneUtil
|
|||
|
||||
setupSoftEffect(node, size, falloff, falloffDepth);
|
||||
}
|
||||
else if (key == "distortion")
|
||||
{
|
||||
auto strength = it.second["strength"].as<float>(0.1f);
|
||||
|
||||
setupDistortion(node, strength);
|
||||
}
|
||||
}
|
||||
|
||||
node.setUserValue(Misc::OsgUserValues::sExtraData, std::string{});
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace osg
|
|||
namespace SceneUtil
|
||||
{
|
||||
void setupSoftEffect(osg::Node& node, float size, bool falloff, float falloffDepth);
|
||||
void setupDistortion(osg::Node& node, float distortionStrength);
|
||||
|
||||
class ProcessExtraDataVisitor : public osg::NodeVisitor
|
||||
{
|
||||
|
|
|
@ -54,3 +54,38 @@ Example usage.
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Distortion
|
||||
----------
|
||||
|
||||
This effect is used to imitate effects such as refraction and heat distortion. A common use case is to assign a normal map to the
|
||||
diffuse slot to a material and add uv scrolling. The red and green channels of the texture are used to offset the final scene texture.
|
||||
Blue and alpha channels are ignored.
|
||||
|
||||
To use this feature the :ref:`post processing` setting must be enabled.
|
||||
This setting can either be activated in the OpenMW launcher, in-game, or changed in `settings.cfg`:
|
||||
|
||||
::
|
||||
|
||||
[Post Processing]
|
||||
enabled = false
|
||||
|
||||
Variables.
|
||||
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| Name | Description | Type | Default |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
| strength| The strength of the distortion effect. Scales linearly. | float | 0.1 |
|
||||
+---------+--------------------------------------------------------------------------------------------------------+---------+---------+
|
||||
|
||||
Example usage.
|
||||
|
||||
::
|
||||
|
||||
omw:data {
|
||||
"shader" : {
|
||||
"distortion" : {
|
||||
"strength": 0.12,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,6 +96,7 @@ set(BUILTIN_DATA_FILES
|
|||
shaders/adjustments.omwfx
|
||||
shaders/bloomlinear.omwfx
|
||||
shaders/debug.omwfx
|
||||
shaders/internal_distortion.omwfx
|
||||
|
||||
mygui/core.skin
|
||||
mygui/core.xml
|
||||
|
|
25
files/data/shaders/internal_distortion.omwfx
Normal file
25
files/data/shaders/internal_distortion.omwfx
Normal file
|
@ -0,0 +1,25 @@
|
|||
fragment main {
|
||||
|
||||
omw_In vec2 omw_TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
const float multiplier = 0.14;
|
||||
|
||||
vec2 offset = omw_Texture2D(omw_SamplerDistortion, omw_TexCoord).rg;
|
||||
offset *= multiplier;
|
||||
offset = clamp(offset, vec2(-1.0), vec2(1.0));
|
||||
|
||||
float occlusionFactor = omw_Texture2D(omw_SamplerDistortion, omw_TexCoord+offset).b;
|
||||
|
||||
omw_FragColor = mix(omw_GetLastShader(omw_TexCoord + offset), omw_GetLastShader(omw_TexCoord), occlusionFactor);
|
||||
}
|
||||
}
|
||||
|
||||
technique {
|
||||
description = "Internal refraction shader for OpenMW";
|
||||
version = "1.0";
|
||||
author = "OpenMW";
|
||||
passes = main;
|
||||
flags = hidden;
|
||||
}
|
|
@ -16,6 +16,7 @@ set(SHADER_FILES
|
|||
lib/particle/occlusion.glsl
|
||||
lib/util/quickstep.glsl
|
||||
lib/util/coordinates.glsl
|
||||
lib/util/distortion.glsl
|
||||
lib/core/fragment.glsl
|
||||
lib/core/fragment.h.glsl
|
||||
lib/core/fragment_multiview.glsl
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#version 120
|
||||
#pragma import_defines(FORCE_OPAQUE)
|
||||
#pragma import_defines(FORCE_OPAQUE, DISTORTION)
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
|
@ -26,6 +26,8 @@ uniform sampler2D normalMap;
|
|||
varying vec2 normalMapUV;
|
||||
#endif
|
||||
|
||||
uniform sampler2D opaqueDepthTex;
|
||||
|
||||
varying float euclideanDepth;
|
||||
varying float linearDepth;
|
||||
|
||||
|
@ -38,9 +40,11 @@ uniform float alphaRef;
|
|||
uniform float emissiveMult;
|
||||
uniform float specStrength;
|
||||
uniform bool useTreeAnim;
|
||||
uniform float distortionStrength;
|
||||
|
||||
#include "lib/light/lighting.glsl"
|
||||
#include "lib/material/alpha.glsl"
|
||||
#include "lib/util/distortion.glsl"
|
||||
|
||||
#include "compatibility/vertexcolors.glsl"
|
||||
#include "compatibility/shadows_fragment.glsl"
|
||||
|
@ -51,6 +55,15 @@ void main()
|
|||
{
|
||||
#if @diffuseMap
|
||||
gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV);
|
||||
|
||||
#if defined(DISTORTION) && DISTORTION
|
||||
vec2 screenCoords = gl_FragCoord.xy / (screenRes * @distorionRTRatio);
|
||||
gl_FragData[0].a = getDiffuseColor().a;
|
||||
gl_FragData[0] = applyDistortion(gl_FragData[0], distortionStrength, gl_FragCoord.z, texture2D(opaqueDepthTex, screenCoords).x);
|
||||
|
||||
return;
|
||||
#endif
|
||||
|
||||
gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, diffuseMapUV);
|
||||
#else
|
||||
gl_FragData[0] = vec4(1.0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#version 120
|
||||
#pragma import_defines(FORCE_OPAQUE)
|
||||
#pragma import_defines(FORCE_OPAQUE, DISTORTION)
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
|
@ -66,6 +66,7 @@ uniform vec2 screenRes;
|
|||
uniform float near;
|
||||
uniform float far;
|
||||
uniform float alphaRef;
|
||||
uniform float distortionStrength;
|
||||
|
||||
#define PER_PIXEL_LIGHTING (@normalMap || @specularMap || @forcePPL)
|
||||
|
||||
|
@ -91,6 +92,7 @@ varying vec4 passTangent;
|
|||
#include "lib/light/lighting.glsl"
|
||||
#include "lib/material/parallax.glsl"
|
||||
#include "lib/material/alpha.glsl"
|
||||
#include "lib/util/distortion.glsl"
|
||||
|
||||
#include "fog.glsl"
|
||||
#include "vertexcolors.glsl"
|
||||
|
@ -100,7 +102,6 @@ varying vec4 passTangent;
|
|||
#if @softParticles
|
||||
#include "lib/particle/soft.glsl"
|
||||
|
||||
uniform sampler2D opaqueDepthTex;
|
||||
uniform float particleSize;
|
||||
uniform bool particleFade;
|
||||
uniform float softFalloffDepth;
|
||||
|
@ -112,6 +113,8 @@ uniform sampler2D orthoDepthMap;
|
|||
varying vec3 orthoDepthMapCoord;
|
||||
#endif
|
||||
|
||||
uniform sampler2D opaqueDepthTex;
|
||||
|
||||
void main()
|
||||
{
|
||||
#if @particleOcclusion
|
||||
|
@ -133,8 +136,17 @@ void main()
|
|||
offset = getParallaxOffset(transpose(normalToViewMatrix) * normalize(-passViewPos), height, flipY);
|
||||
#endif
|
||||
|
||||
vec2 screenCoords = gl_FragCoord.xy / screenRes;
|
||||
|
||||
#if @diffuseMap
|
||||
gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV + offset);
|
||||
|
||||
#if defined(DISTORTION) && DISTORTION
|
||||
gl_FragData[0].a = getDiffuseColor().a;
|
||||
gl_FragData[0] = applyDistortion(gl_FragData[0], distortionStrength, gl_FragCoord.z, texture2D(opaqueDepthTex, screenCoords / @distorionRTRatio).x);
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if @diffuseParallax
|
||||
gl_FragData[0].a = 1.0;
|
||||
#else
|
||||
|
@ -234,7 +246,6 @@ void main()
|
|||
|
||||
gl_FragData[0] = applyFogAtPos(gl_FragData[0], passViewPos, far);
|
||||
|
||||
vec2 screenCoords = gl_FragCoord.xy / screenRes;
|
||||
#if !defined(FORCE_OPAQUE) && @softParticles
|
||||
gl_FragData[0].a *= calcSoftParticleFade(
|
||||
viewVec,
|
||||
|
|
32
files/shaders/lib/util/distortion.glsl
Normal file
32
files/shaders/lib/util/distortion.glsl
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef LIB_UTIL_DISTORTION
|
||||
#define LIB_UTIL_DISTORTION
|
||||
|
||||
vec4 applyDistortion(in vec4 color, in float strength, in float pixelDepth, in float sceneDepth)
|
||||
{
|
||||
vec4 distortion = color;
|
||||
float invOcclusion = 1.0;
|
||||
|
||||
// TODO: Investigate me. Alpha-clipping is enabled for refraction for what seems an arbitrary threshold, even when
|
||||
// there are no associated NIF properties.
|
||||
if (distortion.a < 0.1)
|
||||
discard;
|
||||
|
||||
distortion.b = 0.0;
|
||||
|
||||
#if @reverseZ
|
||||
if (pixelDepth < sceneDepth)
|
||||
#else
|
||||
if (pixelDepth > sceneDepth)
|
||||
#endif
|
||||
{
|
||||
invOcclusion = 0.0;
|
||||
distortion.b = 1.0;
|
||||
}
|
||||
distortion.rg = color.rg * 2.0 - 1.0;
|
||||
|
||||
distortion.rg *= invOcclusion * strength;
|
||||
|
||||
return distortion;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -4,8 +4,8 @@
|
|||
float quickstep(float x)
|
||||
{
|
||||
x = clamp(x, 0.0, 1.0);
|
||||
x = 1.0 - x*x;
|
||||
x = 1.0 - x*x;
|
||||
x = 1.0 - x * x;
|
||||
x = 1.0 - x * x;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue