soft particles

macos-builds-only-for-openmw
glassmancody.info 3 years ago
parent a78dee740a
commit 8c3b00164e

@ -93,6 +93,41 @@ namespace
MWRender::PostProcessor* mPostProcessor;
};
// Copies the currently bound depth attachment to a new texture so drawables in transparent renderbin can safely sample from depth.
class OpaqueDepthCopyCallback : public osgUtil::RenderBin::DrawCallback
{
public:
OpaqueDepthCopyCallback(osg::ref_ptr<osg::Texture2D> opaqueDepthTex, osg::ref_ptr<osg::FrameBufferObject> sourceFbo)
: mOpaqueDepthFbo(new osg::FrameBufferObject)
, mSourceFbo(sourceFbo)
, mOpaqueDepthTex(opaqueDepthTex)
{
mOpaqueDepthFbo->setAttachment(osg::FrameBufferObject::BufferComponent::DEPTH_BUFFER, osg::FrameBufferAttachment(opaqueDepthTex));
}
void drawImplementation(osgUtil::RenderBin* bin, osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous) override
{
if (bin->getStage()->getFrameBufferObject() == mSourceFbo)
{
osg::State& state = *renderInfo.getState();
osg::GLExtensions* ext = state.get<osg::GLExtensions>();
mSourceFbo->apply(state, osg::FrameBufferObject::READ_FRAMEBUFFER);
mOpaqueDepthFbo->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
ext->glBlitFramebuffer(0, 0, mOpaqueDepthTex->getTextureWidth(), mOpaqueDepthTex->getTextureHeight(), 0, 0, mOpaqueDepthTex->getTextureWidth(), mOpaqueDepthTex->getTextureHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST);
mSourceFbo->apply(state);
}
bin->drawImplementation(renderInfo, previous);
}
private:
osg::ref_ptr<osg::FrameBufferObject> mOpaqueDepthFbo;
osg::ref_ptr<osg::FrameBufferObject> mSourceFbo;
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
};
}
namespace MWRender
@ -103,7 +138,9 @@ namespace MWRender
, mDepthFormat(GL_DEPTH_COMPONENT24)
, mRendering(rendering)
{
if (!SceneUtil::getReverseZ())
bool softParticles = Settings::Manager::getBool("soft particles", "Shaders");
if (!SceneUtil::getReverseZ() && !softParticles)
return;
osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext();
@ -124,17 +161,22 @@ namespace MWRender
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
if (SceneUtil::getReverseZ())
{
// 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.
Log(Debug::Warning) << errPreamble << "'GL_ARB_depth_buffer_float' and 'GL_NV_depth_buffer_float' unsupported.";
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.
Log(Debug::Warning) << errPreamble << "'GL_ARB_depth_buffer_float' and 'GL_NV_depth_buffer_float' unsupported.";
if (!softParticles)
return;
}
}
int width = viewer->getCamera()->getViewport()->width();
@ -165,6 +207,12 @@ namespace MWRender
mDepthTex->dirtyTextureObject();
mSceneTex->dirtyTextureObject();
if (mOpaqueDepthTex)
{
mOpaqueDepthTex->setTextureSize(width, height);
mOpaqueDepthTex->dirtyTextureObject();
}
int samples = Settings::Manager::getInt("antialiasing", "Video");
mFbo = new osg::FrameBufferObject;
@ -186,6 +234,9 @@ namespace MWRender
if (const auto depthProxy = std::getenv("OPENMW_ENABLE_DEPTH_CLEAR_PROXY"))
mFirstPersonDepthRBProxy = new osg::RenderBuffer(width, height, mDepthTex->getInternalFormat(), samples);
if (Settings::Manager::getBool("soft particles", "Shaders"))
osgUtil::RenderBin::getRenderBinPrototype("DepthSortedBin")->setDrawCallback(new OpaqueDepthCopyCallback(mOpaqueDepthTex, mMsaaFbo ? mMsaaFbo : mFbo));
mViewer->getCamera()->resize(width, height);
mHUDCamera->resize(width, height);
mRendering.updateProjectionMatrix();
@ -204,6 +255,12 @@ namespace MWRender
mDepthTex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
mDepthTex->setResizeNonPowerOfTwoHint(false);
if (Settings::Manager::getBool("soft particles", "Shaders"))
{
mOpaqueDepthTex = new osg::Texture2D(*mDepthTex);
mOpaqueDepthTex->setName("opaqueTexMap");
}
mSceneTex = new osg::Texture2D;
mSceneTex->setTextureSize(width, height);
mSceneTex->setSourceFormat(GL_RGB);

@ -26,6 +26,7 @@ namespace MWRender
auto getFirstPersonRBProxy() { return mFirstPersonDepthRBProxy; }
int getDepthFormat() { return mDepthFormat; }
osg::ref_ptr<osg::Texture2D> getOpaqueDepthTex() { return mOpaqueDepthTex; }
void resize(int width, int height);
@ -42,6 +43,7 @@ namespace MWRender
osg::ref_ptr<osg::Texture2D> mSceneTex;
osg::ref_ptr<osg::Texture2D> mDepthTex;
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
int mDepthFormat;

@ -91,6 +91,7 @@ namespace MWRender
stateset->addUniform(new osg::Uniform("linearFac", 0.f));
stateset->addUniform(new osg::Uniform("near", 0.f));
stateset->addUniform(new osg::Uniform("far", 0.f));
stateset->addUniform(new osg::Uniform("screenRes", osg::Vec2f{}));
if (mUsePlayerUniforms)
{
stateset->addUniform(new osg::Uniform("windSpeed", 0.0f));
@ -116,6 +117,10 @@ namespace MWRender
if (uFar)
uFar->set(mFar);
auto* uScreenRes = stateset->getUniform("screenRes");
if (uScreenRes)
uScreenRes->set(mScreenRes);
if (mUsePlayerUniforms)
{
auto* windSpeed = stateset->getUniform("windSpeed");
@ -148,6 +153,11 @@ namespace MWRender
mFar = far;
}
void setScreenRes(float width, float height)
{
mScreenRes = osg::Vec2f(width, height);
}
void setWindSpeed(float windSpeed)
{
mWindSpeed = windSpeed;
@ -167,6 +177,7 @@ namespace MWRender
bool mUsePlayerUniforms;
float mWindSpeed;
osg::Vec3f mPlayerPos;
osg::Vec2f mScreenRes;
};
class StateUpdater : public SceneUtil::StateSetUpdater
@ -453,6 +464,7 @@ namespace MWRender
mPostProcessor = new PostProcessor(*this, viewer, mRootNode);
resourceSystem->getSceneManager()->setDepthFormat(mPostProcessor->getDepthFormat());
resourceSystem->getSceneManager()->setOpaqueDepthTex(mPostProcessor->getOpaqueDepthTex());
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.";
@ -1166,6 +1178,7 @@ namespace MWRender
mSharedUniformStateUpdater->setNear(mNearClip);
mSharedUniformStateUpdater->setFar(mViewDistance);
mSharedUniformStateUpdater->setScreenRes(mViewer->getCamera()->getViewport()->width(), mViewer->getCamera()->getViewport()->height());
// 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.

@ -1045,6 +1045,7 @@ namespace NifOsg
void handleParticleSystem(const Nif::Node *nifNode, osg::Group *parentNode, SceneUtil::CompositeStateSetUpdater* composite, int animflags)
{
osg::ref_ptr<ParticleSystem> partsys (new ParticleSystem);
partsys->getOrCreateStateSet();
partsys->setSortMode(osgParticle::ParticleSystem::SORT_BACK_TO_FRONT);
const Nif::NiParticleSystemController* partctrl = nullptr;

@ -429,6 +429,11 @@ namespace Resource
mConvertAlphaTestToAlphaToCoverage = convert;
}
void SceneManager::setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texture)
{
mOpaqueDepthTex = texture;
}
SceneManager::~SceneManager()
{
// this has to be defined in the .cpp file as we can't delete incomplete types
@ -891,6 +896,7 @@ namespace Resource
shaderVisitor->setSpecularMapPattern(mSpecularMapPattern);
shaderVisitor->setApplyLightingToEnvMaps(mApplyLightingToEnvMaps);
shaderVisitor->setConvertAlphaTestToAlphaToCoverage(mConvertAlphaTestToAlphaToCoverage);
shaderVisitor->setOpaqueDepthTex(mOpaqueDepthTex);
return shaderVisitor;
}
}

