mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 20:39:40 +00:00
Replace deprecated alpha test in shader visitor
This commit is contained in:
parent
70bd9d395d
commit
ce2bcba5d4
13 changed files with 345 additions and 63 deletions
|
@ -46,7 +46,7 @@ add_component_dir (resource
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (shader
|
add_component_dir (shader
|
||||||
shadermanager shadervisitor
|
shadermanager shadervisitor removedalphafunc
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (sceneutil
|
add_component_dir (sceneutil
|
||||||
|
|
|
@ -278,7 +278,7 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||||
static osg::ref_ptr<osg::StateSet> ss;
|
static osg::ref_ptr<osg::StateSet> ss;
|
||||||
if (!ss)
|
if (!ss)
|
||||||
{
|
{
|
||||||
ShadowsBinAdder adder("ShadowsBin");
|
ShadowsBinAdder adder("ShadowsBin", _vdsm->getCastingPrograms());
|
||||||
ss = new osg::StateSet;
|
ss = new osg::StateSet;
|
||||||
ss->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "ShadowsBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS);
|
ss->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "ShadowsBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS);
|
||||||
}
|
}
|
||||||
|
@ -883,10 +883,14 @@ void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & sh
|
||||||
{
|
{
|
||||||
// This can't be part of the constructor as OSG mandates that there be a trivial constructor available
|
// This can't be part of the constructor as OSG mandates that there be a trivial constructor available
|
||||||
|
|
||||||
_castingProgram = new osg::Program();
|
osg::ref_ptr<osg::Shader> castingVertexShader = shaderManager.getShader("shadowcasting_vertex.glsl", {}, osg::Shader::VERTEX);
|
||||||
|
for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc)
|
||||||
_castingProgram->addShader(shaderManager.getShader("shadowcasting_vertex.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::VERTEX));
|
{
|
||||||
_castingProgram->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", Shader::ShaderManager::DefineMap(), osg::Shader::FRAGMENT));
|
auto& program = _castingPrograms[alphaFunc - GL_NEVER];
|
||||||
|
program = new osg::Program();
|
||||||
|
program->addShader(castingVertexShader);
|
||||||
|
program->addShader(shaderManager.getShader("shadowcasting_fragment.glsl", { {"alphaFunc", std::to_string(alphaFunc)} }, osg::Shader::FRAGMENT));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData(osgUtil::CullVisitor* /*cv*/)
|
MWShadowTechnique::ViewDependentData* MWShadowTechnique::createViewDependentData(osgUtil::CullVisitor* /*cv*/)
|
||||||
|
@ -1604,10 +1608,11 @@ void MWShadowTechnique::createShaders()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_castingProgram)
|
if (!_castingPrograms[GL_ALWAYS - GL_NEVER])
|
||||||
OSG_NOTICE << "Shadow casting shader has not been set up. Remember to call setupCastingShader(Shader::ShaderManager &)" << std::endl;
|
OSG_NOTICE << "Shadow casting shader has not been set up. Remember to call setupCastingShader(Shader::ShaderManager &)" << std::endl;
|
||||||
|
|
||||||
_shadowCastingStateSet->setAttributeAndModes(_castingProgram, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
// Always use the GL_ALWAYS shader as the shadows bin will change it if necessary
|
||||||
|
_shadowCastingStateSet->setAttributeAndModes(_castingPrograms[GL_ALWAYS - GL_NEVER], osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||||
// The casting program uses a sampler, so to avoid undefined behaviour, we must bind a dummy texture in case no other is supplied
|
// The casting program uses a sampler, so to avoid undefined behaviour, we must bind a dummy texture in case no other is supplied
|
||||||
_shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);
|
_shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);
|
||||||
_shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));
|
_shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));
|
||||||
|
|
|
@ -215,6 +215,8 @@ namespace SceneUtil {
|
||||||
|
|
||||||
virtual void createShaders();
|
virtual void createShaders();
|
||||||
|
|
||||||
|
virtual std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER> getCastingPrograms() const { return _castingPrograms; }
|
||||||
|
|
||||||
virtual bool selectActiveLights(osgUtil::CullVisitor* cv, ViewDependentData* vdd) const;
|
virtual bool selectActiveLights(osgUtil::CullVisitor* cv, ViewDependentData* vdd) const;
|
||||||
|
|
||||||
virtual osg::Polytope computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight);
|
virtual osg::Polytope computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight);
|
||||||
|
@ -288,7 +290,7 @@ namespace SceneUtil {
|
||||||
};
|
};
|
||||||
|
|
||||||
osg::ref_ptr<DebugHUD> _debugHud;
|
osg::ref_ptr<DebugHUD> _debugHud;
|
||||||
osg::ref_ptr<osg::Program> _castingProgram;
|
std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER> _castingPrograms;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "shadowsbin.hpp"
|
#include "shadowsbin.hpp"
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <osg/StateSet>
|
#include <osg/StateSet>
|
||||||
|
#include <osg/AlphaFunc>
|
||||||
#include <osg/Material>
|
#include <osg/Material>
|
||||||
|
#include <osg/Program>
|
||||||
#include <osgUtil/StateGraph>
|
#include <osgUtil/StateGraph>
|
||||||
|
|
||||||
using namespace osgUtil;
|
using namespace osgUtil;
|
||||||
|
@ -40,6 +42,10 @@ namespace
|
||||||
namespace SceneUtil
|
namespace SceneUtil
|
||||||
{
|
{
|
||||||
|
|
||||||
|
std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER> ShadowsBin::sCastingPrograms = {
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
|
||||||
|
};
|
||||||
|
|
||||||
ShadowsBin::ShadowsBin()
|
ShadowsBin::ShadowsBin()
|
||||||
{
|
{
|
||||||
mNoTestStateSet = new osg::StateSet;
|
mNoTestStateSet = new osg::StateSet;
|
||||||
|
@ -49,6 +55,12 @@ ShadowsBin::ShadowsBin()
|
||||||
mShaderAlphaTestStateSet = new osg::StateSet;
|
mShaderAlphaTestStateSet = new osg::StateSet;
|
||||||
mShaderAlphaTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", true));
|
mShaderAlphaTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", true));
|
||||||
mShaderAlphaTestStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
|
mShaderAlphaTestStateSet->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sCastingPrograms.size(); ++i)
|
||||||
|
{
|
||||||
|
mAlphaFuncShaders[i] = new osg::StateSet;
|
||||||
|
mAlphaFuncShaders[i]->setAttribute(sCastingPrograms[i], osg::StateAttribute::ON | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set<StateGraph*>& uninterestingCache)
|
StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::unordered_set<StateGraph*>& uninterestingCache)
|
||||||
|
@ -71,7 +83,6 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
accumulateModeState(ss, state.mAlphaBlend, state.mAlphaBlendOverride, GL_BLEND);
|
accumulateModeState(ss, state.mAlphaBlend, state.mAlphaBlendOverride, GL_BLEND);
|
||||||
accumulateModeState(ss, state.mAlphaTest, state.mAlphaTestOverride, GL_ALPHA_TEST);
|
|
||||||
|
|
||||||
const osg::StateSet::AttributeList& attributes = ss->getAttributeList();
|
const osg::StateSet::AttributeList& attributes = ss->getAttributeList();
|
||||||
osg::StateSet::AttributeList::const_iterator found = attributes.find(std::make_pair(osg::StateAttribute::MATERIAL, 0));
|
osg::StateSet::AttributeList::const_iterator found = attributes.find(std::make_pair(osg::StateAttribute::MATERIAL, 0));
|
||||||
|
@ -83,6 +94,14 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un
|
||||||
state.mMaterial = nullptr;
|
state.mMaterial = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
found = attributes.find(std::make_pair(osg::StateAttribute::ALPHAFUNC, 0));
|
||||||
|
if (found != attributes.end())
|
||||||
|
{
|
||||||
|
// As force shaders is on, we know this is really a RemovedAlphaFunc
|
||||||
|
const osg::StateSet::RefAttributePair& rap = found->second;
|
||||||
|
accumulateState(state.mAlphaFunc, static_cast<osg::AlphaFunc*>(rap.first.get()), state.mAlphaFuncOverride, rap.second);
|
||||||
|
}
|
||||||
|
|
||||||
// osg::FrontFace specifies triangle winding, not front-face culling. We can't safely reparent anything under it.
|
// osg::FrontFace specifies triangle winding, not front-face culling. We can't safely reparent anything under it.
|
||||||
found = attributes.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0));
|
found = attributes.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0));
|
||||||
if (found != attributes.end())
|
if (found != attributes.end())
|
||||||
|
@ -113,16 +132,44 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un
|
||||||
leaf->_parent = sg_new;
|
leaf->_parent = sg_new;
|
||||||
sg_new->_leaves.push_back(leaf);
|
sg_new->_leaves.push_back(leaf);
|
||||||
}
|
}
|
||||||
return sg_new;
|
sg = sg_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GL_ALWAYS is set by default by mwshadowtechnique
|
||||||
|
if (state.mAlphaFunc && state.mAlphaFunc->getFunction() != GL_ALWAYS)
|
||||||
|
{
|
||||||
|
sg_new = sg->find_or_insert(mAlphaFuncShaders[state.mAlphaFunc->getFunction() - GL_NEVER]);
|
||||||
|
for (RenderLeaf* leaf : sg->_leaves)
|
||||||
|
{
|
||||||
|
leaf->_parent = sg_new;
|
||||||
|
sg_new->_leaves.push_back(leaf);
|
||||||
|
}
|
||||||
|
sg = sg_new;
|
||||||
|
}
|
||||||
|
|
||||||
return sg;
|
return sg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShadowsBin::addPrototype(const std::string & name, const std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER>& castingPrograms)
|
||||||
|
{
|
||||||
|
sCastingPrograms = castingPrograms;
|
||||||
|
osg::ref_ptr<osgUtil::RenderBin> bin(new ShadowsBin);
|
||||||
|
osgUtil::RenderBin::addRenderBinPrototype(name, bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool ShadowsBin::State::needTexture() const
|
||||||
|
{
|
||||||
|
return mAlphaBlend || (mAlphaFunc && mAlphaFunc->getFunction() != GL_ALWAYS);
|
||||||
|
}
|
||||||
|
|
||||||
bool ShadowsBin::State::needShadows() const
|
bool ShadowsBin::State::needShadows() const
|
||||||
{
|
{
|
||||||
if (!mMaterial)
|
if (mAlphaFunc && mAlphaFunc->getFunction() == GL_NEVER)
|
||||||
return true;
|
return false;
|
||||||
|
// other alpha func + material combinations might be skippable
|
||||||
|
if (mAlphaBlend && mMaterial)
|
||||||
return materialNeedShadows(mMaterial);
|
return materialNeedShadows(mMaterial);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShadowsBin::sortImplementation()
|
void ShadowsBin::sortImplementation()
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#ifndef OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H
|
#ifndef OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H
|
||||||
#define OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H
|
#define OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H
|
||||||
|
#include <array>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <osgUtil/RenderBin>
|
#include <osgUtil/RenderBin>
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
class Material;
|
class Material;
|
||||||
|
class AlphaFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace SceneUtil
|
namespace SceneUtil
|
||||||
|
@ -15,8 +17,12 @@ namespace SceneUtil
|
||||||
class ShadowsBin : public osgUtil::RenderBin
|
class ShadowsBin : public osgUtil::RenderBin
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
static std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER> sCastingPrograms;
|
||||||
|
|
||||||
osg::ref_ptr<osg::StateSet> mNoTestStateSet;
|
osg::ref_ptr<osg::StateSet> mNoTestStateSet;
|
||||||
osg::ref_ptr<osg::StateSet> mShaderAlphaTestStateSet;
|
osg::ref_ptr<osg::StateSet> mShaderAlphaTestStateSet;
|
||||||
|
|
||||||
|
std::array<osg::ref_ptr<osg::StateSet>, GL_ALWAYS - GL_NEVER> mAlphaFuncShaders;
|
||||||
public:
|
public:
|
||||||
META_Object(SceneUtil, ShadowsBin)
|
META_Object(SceneUtil, ShadowsBin)
|
||||||
ShadowsBin();
|
ShadowsBin();
|
||||||
|
@ -24,6 +30,7 @@ namespace SceneUtil
|
||||||
: osgUtil::RenderBin(rhs, copyop)
|
: osgUtil::RenderBin(rhs, copyop)
|
||||||
, mNoTestStateSet(rhs.mNoTestStateSet)
|
, mNoTestStateSet(rhs.mNoTestStateSet)
|
||||||
, mShaderAlphaTestStateSet(rhs.mShaderAlphaTestStateSet)
|
, mShaderAlphaTestStateSet(rhs.mShaderAlphaTestStateSet)
|
||||||
|
, mAlphaFuncShaders(rhs.mAlphaFuncShaders)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void sortImplementation() override;
|
void sortImplementation() override;
|
||||||
|
@ -33,8 +40,8 @@ namespace SceneUtil
|
||||||
State()
|
State()
|
||||||
: mAlphaBlend(false)
|
: mAlphaBlend(false)
|
||||||
, mAlphaBlendOverride(false)
|
, mAlphaBlendOverride(false)
|
||||||
, mAlphaTest(false)
|
, mAlphaFunc(nullptr)
|
||||||
, mAlphaTestOverride(false)
|
, mAlphaFuncOverride(false)
|
||||||
, mMaterial(nullptr)
|
, mMaterial(nullptr)
|
||||||
, mMaterialOverride(false)
|
, mMaterialOverride(false)
|
||||||
, mImportantState(false)
|
, mImportantState(false)
|
||||||
|
@ -42,33 +49,29 @@ namespace SceneUtil
|
||||||
|
|
||||||
bool mAlphaBlend;
|
bool mAlphaBlend;
|
||||||
bool mAlphaBlendOverride;
|
bool mAlphaBlendOverride;
|
||||||
bool mAlphaTest;
|
osg::AlphaFunc* mAlphaFunc;
|
||||||
bool mAlphaTestOverride;
|
bool mAlphaFuncOverride;
|
||||||
osg::Material* mMaterial;
|
osg::Material* mMaterial;
|
||||||
bool mMaterialOverride;
|
bool mMaterialOverride;
|
||||||
bool mImportantState;
|
bool mImportantState;
|
||||||
bool needTexture() const { return mAlphaBlend || mAlphaTest; }
|
bool needTexture() const;
|
||||||
bool needShadows() const;
|
bool needShadows() const;
|
||||||
// A state is interesting if there's anything about it that might affect whether we can optimise child state
|
// A state is interesting if there's anything about it that might affect whether we can optimise child state
|
||||||
bool interesting() const
|
bool interesting() const
|
||||||
{
|
{
|
||||||
return !needShadows() || needTexture() || mAlphaBlendOverride || mAlphaTestOverride || mMaterialOverride || mImportantState;
|
return !needShadows() || needTexture() || mAlphaBlendOverride || mAlphaFuncOverride || mMaterialOverride || mImportantState;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set<osgUtil::StateGraph*>& uninteresting);
|
osgUtil::StateGraph* cullStateGraph(osgUtil::StateGraph* sg, osgUtil::StateGraph* root, std::unordered_set<osgUtil::StateGraph*>& uninteresting);
|
||||||
|
|
||||||
static void addPrototype(const std::string& name)
|
static void addPrototype(const std::string& name, const std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER>& castingPrograms);
|
||||||
{
|
|
||||||
osg::ref_ptr<osgUtil::RenderBin> bin (new ShadowsBin);
|
|
||||||
osgUtil::RenderBin::addRenderBinPrototype(name, bin);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShadowsBinAdder
|
class ShadowsBinAdder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ShadowsBinAdder(const std::string& name){ ShadowsBin::addPrototype(name); }
|
ShadowsBinAdder(const std::string& name, const std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER>& castingPrograms){ ShadowsBin::addPrototype(name, castingPrograms); }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
27
components/shader/removedalphafunc.cpp
Normal file
27
components/shader/removedalphafunc.cpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#include "removedalphafunc.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <osg/State>
|
||||||
|
|
||||||
|
namespace Shader
|
||||||
|
{
|
||||||
|
std::array<osg::ref_ptr<RemovedAlphaFunc>, GL_ALWAYS - GL_NEVER> RemovedAlphaFunc::sInstances{
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
osg::ref_ptr<RemovedAlphaFunc> RemovedAlphaFunc::getInstance(GLenum func)
|
||||||
|
{
|
||||||
|
assert(func >= GL_NEVER && func <= GL_ALWAYS);
|
||||||
|
if (!sInstances[func - GL_NEVER])
|
||||||
|
sInstances[func - GL_NEVER] = new RemovedAlphaFunc(static_cast<osg::AlphaFunc::ComparisonFunction>(func), 1.0);
|
||||||
|
return sInstances[func - GL_NEVER];
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemovedAlphaFunc::apply(osg::State & state) const
|
||||||
|
{
|
||||||
|
// Hopefully, anything genuinely requiring the default alpha func of GL_ALWAYS explicitly sets it
|
||||||
|
if (!state.getGlobalDefaultAttribute(ALPHAFUNC)->getType() != getType())
|
||||||
|
state.setGlobalDefaultAttribute(static_cast<const osg::StateAttribute*>(cloneType()));
|
||||||
|
}
|
||||||
|
}
|
40
components/shader/removedalphafunc.hpp
Normal file
40
components/shader/removedalphafunc.hpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H
|
||||||
|
#define OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <osg/AlphaFunc>
|
||||||
|
|
||||||
|
namespace Shader
|
||||||
|
{
|
||||||
|
// State attribute used when shader visitor replaces the deprecated alpha function with a shader
|
||||||
|
// Prevents redundant glAlphaFunc calls and lets the shadowsbin know the stateset had alpha testing
|
||||||
|
class RemovedAlphaFunc : public osg::AlphaFunc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Get a singleton-like instance with the right func (but a default threshold)
|
||||||
|
static osg::ref_ptr<RemovedAlphaFunc> getInstance(GLenum func);
|
||||||
|
|
||||||
|
RemovedAlphaFunc()
|
||||||
|
: osg::AlphaFunc()
|
||||||
|
{}
|
||||||
|
|
||||||
|
RemovedAlphaFunc(ComparisonFunction func, float ref)
|
||||||
|
: osg::AlphaFunc(func, ref)
|
||||||
|
{}
|
||||||
|
|
||||||
|
RemovedAlphaFunc(const RemovedAlphaFunc& raf, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY)
|
||||||
|
: osg::AlphaFunc(raf, copyop)
|
||||||
|
{}
|
||||||
|
|
||||||
|
META_StateAttribute(Shader, RemovedAlphaFunc, ALPHAFUNC);
|
||||||
|
|
||||||
|
void apply(osg::State& state) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~RemovedAlphaFunc() = default;
|
||||||
|
|
||||||
|
static std::array<osg::ref_ptr<RemovedAlphaFunc>, GL_ALWAYS - GL_NEVER> sInstances;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //OPENMW_COMPONENTS_REMOVEDALPHAFUNC_H
|
|
@ -1,5 +1,6 @@
|
||||||
#include "shadervisitor.hpp"
|
#include "shadervisitor.hpp"
|
||||||
|
|
||||||
|
#include <osg/AlphaFunc>
|
||||||
#include <osg/Geometry>
|
#include <osg/Geometry>
|
||||||
#include <osg/Material>
|
#include <osg/Material>
|
||||||
#include <osg/Texture>
|
#include <osg/Texture>
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
#include <components/sceneutil/riggeometry.hpp>
|
#include <components/sceneutil/riggeometry.hpp>
|
||||||
#include <components/sceneutil/morphgeometry.hpp>
|
#include <components/sceneutil/morphgeometry.hpp>
|
||||||
|
|
||||||
|
#include "removedalphafunc.hpp"
|
||||||
#include "shadermanager.hpp"
|
#include "shadermanager.hpp"
|
||||||
|
|
||||||
namespace Shader
|
namespace Shader
|
||||||
|
@ -22,6 +24,9 @@ namespace Shader
|
||||||
: mShaderRequired(false)
|
: mShaderRequired(false)
|
||||||
, mColorMode(0)
|
, mColorMode(0)
|
||||||
, mMaterialOverridden(false)
|
, mMaterialOverridden(false)
|
||||||
|
, mAlphaTestOverridden(false)
|
||||||
|
, mAlphaFunc(GL_ALWAYS)
|
||||||
|
, mAlphaRef(1.0)
|
||||||
, mNormalHeight(false)
|
, mNormalHeight(false)
|
||||||
, mTexStageRequiringTangents(-1)
|
, mTexStageRequiringTangents(-1)
|
||||||
, mNode(nullptr)
|
, mNode(nullptr)
|
||||||
|
@ -76,6 +81,34 @@ namespace Shader
|
||||||
return newStateSet.get();
|
return newStateSet.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::UserDataContainer* getWritableUserDataContainer(osg::Object& object)
|
||||||
|
{
|
||||||
|
if (!object.getUserDataContainer())
|
||||||
|
return object.getOrCreateUserDataContainer();
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::UserDataContainer> newUserData = static_cast<osg::UserDataContainer *>(object.getUserDataContainer()->clone(osg::CopyOp::SHALLOW_COPY));
|
||||||
|
object.setUserDataContainer(newUserData);
|
||||||
|
return newUserData.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::StateSet* getRemovedState(osg::StateSet& stateSet)
|
||||||
|
{
|
||||||
|
if (!stateSet.getUserDataContainer())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return static_cast<osg::StateSet *>(stateSet.getUserDataContainer()->getUserObject("removedState"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateRemovedState(osg::UserDataContainer& userData, osg::StateSet* stateSet)
|
||||||
|
{
|
||||||
|
unsigned int index = userData.getUserObjectIndex("removedState");
|
||||||
|
if (index < userData.getNumUserObjects())
|
||||||
|
userData.setUserObject(index, stateSet);
|
||||||
|
else
|
||||||
|
userData.addUserObject(stateSet);
|
||||||
|
stateSet->setName("removedState");
|
||||||
|
}
|
||||||
|
|
||||||
const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap", "specularMap", "decalMap", "bumpMap" };
|
const char* defaultTextures[] = { "diffuseMap", "normalMap", "emissiveMap", "darkMap", "detailMap", "envMap", "specularMap", "decalMap", "bumpMap" };
|
||||||
bool isTextureNameRecognized(const std::string& name)
|
bool isTextureNameRecognized(const std::string& name)
|
||||||
{
|
{
|
||||||
|
@ -234,7 +267,13 @@ namespace Shader
|
||||||
}
|
}
|
||||||
|
|
||||||
const osg::StateSet::AttributeList& attributes = stateset->getAttributeList();
|
const osg::StateSet::AttributeList& attributes = stateset->getAttributeList();
|
||||||
for (osg::StateSet::AttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it)
|
osg::StateSet::AttributeList removedAttributes;
|
||||||
|
osg::ref_ptr<osg::StateSet> removedState;
|
||||||
|
if (removedState = getRemovedState(*stateset))
|
||||||
|
removedAttributes = removedState->getAttributeList();
|
||||||
|
for (const auto& attributeMap : { attributes, removedAttributes })
|
||||||
|
{
|
||||||
|
for (osg::StateSet::AttributeList::const_iterator it = attributeMap.begin(); it != attributeMap.end(); ++it)
|
||||||
{
|
{
|
||||||
if (it->first.first == osg::StateAttribute::MATERIAL)
|
if (it->first.first == osg::StateAttribute::MATERIAL)
|
||||||
{
|
{
|
||||||
|
@ -276,7 +315,19 @@ namespace Shader
|
||||||
mRequirements.back().mColorMode = colorMode;
|
mRequirements.back().mColorMode = colorMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Eventually, move alpha testing to discard in shader adn remove deprecated state here
|
else if (it->first.first == osg::StateAttribute::ALPHAFUNC)
|
||||||
|
{
|
||||||
|
if (!mRequirements.back().mAlphaTestOverridden || it->second.second & osg::StateAttribute::PROTECTED)
|
||||||
|
{
|
||||||
|
if (it->second.second & osg::StateAttribute::OVERRIDE)
|
||||||
|
mRequirements.back().mAlphaTestOverridden = true;
|
||||||
|
|
||||||
|
const osg::AlphaFunc* alpha = static_cast<const osg::AlphaFunc*>(it->second.first.get());
|
||||||
|
mRequirements.back().mAlphaFunc = alpha->getFunction();
|
||||||
|
mRequirements.back().mAlphaRef = alpha->getReferenceValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,6 +373,42 @@ namespace Shader
|
||||||
|
|
||||||
writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode));
|
writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode));
|
||||||
|
|
||||||
|
defineMap["alphaFunc"] = std::to_string(reqs.mAlphaFunc);
|
||||||
|
if (reqs.mAlphaFunc != osg::AlphaFunc::ALWAYS)
|
||||||
|
{
|
||||||
|
writableStateSet->addUniform(new osg::Uniform("alphaRef", reqs.mAlphaRef));
|
||||||
|
|
||||||
|
// back up removed state in case recreateShaders gets rid of the shader later
|
||||||
|
osg::ref_ptr<osg::StateSet> removedState;
|
||||||
|
if ((removedState = getRemovedState(*writableStateSet)) && !mAllowedToModifyStateSets)
|
||||||
|
removedState = new osg::StateSet(*removedState, osg::CopyOp::SHALLOW_COPY);
|
||||||
|
if (!removedState)
|
||||||
|
removedState = new osg::StateSet();
|
||||||
|
|
||||||
|
if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT)
|
||||||
|
removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST));
|
||||||
|
// This disables the deprecated fixed-function alpha test
|
||||||
|
writableStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED);
|
||||||
|
|
||||||
|
const auto* alphaFunc = writableStateSet->getAttributePair(osg::StateAttribute::ALPHAFUNC);
|
||||||
|
if (alphaFunc)
|
||||||
|
removedState->setAttribute(alphaFunc->first, alphaFunc->second);
|
||||||
|
// This prevents redundant glAlphaFunc calls while letting the shadows bin still see the test
|
||||||
|
writableStateSet->setAttribute(RemovedAlphaFunc::getInstance(reqs.mAlphaFunc), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
|
if (!removedState->getModeList().empty() || !removedState->getAttributeList().empty())
|
||||||
|
{
|
||||||
|
// user data is normally shallow copied so shared with the original stateset
|
||||||
|
osg::ref_ptr<osg::UserDataContainer> writableUserData;
|
||||||
|
if (mAllowedToModifyStateSets)
|
||||||
|
writableUserData = writableStateSet->getOrCreateUserDataContainer();
|
||||||
|
else
|
||||||
|
writableUserData = getWritableUserDataContainer(*writableStateSet);
|
||||||
|
|
||||||
|
updateRemovedState(*writableUserData, removedState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Shader> vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX));
|
osg::ref_ptr<osg::Shader> vertexShader (mShaderManager.getShader(mDefaultVsTemplate, defineMap, osg::Shader::VERTEX));
|
||||||
osg::ref_ptr<osg::Shader> fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT));
|
osg::ref_ptr<osg::Shader> fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT));
|
||||||
|
|
||||||
|
@ -347,6 +434,25 @@ namespace Shader
|
||||||
writableStateSet = getWritableStateSet(node);
|
writableStateSet = getWritableStateSet(node);
|
||||||
|
|
||||||
writableStateSet->removeAttribute(osg::StateAttribute::PROGRAM);
|
writableStateSet->removeAttribute(osg::StateAttribute::PROGRAM);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::StateSet> removedState;
|
||||||
|
if (removedState = getRemovedState(*writableStateSet))
|
||||||
|
{
|
||||||
|
// user data is normally shallow copied so shared with the original stateset
|
||||||
|
osg::ref_ptr<osg::UserDataContainer> writableUserData;
|
||||||
|
if (mAllowedToModifyStateSets)
|
||||||
|
writableUserData = writableStateSet->getUserDataContainer();
|
||||||
|
else
|
||||||
|
writableUserData = getWritableUserDataContainer(*writableStateSet);
|
||||||
|
unsigned int index = writableUserData->getUserObjectIndex("removedState");
|
||||||
|
writableUserData->removeUserObject(index);
|
||||||
|
|
||||||
|
for (const auto& [mode, value] : removedState->getModeList())
|
||||||
|
writableStateSet->setMode(mode, value);
|
||||||
|
|
||||||
|
for (const auto& attribute : removedState->getAttributeList())
|
||||||
|
writableStateSet->setAttribute(attribute.second.first, attribute.second.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderVisitor::adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs)
|
bool ShaderVisitor::adjustGeometry(osg::Geometry& sourceGeometry, const ShaderRequirements& reqs)
|
||||||
|
|
|
@ -79,6 +79,10 @@ namespace Shader
|
||||||
int mColorMode;
|
int mColorMode;
|
||||||
|
|
||||||
bool mMaterialOverridden;
|
bool mMaterialOverridden;
|
||||||
|
bool mAlphaTestOverridden;
|
||||||
|
|
||||||
|
GLenum mAlphaFunc;
|
||||||
|
float mAlphaRef;
|
||||||
|
|
||||||
bool mNormalHeight; // true if normal map has height info in alpha channel
|
bool mNormalHeight; // true if normal map has height info in alpha channel
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ set(SHADER_FILES
|
||||||
water_vertex.glsl
|
water_vertex.glsl
|
||||||
water_fragment.glsl
|
water_fragment.glsl
|
||||||
water_nm.png
|
water_nm.png
|
||||||
|
alpha.glsl
|
||||||
objects_vertex.glsl
|
objects_vertex.glsl
|
||||||
objects_fragment.glsl
|
objects_fragment.glsl
|
||||||
terrain_vertex.glsl
|
terrain_vertex.glsl
|
||||||
|
|
38
files/shaders/alpha.glsl
Normal file
38
files/shaders/alpha.glsl
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
#define FUNC_NEVER 0x0200
|
||||||
|
#define FUNC_LESS 0x0201
|
||||||
|
#define FUNC_EQUAL 0x0202
|
||||||
|
#define FUNC_LEQUAL 0x0203
|
||||||
|
#define FUNC_GREATER 0x0204
|
||||||
|
#define FUNC_NOTEQUAL 0x0205
|
||||||
|
#define FUNC_GEQUAL 0x0206
|
||||||
|
#define FUNC_ALWAYS 0x0207
|
||||||
|
|
||||||
|
#if @alphaFunc != FUNC_ALWAYS && @alphaFunc != FUNC_NEVER
|
||||||
|
uniform float alphaRef;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void alphaTest()
|
||||||
|
{
|
||||||
|
#if @alphaFunc == FUNC_NEVER
|
||||||
|
discard;
|
||||||
|
#elif @alphaFunc == FUNC_LESS
|
||||||
|
if (gl_FragData[0].a > alphaRef)
|
||||||
|
discard;
|
||||||
|
#elif @alphaFunc == FUNC_EQUAL
|
||||||
|
if (gl_FragData[0].a != alphaRef)
|
||||||
|
discard;
|
||||||
|
#elif @alphaFunc == FUNC_LEQUAL
|
||||||
|
if (gl_FragData[0].a >= alphaRef)
|
||||||
|
discard;
|
||||||
|
#elif @alphaFunc == FUNC_GREATER
|
||||||
|
if (gl_FragData[0].a < alphaRef)
|
||||||
|
discard;
|
||||||
|
#elif @alphaFunc == FUNC_NOTEQUAL
|
||||||
|
if (gl_FragData[0].a == alphaRef)
|
||||||
|
discard;
|
||||||
|
#elif @alphaFunc == FUNC_GEQUAL
|
||||||
|
if (gl_FragData[0].a <= alphaRef)
|
||||||
|
discard;
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -67,6 +67,7 @@ varying vec3 passNormal;
|
||||||
#include "shadows_fragment.glsl"
|
#include "shadows_fragment.glsl"
|
||||||
#include "lighting.glsl"
|
#include "lighting.glsl"
|
||||||
#include "parallax.glsl"
|
#include "parallax.glsl"
|
||||||
|
#include "alpha.glsl"
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
|
@ -108,6 +109,7 @@ void main()
|
||||||
|
|
||||||
#if @diffuseMap
|
#if @diffuseMap
|
||||||
gl_FragData[0] = texture2D(diffuseMap, adjustedDiffuseUV);
|
gl_FragData[0] = texture2D(diffuseMap, adjustedDiffuseUV);
|
||||||
|
alphaTest();
|
||||||
#else
|
#else
|
||||||
gl_FragData[0] = vec4(1.0);
|
gl_FragData[0] = vec4(1.0);
|
||||||
#endif
|
#endif
|
||||||
|
@ -164,6 +166,8 @@ void main()
|
||||||
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing);
|
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
alphaTest();
|
||||||
|
|
||||||
#if @envMap && !@preLightEnv
|
#if @envMap && !@preLightEnv
|
||||||
gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma;
|
gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,6 +8,8 @@ varying float alphaPassthrough;
|
||||||
uniform bool useDiffuseMapForShadowAlpha;
|
uniform bool useDiffuseMapForShadowAlpha;
|
||||||
uniform bool alphaTestShadows;
|
uniform bool alphaTestShadows;
|
||||||
|
|
||||||
|
#include "alpha.glsl"
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
gl_FragData[0].rgb = vec3(1.0);
|
gl_FragData[0].rgb = vec3(1.0);
|
||||||
|
@ -16,7 +18,10 @@ void main()
|
||||||
else
|
else
|
||||||
gl_FragData[0].a = alphaPassthrough;
|
gl_FragData[0].a = alphaPassthrough;
|
||||||
|
|
||||||
// Prevent translucent things casting shadow (including the player using an invisibility effect). For now, rely on the deprecated FF test for non-blended stuff.
|
alphaTest();
|
||||||
|
|
||||||
|
// Prevent translucent things casting shadow (including the player using an invisibility effect).
|
||||||
|
// This replaces alpha blending, which obviously doesn't work with depth buffers
|
||||||
if (alphaTestShadows && gl_FragData[0].a <= 0.5)
|
if (alphaTestShadows && gl_FragData[0].a <= 0.5)
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue