mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-06 01:49:42 +00:00
Merge branch 'leave_butter_to_soften' into 'master'
Soft Particles (#6128) See merge request OpenMW/openmw!980
This commit is contained in:
commit
5836d0225f
16 changed files with 198 additions and 12 deletions
|
@ -85,6 +85,7 @@
|
|||
Feature #6017: Separate persistent and temporary cell references when saving
|
||||
Feature #6032: Reverse-z depth buffer
|
||||
Feature #6078: First person should not clear depth buffer
|
||||
Feature #6128: Soft Particles
|
||||
Feature #6161: Refactor Sky to use shaders and GLES/GL3 friendly
|
||||
Feature #6162: Refactor GUI to use shaders and to be GLES and GL3+ friendly
|
||||
Feature #6199: Support FBO Rendering
|
||||
|
|
|
@ -117,6 +117,7 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
loadSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
|
||||
loadSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
|
||||
loadSettingBool(radialFogCheckBox, "radial fog", "Shaders");
|
||||
loadSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
|
||||
loadSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
|
||||
connect(animSourcesCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotAnimSourcesToggled(bool)));
|
||||
loadSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
|
||||
|
@ -270,6 +271,7 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
saveSettingBool(autoUseTerrainSpecularMapsCheckBox, "auto use terrain specular maps", "Shaders");
|
||||
saveSettingBool(bumpMapLocalLightingCheckBox, "apply lighting to environment maps", "Shaders");
|
||||
saveSettingBool(radialFogCheckBox, "radial fog", "Shaders");
|
||||
saveSettingBool(softParticlesCheckBox, "soft particles", "Shaders");
|
||||
saveSettingBool(magicItemAnimationsCheckBox, "use magic item animations", "Game");
|
||||
saveSettingBool(animSourcesCheckBox, "use additional anim sources", "Game");
|
||||
saveSettingBool(weaponSheathingCheckBox, "weapon sheathing", "Game");
|
||||
|
|
|
@ -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
|
||||
|
@ -452,6 +463,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.";
|
||||
|
@ -1156,6 +1168,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;
|
||||
|
|
|
@ -430,6 +430,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
|
||||
|
@ -892,6 +897,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,27 @@ namespace Shader
|
|||
updateAddedState(*writableUserData, addedState);
|
||||
}
|
||||
|
||||
bool softParticles = false;
|
||||
|
||||
if (mOpaqueDepthTex)
|
||||
{
|
||||
auto partsys = dynamic_cast<osgParticle::ParticleSystem*>(&node);
|
||||
|
||||
if (partsys)
|
||||
{
|
||||
softParticles = true;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
defineMap["softParticles"] = softParticles ? "1" : "0";
|
||||
|
||||
std::string shaderPrefix;
|
||||
if (!node.getUserValue("shaderPrefix", shaderPrefix))
|
||||
shaderPrefix = mDefaultShaderPrefix;
|
||||
|
@ -769,6 +793,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
|
||||
|
|
|
@ -260,7 +260,7 @@ aforementioned changes in visuals.
|
|||
This setting has no effect if :ref:`lighting method` is 'legacy'.
|
||||
|
||||
antialias alpha test
|
||||
---------------------------------------
|
||||
--------------------
|
||||
|
||||
:Type: boolean
|
||||
:Range: True/False
|
||||
|
@ -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 particle effects. 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,9 @@ 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
|
||||
|
||||
# Soften intersection of blended particle systems with opaque geometry
|
||||
soft particles = false
|
||||
|
||||
[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}")
|
||||
|
|
|
@ -80,6 +80,10 @@ varying vec3 passNormal;
|
|||
#include "parallax.glsl"
|
||||
#include "alpha.glsl"
|
||||
|
||||
#if @softParticles
|
||||
#include "softparticles.glsl"
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
#if @diffuseMap
|
||||
|
@ -220,6 +224,10 @@ void main()
|
|||
#endif
|
||||
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
|
||||
|
||||
#if @softParticles
|
||||
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;
|
||||
|
|
32
files/shaders/softparticles.glsl
Normal file
32
files/shaders/softparticles.glsl
Normal file
|
@ -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);
|
||||
}
|
|
@ -444,6 +444,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="softParticlesCheckBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Enables soft particles for particle effects. This technique softens the intersection between individual particles and other opaque geometry by blending between them.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Soft Particles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
Loading…
Reference in a new issue