@ -9,6 +9,7 @@
#include <osg/ref_ptr>
#include <osg/Node>
#include <osg/Texture>
#include <osg/Texture2D>
#include "resourcemanager.hpp"
@ -111,6 +112,8 @@ namespace Resource
void setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported);
bool isSupportedLightingMethod(SceneUtil::LightingMethod method) const;
void setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texture);
enum class UBOBinding
{
// If we add more UBO's, we should probably assign their bindings dynamically according to the current count of UBO's in the programTemplate
@ -209,6 +212,7 @@ namespace Resource
SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods;
bool mConvertAlphaTestToAlphaToCoverage;
GLenum mDepthFormat;
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
osg::ref_ptr<Resource::SharedStateManager> mSharedStateManager;
mutable std::mutex mSharedStateMutex;

@ -11,6 +11,8 @@
#include <osg/Texture>
#include <osg/ValueObject>
#include <osgParticle/ParticleSystem>
#include <osgUtil/TangentSpaceGenerator>
#include <components/debug/debuglog.hpp>
@ -19,6 +21,7 @@
#include <components/vfs/manager.hpp>
#include <components/sceneutil/riggeometry.hpp>
#include <components/sceneutil/morphgeometry.hpp>
#include <components/sceneutil/util.hpp>
#include "removedalphafunc.hpp"
#include "shadermanager.hpp"
@ -554,6 +557,18 @@ namespace Shader
updateAddedState(*writableUserData, addedState);
}
if (auto partsys = dynamic_cast<osgParticle::ParticleSystem*>(&node))
{
writableStateSet->setDefine("SOFT_PARTICLES", "1", osg::StateAttribute::ON);
auto depth = SceneUtil::createDepth();
depth->setWriteMask(false);
writableStateSet->setAttributeAndModes(depth, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
writableStateSet->addUniform(new osg::Uniform("particleSize", partsys->getDefaultParticleTemplate().getSizeRange().maximum));
writableStateSet->addUniform(new osg::Uniform("opaqueDepthTex", 2));
writableStateSet->setTextureAttributeAndModes(2, mOpaqueDepthTex, osg::StateAttribute::ON);
}
std::string shaderPrefix;
if (!node.getUserValue("shaderPrefix", shaderPrefix))
shaderPrefix = mDefaultShaderPrefix;
@ -769,6 +784,11 @@ namespace Shader
mConvertAlphaTestToAlphaToCoverage = convert;
}
void ShaderVisitor::setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texture)
{
mOpaqueDepthTex = texture;
}
ReinstateRemovedStateVisitor::ReinstateRemovedStateVisitor(bool allowedToModifyStateSets)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mAllowedToModifyStateSets(allowedToModifyStateSets)

@ -3,6 +3,7 @@
#include <osg/NodeVisitor>
#include <osg/Program>
#include <osg/Texture2D>
namespace Resource
{
@ -45,6 +46,8 @@ namespace Shader
void setConvertAlphaTestToAlphaToCoverage(bool convert);
void setOpaqueDepthTex(osg::ref_ptr<osg::Texture2D> texture);
void apply(osg::Node& node) override;
void apply(osg::Drawable& drawable) override;
@ -110,6 +113,7 @@ namespace Shader
bool adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs);
osg::ref_ptr<const osg::Program> mProgramTemplate;
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
};
class ReinstateRemovedStateVisitor : public osg::NodeVisitor

@ -269,3 +269,16 @@ antialias alpha test
Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage when :ref:`antialiasing` is on.
This allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation.
When MSAA is off, this setting will have no visible effect, but might have a performance cost.
soft particles
------------------------
:Type: boolean
:Range: True/False
:Default: False
Enables soft particles for almost all particle effects, excluding precipitation.
This technique softens the intersection between individual particles and other
opaque geometry by blending between them. Note, this relies on overriding
specific properties of particle systems that potentially differ from the source
content, this setting may change the look of some particle systems.

@ -494,6 +494,8 @@ minimum interior brightness = 0.08
# When MSAA is off, this setting will have no visible effect, but might have a performance cost.
antialias alpha test = false
soft particles = true
[Input]
# Capture control of the cursor prevent movement outside the window.

@ -39,6 +39,7 @@ set(SHADER_FILES
sky_vertex.glsl
sky_fragment.glsl
skypasses.glsl
softparticles.glsl
)
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")

@ -1,5 +1,6 @@
#version 120
#pragma import_defines(FORCE_OPAQUE)
#pragma import_defines(SOFT_PARTICLES)
#if @useUBO
#extension GL_ARB_uniform_buffer_object : require
@ -80,6 +81,10 @@ varying vec3 passNormal;
#include "parallax.glsl"
#include "alpha.glsl"
#if defined(SOFT_PARTICLES) && SOFT_PARTICLES
#include "softparticles.glsl"
#endif
void main()
{
#if @diffuseMap
@ -220,6 +225,10 @@ void main()
#endif
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
#if defined(SOFT_PARTICLES) && SOFT_PARTICLES
gl_FragData[0].a *= calcSoftParticleFade();
#endif
#if defined(FORCE_OPAQUE) && FORCE_OPAQUE
// having testing & blending isn't enough - we need to write an opaque pixel to be opaque
gl_FragData[0].a = 1.0;

@ -0,0 +1,32 @@
uniform float near;
uniform float far;
uniform sampler2D opaqueDepthTex;
uniform vec2 screenRes;
uniform float particleSize;
float viewDepth(float depth)
{
#if @reverseZ
depth = 1.0 - depth;
#endif
return (near * far) / ((far - near) * depth - far);
}
float calcSoftParticleFade()
{
const float falloffMultiplier = 0.33;
const float contrast = 1.30;
vec2 screenCoords = gl_FragCoord.xy / screenRes;
float sceneDepth = viewDepth(texture2D(opaqueDepthTex, screenCoords).x);
float particleDepth = viewDepth(gl_FragCoord.z);
float falloff = particleSize * falloffMultiplier;
float delta = particleDepth - sceneDepth;
if (delta < 0.0)
discard;
const float shift = 0.845;
return shift * pow(clamp(delta/falloff, 0.0, 1.0), contrast);
}
Loading…
Cancel
Save