Replace deprecated alpha test in shader visitor

pull/593/head
AnyOldName3 4 years ago
parent 70bd9d395d
commit ce2bcba5d4

@ -46,7 +46,7 @@ add_component_dir (resource
)
add_component_dir (shader
shadermanager shadervisitor
shadermanager shadervisitor removedalphafunc
)
add_component_dir (sceneutil

@ -278,7 +278,7 @@ void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
static osg::ref_ptr<osg::StateSet> ss;
if (!ss)
{
ShadowsBinAdder adder("ShadowsBin");
ShadowsBinAdder adder("ShadowsBin", _vdsm->getCastingPrograms());
ss = new osg::StateSet;
ss->setRenderBinDetails(osg::StateSet::OPAQUE_BIN, "ShadowsBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS);
}
@ -882,11 +882,15 @@ void SceneUtil::MWShadowTechnique::disableFrontFaceCulling()
void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & shaderManager)
{
// This can't be part of the constructor as OSG mandates that there be a trivial constructor available
_castingProgram = new osg::Program();
_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));
osg::ref_ptr<osg::Shader> castingVertexShader = shaderManager.getShader("shadowcasting_vertex.glsl", {}, osg::Shader::VERTEX);
for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc)
{
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*/)
@ -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;
_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
_shadowCastingStateSet->setTextureAttributeAndModes(0, _fallbackBaseTexture.get(), osg::StateAttribute::ON);
_shadowCastingStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));

@ -215,6 +215,8 @@ namespace SceneUtil {
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 osg::Polytope computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight);
@ -288,7 +290,7 @@ namespace SceneUtil {
};
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 <unordered_set>
#include <osg/StateSet>
#include <osg/AlphaFunc>
#include <osg/Material>
#include <osg/Program>
#include <osgUtil/StateGraph>
using namespace osgUtil;
@ -40,6 +42,10 @@ namespace
namespace SceneUtil
{
std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER> ShadowsBin::sCastingPrograms = {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
ShadowsBin::ShadowsBin()
{
mNoTestStateSet = new osg::StateSet;
@ -49,6 +55,12 @@ ShadowsBin::ShadowsBin()
mShaderAlphaTestStateSet = new osg::StateSet;
mShaderAlphaTestStateSet->addUniform(new osg::Uniform("alphaTestShadows", true));
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)
@ -71,7 +83,6 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un
continue;
accumulateModeState(ss, state.mAlphaBlend, state.mAlphaBlendOverride, GL_BLEND);
accumulateModeState(ss, state.mAlphaTest, state.mAlphaTestOverride, GL_ALPHA_TEST);
const osg::StateSet::AttributeList& attributes = ss->getAttributeList();
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;
}
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.
found = attributes.find(std::make_pair(osg::StateAttribute::FRONTFACE, 0));
if (found != attributes.end())
@ -113,16 +132,44 @@ StateGraph* ShadowsBin::cullStateGraph(StateGraph* sg, StateGraph* root, std::un
leaf->_parent = sg_new;
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;
}
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
{
if (!mMaterial)
return true;
return materialNeedShadows(mMaterial);
if (mAlphaFunc && mAlphaFunc->getFunction() == GL_NEVER)
return false;
// other alpha func + material combinations might be skippable
if (mAlphaBlend && mMaterial)
return materialNeedShadows(mMaterial);
return true;
}
void ShadowsBin::sortImplementation()

@ -1,11 +1,13 @@
#ifndef OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H
#define OPENMW_COMPONENTS_SCENEUTIL_SHADOWBIN_H
#include <array>
#include <unordered_set>
#include <osgUtil/RenderBin>
namespace osg
{
class Material;
class AlphaFunc;
}
namespace SceneUtil
@ -15,8 +17,12 @@ namespace SceneUtil
class ShadowsBin : public osgUtil::RenderBin
{
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> mShaderAlphaTestStateSet;
std::array<osg::ref_ptr<osg::StateSet>, GL_ALWAYS - GL_NEVER> mAlphaFuncShaders;
public:
META_Object(SceneUtil, ShadowsBin)
ShadowsBin();
@ -24,6 +30,7 @@ namespace SceneUtil
: osgUtil::RenderBin(rhs, copyop)
, mNoTestStateSet(rhs.mNoTestStateSet)
, mShaderAlphaTestStateSet(rhs.mShaderAlphaTestStateSet)
, mAlphaFuncShaders(rhs.mAlphaFuncShaders)
{}
void sortImplementation() override;
@ -33,8 +40,8 @@ namespace SceneUtil
State()
: mAlphaBlend(false)
, mAlphaBlendOverride(false)
, mAlphaTest(false)
, mAlphaTestOverride(false)
, mAlphaFunc(nullptr)
, mAlphaFuncOverride(false)
, mMaterial(nullptr)
, mMaterialOverride(false)
, mImportantState(false)
@ -42,33 +49,29 @@ namespace SceneUtil
bool mAlphaBlend;
bool mAlphaBlendOverride;
bool mAlphaTest;
bool mAlphaTestOverride;
osg::AlphaFunc* mAlphaFunc;
bool mAlphaFuncOverride;
osg::Material* mMaterial;
bool mMaterialOverride;
bool mImportantState;
bool needTexture() const { return mAlphaBlend || mAlphaTest; }
bool needTexture() const;
bool needShadows() const;
// A state is interesting if there's anything about it that might affect whether we can optimise child state
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);
static void addPrototype(const std::string& name)
{
osg::ref_ptr<osgUtil::RenderBin> bin (new ShadowsBin);
osgUtil::RenderBin::addRenderBinPrototype(name, bin);
}
static void addPrototype(const std::string& name, const std::array<osg::ref_ptr<osg::Program>, GL_ALWAYS - GL_NEVER>& castingPrograms);
};
class ShadowsBinAdder
{
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); }
};
}

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

@ -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 <osg/AlphaFunc>
#include <osg/Geometry>
#include <osg/Material>
#include <osg/Texture>
@ -13,6 +14,7 @@
#include <components/sceneutil/riggeometry.hpp>
#include <components/sceneutil/morphgeometry.hpp>
#include "removedalphafunc.hpp"
#include "shadermanager.hpp"
namespace Shader
@ -22,6 +24,9 @@ namespace Shader
: mShaderRequired(false)
, mColorMode(0)
, mMaterialOverridden(false)
, mAlphaTestOverridden(false)
, mAlphaFunc(GL_ALWAYS)
, mAlphaRef(1.0)
, mNormalHeight(false)
, mTexStageRequiringTangents(-1)
, mNode(nullptr)
@ -76,6 +81,34 @@ namespace Shader
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" };
bool isTextureNameRecognized(const std::string& name)
{
@ -234,49 +267,67 @@ namespace Shader
}
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 })
{
if (it->first.first == osg::StateAttribute::MATERIAL)
for (osg::StateSet::AttributeList::const_iterator it = attributeMap.begin(); it != attributeMap.end(); ++it)
{
// This should probably be moved out of ShaderRequirements and be applied directly now it's a uniform instead of a define
if (!mRequirements.back().mMaterialOverridden || it->second.second & osg::StateAttribute::PROTECTED)
if (it->first.first == osg::StateAttribute::MATERIAL)
{
if (it->second.second & osg::StateAttribute::OVERRIDE)
mRequirements.back().mMaterialOverridden = true;
// This should probably be moved out of ShaderRequirements and be applied directly now it's a uniform instead of a define
if (!mRequirements.back().mMaterialOverridden || it->second.second & osg::StateAttribute::PROTECTED)
{
if (it->second.second & osg::StateAttribute::OVERRIDE)
mRequirements.back().mMaterialOverridden = true;
const osg::Material* mat = static_cast<const osg::Material*>(it->second.first.get());
const osg::Material* mat = static_cast<const osg::Material*>(it->second.first.get());
if (!writableStateSet)
writableStateSet = getWritableStateSet(node);
if (!writableStateSet)
writableStateSet = getWritableStateSet(node);
int colorMode;
switch (mat->getColorMode())
{
case osg::Material::OFF:
colorMode = 0;
break;
case osg::Material::EMISSION:
colorMode = 1;
break;
default:
case osg::Material::AMBIENT_AND_DIFFUSE:
colorMode = 2;
break;
case osg::Material::AMBIENT:
colorMode = 3;
break;
case osg::Material::DIFFUSE:
colorMode = 4;
break;
case osg::Material::SPECULAR:
colorMode = 5;
break;
int colorMode;
switch (mat->getColorMode())
{
case osg::Material::OFF:
colorMode = 0;
break;
case osg::Material::EMISSION:
colorMode = 1;
break;
default:
case osg::Material::AMBIENT_AND_DIFFUSE:
colorMode = 2;
break;
case osg::Material::AMBIENT:
colorMode = 3;
break;
case osg::Material::DIFFUSE:
colorMode = 4;
break;
case osg::Material::SPECULAR:
colorMode = 5;
break;
}
mRequirements.back().mColorMode = colorMode;
}
}
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;
mRequirements.back().mColorMode = colorMode;
const osg::AlphaFunc* alpha = static_cast<const osg::AlphaFunc*>(it->second.first.get());
mRequirements.back().mAlphaFunc = alpha->getFunction();
mRequirements.back().mAlphaRef = alpha->getReferenceValue();
}
}
}
// Eventually, move alpha testing to discard in shader adn remove deprecated state here
}
}
@ -322,6 +373,42 @@ namespace Shader
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> fragmentShader (mShaderManager.getShader(mDefaultFsTemplate, defineMap, osg::Shader::FRAGMENT));
@ -347,6 +434,25 @@ namespace Shader
writableStateSet = getWritableStateSet(node);
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)

@ -79,6 +79,10 @@ namespace Shader
int mColorMode;
bool mMaterialOverridden;
bool mAlphaTestOverridden;
GLenum mAlphaFunc;
float mAlphaRef;
bool mNormalHeight; // true if normal map has height info in alpha channel

@ -10,6 +10,7 @@ set(SHADER_FILES
water_vertex.glsl
water_fragment.glsl
water_nm.png
alpha.glsl
objects_vertex.glsl
objects_fragment.glsl
terrain_vertex.glsl

@ -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 "lighting.glsl"
#include "parallax.glsl"
#include "alpha.glsl"
void main()
{
@ -108,6 +109,7 @@ void main()
#if @diffuseMap
gl_FragData[0] = texture2D(diffuseMap, adjustedDiffuseUV);
alphaTest();
#else
gl_FragData[0] = vec4(1.0);
#endif
@ -164,6 +166,8 @@ void main()
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing);
#endif
alphaTest();
#if @envMap && !@preLightEnv
gl_FragData[0].xyz += texture2D(envMap, envTexCoordGen).xyz * envMapColor.xyz * envLuma;
#endif

@ -8,6 +8,8 @@ varying float alphaPassthrough;
uniform bool useDiffuseMapForShadowAlpha;
uniform bool alphaTestShadows;
#include "alpha.glsl"
void main()
{
gl_FragData[0].rgb = vec3(1.0);
@ -16,7 +18,10 @@ void main()
else
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)
discard;
}

Loading…
Cancel
Save