Merge pull request #1547 from AnyOldName3/osgshadow-test-vdsm

Shadows
pull/2176/head
Chris Djali 6 years ago committed by GitHub
commit cb5a57e41b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -608,9 +608,9 @@ printf "OSG 3.4.1-scrawl... "
SUFFIX=""
fi
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer}${SUFFIX}.dll
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll
echo Done.
}
cd $DEPS

@ -256,7 +256,7 @@ if(NOT HAVE_STDINT_H)
endif()
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX osgShadow)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
set(USED_OSG_PLUGINS

@ -126,6 +126,7 @@ target_link_libraries(openmw
${OSGDB_LIBRARIES}
${OSGVIEWER_LIBRARIES}
${OSGGA_LIBRARIES}
${OSGSHADOW_LIBRARIES}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}

@ -16,6 +16,7 @@
#include <components/debug/debuglog.hpp>
#include <components/fallback/fallback.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/shadow.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -136,6 +137,7 @@ namespace MWRender
mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture);
mCamera->setName("CharacterPreview");
mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
mCamera->setCullMask(~(Mask_UpdateVisitor));
mCamera->setNodeMask(Mask_RenderToTexture);
@ -152,6 +154,8 @@ namespace MWRender
defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
stateset->setAttribute(defaultMat);
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
// assign large value to effectively turn off fog
// shaders don't respect glDisable(GL_FOG)
osg::ref_ptr<osg::Fog> fog (new osg::Fog);

@ -17,6 +17,7 @@
#include <components/misc/constants.hpp>
#include <components/settings/settings.hpp>
#include <components/sceneutil/visitor.hpp>
#include <components/sceneutil/shadow.hpp>
#include <components/files/memorystream.hpp>
#include "../mwbase/environment.hpp"
@ -177,7 +178,7 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
camera->setRenderOrder(osg::Camera::PRE_RENDER);
camera->setCullMask(Mask_Scene|Mask_SimpleWater|Mask_Terrain);
camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object);
camera->setNodeMask(Mask_RenderToTexture);
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
@ -209,6 +210,8 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
camera->addChild(lightSource);
camera->setStateSet(stateset);
camera->setViewport(0, 0, mMapResolution, mMapResolution);
@ -377,7 +380,7 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell)
void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell)
{
osg::ComputeBoundsVisitor computeBoundsVisitor;
computeBoundsVisitor.setTraversalMask(Mask_Scene|Mask_Terrain);
computeBoundsVisitor.setTraversalMask(Mask_Scene | Mask_Terrain | Mask_Object);
mSceneRoot->accept(computeBoundsVisitor);
osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox();

@ -362,11 +362,16 @@ public:
if (cv->getProjectionMatrix()->getPerspective(fov, aspect, zNear, zFar))
{
fov = mFov;
osg::RefMatrix* newProjectionMatrix = new osg::RefMatrix(*cv->getProjectionMatrix());
osg::ref_ptr<osg::RefMatrix> newProjectionMatrix = new osg::RefMatrix();
newProjectionMatrix->makePerspective(fov, aspect, zNear, zFar);
cv->pushProjectionMatrix(newProjectionMatrix);
osg::ref_ptr<osg::RefMatrix> invertedOldMatrix = cv->getProjectionMatrix();
invertedOldMatrix = new osg::RefMatrix(osg::RefMatrix::inverse(*invertedOldMatrix));
osg::ref_ptr<osg::RefMatrix> viewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix());
viewMatrix->postMult(*newProjectionMatrix);
viewMatrix->postMult(*invertedOldMatrix);
cv->pushModelViewMatrix(viewMatrix, osg::Transform::ReferenceFrame::ABSOLUTE_RF);
traverse(node, nv);
cv->popProjectionMatrix();
cv->popModelViewMatrix();
}
else
traverse(node, nv);

@ -71,6 +71,7 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr)
void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool animated, bool allowLight)
{
insertBegin(ptr);
ptr.getRefData().getBaseNode()->setNodeMask(Mask_Object);
osg::ref_ptr<ObjectAnimation> anim (new ObjectAnimation(ptr, mesh, mResourceSystem, animated, allowLight));

@ -39,6 +39,7 @@
#include <components/sceneutil/workqueue.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/sceneutil/writescene.hpp>
#include <components/sceneutil/shadow.hpp>
#include <components/terrain/terraingrid.hpp>
#include <components/terrain/quadtreeworld.hpp>
@ -219,7 +220,7 @@ namespace MWRender
{
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
resourceSystem->getSceneManager()->setForceShaders(Settings::Manager::getBool("force shaders", "Shaders"));
resourceSystem->getSceneManager()->setForceShaders(Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows")); // Shadows have problems with fixed-function mode
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
resourceSystem->getSceneManager()->setForcePerPixelLighting(Settings::Manager::getBool("force per pixel lighting", "Shaders"));
resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "Shaders"));
@ -233,7 +234,28 @@ namespace MWRender
mSceneRoot = sceneRoot;
sceneRoot->setStartLight(1);
mRootNode->addChild(mSceneRoot);
int shadowCastingTraversalMask = Mask_Scene;
if (Settings::Manager::getBool("actor shadows", "Shadows"))
shadowCastingTraversalMask |= Mask_Actor;
if (Settings::Manager::getBool("player shadows", "Shadows"))
shadowCastingTraversalMask |= Mask_Player;
if (Settings::Manager::getBool("terrain shadows", "Shadows"))
shadowCastingTraversalMask |= Mask_Terrain;
int indoorShadowCastingTraversalMask = shadowCastingTraversalMask;
if (Settings::Manager::getBool("object shadows", "Shadows"))
shadowCastingTraversalMask |= Mask_Object;
mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager()));
Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines();
Shader::ShaderManager::DefineMap globalDefines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
for (auto itr = shadowDefines.begin(); itr != shadowDefines.end(); itr++)
globalDefines[itr->first] = itr->second;
// It is unnecessary to stop/start the viewer as no frames are being rendered yet.
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines);
mNavMesh.reset(new NavMesh(mRootNode, Settings::Manager::getBool("enable nav mesh render", "Navigator")));
mActorsPaths.reset(new ActorsPaths(mRootNode, Settings::Manager::getBool("enable agents paths render", "Navigator")));
@ -443,7 +465,7 @@ namespace MWRender
osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell->mAmbi.mSunlight);
mSunLight->setDiffuse(diffuse);
mSunLight->setSpecular(diffuse);
mSunLight->setDirection(osg::Vec3f(1.f,-1.f,-1.f));
mSunLight->setPosition(osg::Vec4f(-0.15f, 0.15f, 1.f, 0.f));
}
void RenderingManager::setSunColour(const osg::Vec4f& diffuse, const osg::Vec4f& specular)
@ -491,6 +513,10 @@ namespace MWRender
void RenderingManager::setSkyEnabled(bool enabled)
{
mSky->setEnabled(enabled);
if (enabled)
mShadowManager->enableOutdoorMode();
else
mShadowManager->enableIndoorMode();
}
bool RenderingManager::toggleBorders()

@ -53,6 +53,7 @@ namespace Fallback
namespace SceneUtil
{
class ShadowManager;
class WorkQueue;
class UnrefQueue;
}
@ -267,6 +268,7 @@ namespace MWRender
TerrainStorage* mTerrainStorage;
std::unique_ptr<SkyManager> mSky;
std::unique_ptr<EffectManager> mEffectManager;
std::unique_ptr<SceneUtil::ShadowManager> mShadowManager;
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> mPlayerNode;
std::unique_ptr<Camera> mCamera;

@ -42,6 +42,7 @@
#include <components/sceneutil/statesetupdater.hpp>
#include <components/sceneutil/controller.hpp>
#include <components/sceneutil/visitor.hpp>
#include <components/sceneutil/shadow.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -1127,7 +1128,8 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
skyroot->setName("Sky Root");
// Assign empty program to specify we don't want shaders
// The shaders generated by the SceneManager can't handle everything we need
skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(), osg::StateAttribute::OVERRIDE);
skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(), osg::StateAttribute::OVERRIDE|osg::StateAttribute::PROTECTED|osg::StateAttribute::ON);
SceneUtil::ShadowManager::disableShadowsForStateSet(skyroot->getOrCreateStateSet());
skyroot->setNodeMask(Mask_Sky);
parentNode->addChild(skyroot);

@ -33,25 +33,26 @@ namespace MWRender
Mask_SimpleWater = (1<<7),
Mask_Terrain = (1<<8),
Mask_FirstPerson = (1<<9),
Mask_Object = (1<<10),
// child of Sky
Mask_Sun = (1<<10),
Mask_WeatherParticles = (1<<11),
Mask_Sun = (1<<11),
Mask_WeatherParticles = (1<<12),
// top level masks
Mask_Scene = (1<<12),
Mask_GUI = (1<<13),
Mask_Scene = (1<<13),
Mask_GUI = (1<<14),
// Set on a ParticleSystem Drawable
Mask_ParticleSystem = (1<<14),
Mask_ParticleSystem = (1<<15),
// Set on cameras within the main scene graph
Mask_RenderToTexture = (1<<15),
Mask_RenderToTexture = (1<<16),
Mask_PreCompile = (1<<16),
Mask_PreCompile = (1<<17),
// Set on a camera's cull mask to enable the LightManager
Mask_Lighting = (1<<17)
Mask_Lighting = (1<<18)
};
}

@ -25,6 +25,7 @@
#include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/shadow.hpp>
#include <components/sceneutil/waterutil.hpp>
#include <components/misc/constants.hpp>
@ -224,7 +225,7 @@ public:
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
setName("RefractionCamera");
setCullMask(Mask_Effect|Mask_Scene|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting);
setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting);
setNodeMask(Mask_RenderToTexture);
setViewport(0, 0, rttSize, rttSize);
@ -263,6 +264,8 @@ public:
mRefractionDepthTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture);
SceneUtil::ShadowManager::disableShadowsForStateSet(getOrCreateStateSet());
}
void setScene(osg::Node* scene)
@ -315,7 +318,7 @@ public:
bool reflectActors = Settings::Manager::getBool("reflect actors", "Water");
setCullMask(Mask_Effect|Mask_Scene|Mask_Terrain|Mask_ParticleSystem|Mask_Sky|Mask_Player|Mask_Lighting|(reflectActors ? Mask_Actor : 0));
setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Terrain|Mask_ParticleSystem|Mask_Sky|Mask_Player|Mask_Lighting|(reflectActors ? Mask_Actor : 0));
setNodeMask(Mask_RenderToTexture);
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
@ -341,6 +344,8 @@ public:
mClipCullNode = new ClipCullNode;
addChild(mClipCullNode);
SceneUtil::ShadowManager::disableShadowsForStateSet(getOrCreateStateSet());
}
void setWaterLevel(float waterLevel)

@ -51,7 +51,7 @@ add_component_dir (shader
add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
actorutil detourdebugdraw navmesh agentpath
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique
)
add_component_dir (nif
@ -228,6 +228,7 @@ target_link_libraries(components
${OSGVIEWER_LIBRARIES}
${OSGTEXT_LIBRARIES}
${OSGGA_LIBRARIES}
${OSGSHADOW_LIBRARIES}
${OSGANIMATION_LIBRARIES}
${Bullet_LIBRARIES}
${SDL2_LIBRARIES}

@ -1284,6 +1284,7 @@ namespace NifOsg
boundTextures.clear();
}
// If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the shadow casting shader will need to be updated accordingly.
for (int i=0; i<Nif::NiTexturingProperty::NumTextures; ++i)
{
if (texprop->textures[i].inUse)

@ -399,7 +399,7 @@ namespace SceneUtil
return false;
}
if (!(cv->getCurrentCamera()->getCullMask() & mLightManager->getLightingMask()))
if (!(cv->getTraversalMask() & mLightManager->getLightingMask()))
return false;
// Possible optimizations:

File diff suppressed because it is too large Load Diff

@ -0,0 +1,287 @@
/* This file is based on OpenSceneGraph's include/osgShadow/ViewDependentShadowMap.
* Where applicable, any changes made are covered by OpenMW's GPL 3 license, not the OSGPL.
* The original copyright notice is listed below.
*/
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2011 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#ifndef COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H
#define COMPONENTS_SCENEUTIL_MWSHADOWTECHNIQUE_H 1
#include <osg/Camera>
#include <osg/Material>
#include <osg/MatrixTransform>
#include <osg/LightSource>
#include <osg/PolygonOffset>
#include <osgShadow/ShadowTechnique>
#include <components/shader/shadermanager.hpp>
#include <components/terrain/quadtreeworld.hpp>
namespace SceneUtil {
/** ViewDependentShadowMap provides an base implementation of view dependent shadow mapping techniques.*/
class MWShadowTechnique : public osgShadow::ShadowTechnique
{
public:
MWShadowTechnique();
MWShadowTechnique(const MWShadowTechnique& vdsm, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
META_Object(SceneUtil, MWShadowTechnique);
/** initialize the ShadowedScene and local cached data structures.*/
virtual void init();
/** run the update traversal of the ShadowedScene and update any loca chached data structures.*/
virtual void update(osg::NodeVisitor& nv);
/** run the cull traversal of the ShadowedScene and set up the rendering for this ShadowTechnique.*/
virtual void cull(osgUtil::CullVisitor& cv);
/** Resize any per context GLObject buffers to specified size. */
virtual void resizeGLObjectBuffers(unsigned int maxSize);
/** If State is non-zero, this function releases any associated OpenGL objects for
* the specified graphics context. Otherwise, releases OpenGL objects
* for all graphics contexts. */
virtual void releaseGLObjects(osg::State* = 0) const;
/** Clean scene graph from any shadow technique specific nodes, state and drawables.*/
virtual void cleanSceneGraph();
virtual void enableShadows();
virtual void disableShadows();
virtual void enableDebugHUD();
virtual void disableDebugHUD();
virtual void setSplitPointUniformLogarithmicRatio(double ratio);
virtual void setSplitPointDeltaBias(double bias);
virtual void setPolygonOffset(float factor, float units);
virtual void enableFrontFaceCulling();
virtual void disableFrontFaceCulling();
virtual void setupCastingShader(Shader::ShaderManager &shaderManager);
class ComputeLightSpaceBounds : public osg::NodeVisitor, public osg::CullStack
{
public:
ComputeLightSpaceBounds(osg::Viewport* viewport, const osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix);
void apply(osg::Node& node);
void apply(osg::Drawable& drawable);
void apply(Terrain::QuadTreeWorld& quadTreeWorld);
void apply(osg::Billboard&);
void apply(osg::Projection&);
void apply(osg::Transform& transform);
void apply(osg::Camera&);
void updateBound(const osg::BoundingBox& bb);
void update(const osg::Vec3& v);
osg::BoundingBox _bb;
};
struct Frustum
{
Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar);
osg::Matrixd projectionMatrix;
osg::Matrixd modelViewMatrix;
typedef std::vector<osg::Vec3d> Vertices;
Vertices corners;
typedef std::vector<unsigned int> Indices;
typedef std::vector<Indices> Faces;
Faces faces;
typedef std::vector<Indices> Edges;
Edges edges;
osg::Vec3d eye;
osg::Vec3d centerNearPlane;
osg::Vec3d centerFarPlane;
osg::Vec3d center;
osg::Vec3d frustumCenterLine;
};
// forward declare
class ViewDependentData;
struct LightData : public osg::Referenced
{
LightData(ViewDependentData* vdd);
virtual void setLightData(osg::RefMatrix* lm, const osg::Light* l, const osg::Matrixd& modelViewMatrix);
ViewDependentData* _viewDependentData;
osg::ref_ptr<osg::RefMatrix> lightMatrix;
osg::ref_ptr<const osg::Light> light;
osg::Vec4d lightPos;
osg::Vec3d lightPos3;
osg::Vec3d lightDir;
bool directionalLight;
typedef std::vector<unsigned int> ActiveTextureUnits;
ActiveTextureUnits textureUnits;
};
typedef std::list< osg::ref_ptr<LightData> > LightDataList;
struct ShadowData : public osg::Referenced
{
ShadowData(ViewDependentData* vdd);
virtual void releaseGLObjects(osg::State* = 0) const;
ViewDependentData* _viewDependentData;
unsigned int _textureUnit;
osg::ref_ptr<osg::Texture2D> _texture;
osg::ref_ptr<osg::TexGen> _texgen;
osg::ref_ptr<osg::Camera> _camera;
};
typedef std::list< osg::ref_ptr<ShadowData> > ShadowDataList;
class ViewDependentData : public osg::Referenced
{
public:
ViewDependentData(MWShadowTechnique* vdsm);
const MWShadowTechnique* getViewDependentShadowMap() const { return _viewDependentShadowMap; }
LightDataList& getLightDataList() { return _lightDataList; }
ShadowDataList& getShadowDataList() { return _shadowDataList; }
osg::StateSet* getStateSet() { return _stateset.get(); }
virtual void releaseGLObjects(osg::State* = 0) const;
protected:
virtual ~ViewDependentData() {}
MWShadowTechnique* _viewDependentShadowMap;
osg::ref_ptr<osg::StateSet> _stateset;
LightDataList _lightDataList;
ShadowDataList _shadowDataList;
};
virtual ViewDependentData* createViewDependentData(osgUtil::CullVisitor* cv);
ViewDependentData* getViewDependentData(osgUtil::CullVisitor* cv);
virtual void createShaders();
virtual bool selectActiveLights(osgUtil::CullVisitor* cv, ViewDependentData* vdd) const;
virtual osg::Polytope computeLightViewFrustumPolytope(Frustum& frustum, LightData& positionedLight);
virtual bool computeShadowCameraSettings(Frustum& frustum, LightData& positionedLight, osg::Matrixd& projectionMatrix, osg::Matrixd& viewMatrix);
virtual bool cropShadowCameraToMainFrustum(Frustum& frustum, osg::Camera* camera, double viewNear, double viewFar, std::vector<osg::Plane>& planeList);
virtual bool adjustPerspectiveShadowMapCameraSettings(osgUtil::RenderStage* renderStage, Frustum& frustum, LightData& positionedLight, osg::Camera* camera, double viewNear, double viewFar);
virtual bool assignTexGenSettings(osgUtil::CullVisitor* cv, osg::Camera* camera, unsigned int textureUnit, osg::TexGen* texgen);
virtual void cullShadowReceivingScene(osgUtil::CullVisitor* cv) const;
virtual void cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const;
virtual osg::StateSet* selectStateSetForRenderingShadow(ViewDependentData& vdd) const;
protected:
virtual ~MWShadowTechnique();
typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr<ViewDependentData> > ViewDependentDataMap;
mutable OpenThreads::Mutex _viewDependentDataMapMutex;
ViewDependentDataMap _viewDependentDataMap;
osg::ref_ptr<osg::StateSet> _shadowRecievingPlaceholderStateSet;
osg::ref_ptr<osg::StateSet> _shadowCastingStateSet;
osg::ref_ptr<osg::PolygonOffset> _polygonOffset;
osg::ref_ptr<osg::Texture2D> _fallbackBaseTexture;
osg::ref_ptr<osg::Texture2D> _fallbackShadowMapTexture;
typedef std::vector< osg::ref_ptr<osg::Uniform> > Uniforms;
mutable OpenThreads::Mutex _accessUniformsAndProgramMutex;
Uniforms _uniforms;
osg::ref_ptr<osg::Program> _program;
bool _enableShadows;
double _splitPointUniformLogRatio = 0.5;
double _splitPointDeltaBias = 0.0;
float _polygonOffsetFactor = 1.1;
float _polygonOffsetUnits = 4.0;
bool _useFrontFaceCulling = true;
class DebugHUD : public osg::Referenced
{
public:
DebugHUD(int numberOfShadowMapsPerLight);
virtual void draw(osg::ref_ptr<osg::Texture2D> texture, unsigned int shadowMapNumber, const osg::Matrixd &matrix, osgUtil::CullVisitor& cv);
virtual void releaseGLObjects(osg::State* state = 0) const;
virtual void setFrustumVertices(osg::ref_ptr<osg::Vec3Array> vertices, unsigned int traversalNumber);
protected:
virtual void addAnotherShadowMap();
static const int sDebugTextureUnit = 0;
std::vector<osg::ref_ptr<osg::Camera>> mDebugCameras;
osg::ref_ptr<osg::Program> mDebugProgram;
std::vector<osg::ref_ptr<osg::Node>> mDebugGeometry;
std::vector<osg::ref_ptr<osg::Group>> mFrustumTransforms;
std::vector<osg::ref_ptr<osg::Uniform>> mFrustumUniforms;
std::vector<osg::ref_ptr<osg::Geometry>> mFrustumGeometries;
};
osg::ref_ptr<DebugHUD> _debugHud;
osg::ref_ptr<osg::Program> _castingProgram;
};
}
#endif

@ -71,6 +71,8 @@ void RigGeometry::setSourceGeometry(osg::ref_ptr<osg::Geometry> sourceGeometry)
to.setSupportsDisplayList(false);
to.setUseVertexBufferObjects(true);
to.setCullingActive(false); // make sure to disable culling since that's handled by this class
to.setComputeBoundingBoxCallback(new CopyBoundingBoxCallback());
to.setComputeBoundingSphereCallback(new CopyBoundingSphereCallback());
// vertices and normals are modified every frame, so we need to deep copy them.
// assign a dedicated VBO to make sure that modifications don't interfere with source geometry's VBO.
@ -296,6 +298,14 @@ void RigGeometry::updateBounds(osg::NodeVisitor *nv)
_boundingSphereComputed = true;
for (unsigned int i=0; i<getNumParents(); ++i)
getParent(i)->dirtyBound();
for (unsigned int i = 0; i < 2; ++i)
{
osg::Geometry& geom = *mGeometry[i];
static_cast<CopyBoundingBoxCallback*>(geom.getComputeBoundingBoxCallback())->boundingBox = _boundingBox;
static_cast<CopyBoundingSphereCallback*>(geom.getComputeBoundingSphereCallback())->boundingSphere = _boundingSphere;
geom.dirtyBound();
}
}
}

@ -50,6 +50,20 @@ namespace SceneUtil
virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
virtual void accept(osg::PrimitiveFunctor&) const;
struct CopyBoundingBoxCallback : osg::Drawable::ComputeBoundingBoxCallback
{
osg::BoundingBox boundingBox;
virtual osg::BoundingBox computeBound(const osg::Drawable&) const override { return boundingBox; }
};
struct CopyBoundingSphereCallback : osg::Node::ComputeBoundingSphereCallback
{
osg::BoundingSphere boundingSphere;
virtual osg::BoundingSphere computeBound(const osg::Node&) const override { return boundingSphere; }
};
private:
void cull(osg::NodeVisitor* nv);
void updateBounds(osg::NodeVisitor* nv);

@ -0,0 +1,158 @@
#include "shadow.hpp"
#include <osgShadow/ShadowedScene>
#include <components/settings/settings.hpp>
namespace SceneUtil
{
using namespace osgShadow;
void ShadowManager::setupShadowSettings()
{
mEnableShadows = Settings::Manager::getBool("enable shadows", "Shadows");
if (!mEnableShadows)
{
mShadowTechnique->disableShadows();
return;
}
mShadowTechnique->enableShadows();
mShadowSettings->setLightNum(0);
mShadowSettings->setReceivesShadowTraversalMask(~0u);
int numberOfShadowMapsPerLight = Settings::Manager::getInt("number of shadow maps", "Shadows");
mShadowSettings->setNumShadowMapsPerLight(numberOfShadowMapsPerLight);
mShadowSettings->setBaseShadowTextureUnit(8 - numberOfShadowMapsPerLight);
mShadowSettings->setMinimumShadowMapNearFarRatio(Settings::Manager::getFloat("minimum lispsm near far ratio", "Shadows"));
if (Settings::Manager::getBool("compute tight scene bounds", "Shadows"))
mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES);
int mapres = Settings::Manager::getInt("shadow map resolution", "Shadows");
mShadowSettings->setTextureSize(osg::Vec2s(mapres, mapres));
mShadowTechnique->setSplitPointUniformLogarithmicRatio(Settings::Manager::getFloat("split point uniform logarithmic ratio", "Shadows"));
mShadowTechnique->setSplitPointDeltaBias(Settings::Manager::getFloat("split point bias", "Shadows"));
mShadowTechnique->setPolygonOffset(Settings::Manager::getFloat("polygon offset factor", "Shadows"), Settings::Manager::getFloat("polygon offset units", "Shadows"));
if (Settings::Manager::getBool("use front face culling", "Shadows"))
mShadowTechnique->enableFrontFaceCulling();
else
mShadowTechnique->disableFrontFaceCulling();
if (Settings::Manager::getBool("allow shadow map overlap", "Shadows"))
mShadowSettings->setMultipleShadowMapHint(osgShadow::ShadowSettings::CASCADED);
else
mShadowSettings->setMultipleShadowMapHint(osgShadow::ShadowSettings::PARALLEL_SPLIT);
if (Settings::Manager::getBool("enable debug hud", "Shadows"))
mShadowTechnique->enableDebugHUD();
else
mShadowTechnique->disableDebugHUD();
}
void ShadowManager::disableShadowsForStateSet(osg::ref_ptr<osg::StateSet> stateset)
{
int numberOfShadowMapsPerLight = Settings::Manager::getInt("number of shadow maps", "Shadows");
int baseShadowTextureUnit = 8 - numberOfShadowMapsPerLight;
osg::ref_ptr<osg::Image> fakeShadowMapImage = new osg::Image();
fakeShadowMapImage->allocateImage(1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT);
*(float*)fakeShadowMapImage->data() = std::numeric_limits<float>::infinity();
osg::ref_ptr<osg::Texture> fakeShadowMapTexture = new osg::Texture2D(fakeShadowMapImage);
fakeShadowMapTexture->setShadowComparison(true);
fakeShadowMapTexture->setShadowCompareFunc(osg::Texture::ShadowCompareFunc::ALWAYS);
for (int i = baseShadowTextureUnit; i < baseShadowTextureUnit + numberOfShadowMapsPerLight; ++i)
{
stateset->setTextureAttributeAndModes(i, fakeShadowMapTexture, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED);
stateset->addUniform(new osg::Uniform(("shadowTexture" + std::to_string(i - baseShadowTextureUnit)).c_str(), i));
stateset->addUniform(new osg::Uniform(("shadowTextureUnit" + std::to_string(i - baseShadowTextureUnit)).c_str(), i));
}
}
ShadowManager::ShadowManager(osg::ref_ptr<osg::Group> sceneRoot, osg::ref_ptr<osg::Group> rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager) : mShadowedScene(new osgShadow::ShadowedScene),
mShadowTechnique(new MWShadowTechnique),
mOutdoorShadowCastingMask(outdoorShadowCastingMask),
mIndoorShadowCastingMask(indoorShadowCastingMask)
{
mShadowedScene->setShadowTechnique(mShadowTechnique);
mShadowedScene->addChild(sceneRoot);
rootNode->addChild(mShadowedScene);
mShadowSettings = mShadowedScene->getShadowSettings();
setupShadowSettings();
mShadowTechnique->setupCastingShader(shaderManager);
enableOutdoorMode();
}
Shader::ShaderManager::DefineMap ShadowManager::getShadowDefines()
{
if (!mEnableShadows)
return getShadowsDisabledDefines();
Shader::ShaderManager::DefineMap definesWithShadows;
definesWithShadows["shadows_enabled"] = "1";
for (unsigned int i = 0; i < mShadowSettings->getNumShadowMapsPerLight(); ++i)
definesWithShadows["shadow_texture_unit_list"] += std::to_string(i) + ",";
// remove extra comma
definesWithShadows["shadow_texture_unit_list"] = definesWithShadows["shadow_texture_unit_list"].substr(0, definesWithShadows["shadow_texture_unit_list"].length() - 1);
definesWithShadows["shadowMapsOverlap"] = Settings::Manager::getBool("allow shadow map overlap", "Shadows") ? "1" : "0";
definesWithShadows["useShadowDebugOverlay"] = Settings::Manager::getBool("enable debug overlay", "Shadows") ? "1" : "0";
// switch this to reading settings if it's ever exposed to the user
definesWithShadows["perspectiveShadowMaps"] = mShadowSettings->getShadowMapProjectionHint() == ShadowSettings::PERSPECTIVE_SHADOW_MAP ? "1" : "0";
definesWithShadows["disableNormalOffsetShadows"] = Settings::Manager::getFloat("normal offset distance", "Shadows") == 0.0 ? "1" : "0";
definesWithShadows["shadowNormalOffset"] = std::to_string(Settings::Manager::getFloat("normal offset distance", "Shadows"));
return definesWithShadows;
}
Shader::ShaderManager::DefineMap ShadowManager::getShadowsDisabledDefines()
{
Shader::ShaderManager::DefineMap definesWithoutShadows;
definesWithoutShadows["shadows_enabled"] = "0";
definesWithoutShadows["shadow_texture_unit_list"] = "";
definesWithoutShadows["shadowMapsOverlap"] = "0";
definesWithoutShadows["useShadowDebugOverlay"] = "0";
definesWithoutShadows["perspectiveShadowMaps"] = "0";
definesWithoutShadows["disableNormalOffsetShadows"] = "0";
definesWithoutShadows["shadowNormalOffset"] = "0.0";
return definesWithoutShadows;
}
void ShadowManager::enableIndoorMode()
{
if (Settings::Manager::getBool("enable indoor shadows", "Shadows"))
mShadowSettings->setCastsShadowTraversalMask(mIndoorShadowCastingMask);
else
mShadowTechnique->disableShadows();
}
void ShadowManager::enableOutdoorMode()
{
if (mEnableShadows)
mShadowTechnique->enableShadows();
mShadowSettings->setCastsShadowTraversalMask(mOutdoorShadowCastingMask);
}
}

@ -0,0 +1,43 @@
#ifndef COMPONENTS_SCENEUTIL_SHADOW_H
#define COMPONENTS_SCENEUTIL_SHADOW_H
#include <osgShadow/ShadowSettings>
#include <osgShadow/ShadowedScene>
#include <components/shader/shadermanager.hpp>
#include "mwshadowtechnique.hpp"
namespace SceneUtil
{
class ShadowManager
{
public:
static void disableShadowsForStateSet(osg::ref_ptr<osg::StateSet> stateSet);
ShadowManager(osg::ref_ptr<osg::Group> sceneRoot, osg::ref_ptr<osg::Group> rootNode, unsigned int outdoorShadowCastingMask, unsigned int indoorShadowCastingMask, Shader::ShaderManager &shaderManager);
virtual ~ShadowManager() = default;
virtual void setupShadowSettings();
virtual Shader::ShaderManager::DefineMap getShadowDefines();
virtual Shader::ShaderManager::DefineMap getShadowsDisabledDefines();
virtual void enableIndoorMode();
virtual void enableOutdoorMode();
protected:
bool mEnableShadows;
osg::ref_ptr<osgShadow::ShadowedScene> mShadowedScene;
osg::ref_ptr<osgShadow::ShadowSettings> mShadowSettings;
osg::ref_ptr<MWShadowTechnique> mShadowTechnique;
unsigned int mOutdoorShadowCastingMask;
unsigned int mIndoorShadowCastingMask;
};
}
#endif //COMPONENTS_SCENEUTIL_SHADOW_H

@ -21,6 +21,44 @@ namespace Shader
mPath = path;
}
bool addLineDirectivesAfterConditionalBlocks(std::string& source)
{
for (size_t position = 0; position < source.length(); )
{
size_t foundPos = source.find("#endif", position);
foundPos = std::min(foundPos, source.find("#elif", position));
foundPos = std::min(foundPos, source.find("#else", position));
if (foundPos == std::string::npos)
break;
foundPos = source.find_first_of("\n\r", foundPos);
foundPos = source.find_first_not_of("\n\r", foundPos);
size_t lineDirectivePosition = source.rfind("#line", foundPos);
int lineNumber;
if (lineDirectivePosition != std::string::npos)
{
size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length();
size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart);
std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart);
lineNumber = std::stoi(lineNumberString) - 1;
}
else
{
lineDirectivePosition = 0;
lineNumber = 1;
}
lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\n');
source.replace(foundPos, 0, "#line " + std::to_string(lineNumber) + "\n");
position = foundPos;
}
return true;
}
bool parseIncludes(boost::filesystem::path shaderPath, std::string& source)
{
boost::replace_all(source, "\r\n", "\n");
@ -54,14 +92,30 @@ namespace Shader
std::stringstream buffer;
buffer << includeFstream.rdbuf();
std::string stringRepresentation = buffer.str();
addLineDirectivesAfterConditionalBlocks(stringRepresentation);
// insert #line directives so we get correct line numbers in compiler errors
int includedFileNumber = fileNumber++;
int lineNumber = std::count(source.begin(), source.begin() + foundPos, '\n');
size_t lineDirectivePosition = source.rfind("#line", foundPos);
int lineNumber;
if (lineDirectivePosition != std::string::npos)
{
size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length();
size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart);
std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart);
lineNumber = std::stoi(lineNumberString) - 1;
}
else
{
lineDirectivePosition = 0;
lineNumber = 1;
}
lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + foundPos, '\n');
std::stringstream toInsert;
toInsert << "#line 0 " << includedFileNumber << "\n" << buffer.str() << "\n#line " << lineNumber << " 0\n";
toInsert << "#line 0 " << includedFileNumber << "\n" << stringRepresentation << "\n#line " << lineNumber << " 0\n";
source.replace(foundPos, (end-foundPos+1), toInsert.str());
@ -74,13 +128,97 @@ namespace Shader
return true;
}
bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines)
bool parseFors(std::string& source)
{
const char escapeCharacter = '$';
size_t foundPos = 0;
while ((foundPos = source.find(escapeCharacter)) != std::string::npos)
{
size_t endPos = source.find_first_of(" \n\r()[].;,", foundPos);
if (endPos == std::string::npos)
{
Log(Debug::Error) << "Unexpected EOF";
return false;
}
std::string command = source.substr(foundPos + 1, endPos - (foundPos + 1));
if (command != "foreach")
{
Log(Debug::Error) << "Unknown shader directive: $" << command;
return false;
}
size_t iterNameStart = endPos + 1;
size_t iterNameEnd = source.find_first_of(" \n\r()[].;,", iterNameStart);
if (iterNameEnd == std::string::npos)
{
Log(Debug::Error) << "Unexpected EOF";
return false;
}
std::string iteratorName = "$" + source.substr(iterNameStart, iterNameEnd - iterNameStart);
size_t listStart = iterNameEnd + 1;
size_t listEnd = source.find_first_of("\n\r", listStart);
if (listEnd == std::string::npos)
{
Log(Debug::Error) << "Unexpected EOF";
return false;
}
std::string list = source.substr(listStart, listEnd - listStart);
std::vector<std::string> listElements;
if (list != "")
boost::split(listElements, list, boost::is_any_of(","));
size_t contentStart = source.find_first_not_of("\n\r", listEnd);
size_t contentEnd = source.find("$endforeach", contentStart);
if (contentEnd == std::string::npos)
{
Log(Debug::Error) << "Unexpected EOF";
return false;
}
std::string content = source.substr(contentStart, contentEnd - contentStart);
size_t overallEnd = contentEnd + std::string("$endforeach").length();
size_t lineDirectivePosition = source.rfind("#line", overallEnd);
int lineNumber;
if (lineDirectivePosition != std::string::npos)
{
size_t lineNumberStart = lineDirectivePosition + std::string("#line ").length();
size_t lineNumberEnd = source.find_first_not_of("0123456789", lineNumberStart);
std::string lineNumberString = source.substr(lineNumberStart, lineNumberEnd - lineNumberStart);
lineNumber = std::stoi(lineNumberString);
}
else
{
lineDirectivePosition = 0;
lineNumber = 2;
}
lineNumber += std::count(source.begin() + lineDirectivePosition, source.begin() + overallEnd, '\n');
std::string replacement = "";
for (std::vector<std::string>::const_iterator element = listElements.cbegin(); element != listElements.cend(); element++)
{
std::string contentInstance = content;
size_t foundIterator;
while ((foundIterator = contentInstance.find(iteratorName)) != std::string::npos)
contentInstance.replace(foundIterator, iteratorName.length(), *element);
replacement += contentInstance;
}
replacement += "\n#line " + std::to_string(lineNumber);
source.replace(foundPos, overallEnd - foundPos, replacement);
}
return true;
}
bool parseDefines(std::string& source, const ShaderManager::DefineMap& defines, const ShaderManager::DefineMap& globalDefines)
{
const char escapeCharacter = '@';
size_t foundPos = 0;
std::vector<std::string> forIterators;
while ((foundPos = source.find(escapeCharacter)) != std::string::npos)
{
size_t endPos = source.find_first_of(" \n\r()[].;", foundPos);
size_t endPos = source.find_first_of(" \n\r()[].;,", foundPos);
if (endPos == std::string::npos)
{
Log(Debug::Error) << "Unexpected EOF";
@ -88,14 +226,46 @@ namespace Shader
}
std::string define = source.substr(foundPos+1, endPos - (foundPos+1));
ShaderManager::DefineMap::const_iterator defineFound = defines.find(define);
if (defineFound == defines.end())
ShaderManager::DefineMap::const_iterator globalDefineFound = globalDefines.find(define);
if (define == "foreach")
{
Log(Debug::Error) << "Undefined " << define;
return false;
source.replace(foundPos, 1, "$");
size_t iterNameStart = endPos + 1;
size_t iterNameEnd = source.find_first_of(" \n\r()[].;,", iterNameStart);
if (iterNameEnd == std::string::npos)
{
Log(Debug::Error) << "Unexpected EOF";
return false;
}
forIterators.push_back(source.substr(iterNameStart, iterNameEnd - iterNameStart));
}
else if (define == "endforeach")
{
source.replace(foundPos, 1, "$");
if (forIterators.empty())
{
Log(Debug::Error) << "endforeach without foreach";
return false;
}
else
forIterators.pop_back();
}
else if (std::find(forIterators.begin(), forIterators.end(), define) != forIterators.end())
{
source.replace(foundPos, 1, "$");
}
else if (defineFound != defines.end())
{
source.replace(foundPos, endPos - foundPos, defineFound->second);
}
else if (globalDefineFound != globalDefines.end())
{
source.replace(foundPos, endPos - foundPos, globalDefineFound->second);
}
else
{
source.replace(foundPos, endPos-foundPos, defineFound->second);
Log(Debug::Error) << "Undefined " << define;
return false;
}
}
return true;
@ -122,7 +292,7 @@ namespace Shader
// parse includes
std::string source = buffer.str();
if (!parseIncludes(boost::filesystem::path(mPath), source))
if (!addLineDirectivesAfterConditionalBlocks(source) || !parseIncludes(boost::filesystem::path(mPath), source))
return nullptr;
templateIt = mShaderTemplates.insert(std::make_pair(shaderTemplate, source)).first;
@ -132,7 +302,7 @@ namespace Shader
if (shaderIt == mShaders.end())
{
std::string shaderSource = templateIt->second;
if (!parseDefines(shaderSource, defines))
if (!parseDefines(shaderSource, defines, mGlobalDefines) || !parseFors(shaderSource))
{
// Add to the cache anyway to avoid logging the same error over and over.
mShaders.insert(std::make_pair(std::make_pair(shaderTemplate, defines), nullptr));
@ -164,11 +334,39 @@ namespace Shader
return found->second;
}
ShaderManager::DefineMap ShaderManager::getGlobalDefines()
{
return DefineMap(mGlobalDefines);
}
void ShaderManager::setGlobalDefines(DefineMap & globalDefines)
{
mGlobalDefines = globalDefines;
for (auto shaderMapElement: mShaders)
{
std::string templateId = shaderMapElement.first.first;
ShaderManager::DefineMap defines = shaderMapElement.first.second;
osg::ref_ptr<osg::Shader> shader = shaderMapElement.second;
if (shader == nullptr)
// I'm not sure how to handle a shader that was already broken as there's no way to get a potential replacement to the nodes that need it.
continue;
std::string shaderSource = mShaderTemplates[templateId];
if (!parseDefines(shaderSource, defines, mGlobalDefines) || !parseFors(shaderSource))
// We just broke the shader and there's no way to force existing objects back to fixed-function mode as we would when creating the shader.
// If we put a nullptr in the shader map, we just lose the ability to put a working one in later.
continue;
shader->setShaderSource(shaderSource);
}
}
void ShaderManager::releaseGLObjects(osg::State *state)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex);
for (auto shader : mShaders)
shader.second->releaseGLObjects(state);
{
if (shader.second != nullptr)
shader.second->releaseGLObjects(state);
}
for (auto program : mPrograms)
program.second->releaseGLObjects(state);
}

@ -8,6 +8,8 @@
#include <osg/Shader>
#include <osgViewer/Viewer>
#include <OpenThreads/Mutex>
namespace Shader
@ -32,11 +34,21 @@ namespace Shader
osg::ref_ptr<osg::Program> getProgram(osg::ref_ptr<osg::Shader> vertexShader, osg::ref_ptr<osg::Shader> fragmentShader);
/// Get (a copy of) the DefineMap used to construct all shaders
DefineMap getGlobalDefines();
/// Set the DefineMap used to construct all shaders
/// @param defines The DefineMap to use
/// @note This will change the source code for any shaders already created, potentially causing problems if they're being used to render a frame. It is recommended that any associated Viewers have their threading stopped while this function is running if any shaders are in use.
void setGlobalDefines(DefineMap & globalDefines);
void releaseGLObjects(osg::State* state);
private:
std::string mPath;
DefineMap mGlobalDefines;
// <name, code>
typedef std::map<std::string, std::string> TemplateMap;
TemplateMap mShaderTemplates;

@ -23,8 +23,7 @@ namespace Shader
ShaderVisitor::ShaderRequirements::ShaderRequirements()
: mShaderRequired(false)
, mColorMaterial(false)
, mVertexColorMode(GL_AMBIENT_AND_DIFFUSE)
, mColorMode(0)
, mMaterialOverridden(false)
, mNormalHeight(false)
, mTexStageRequiringTangents(-1)
@ -217,6 +216,13 @@ namespace Shader
mRequirements.back().mShaderRequired = true;
}
}
if (diffuseMap)
{
if (!writableStateSet)
writableStateSet = getWritableStateSet(node);
writableStateSet->addUniform(new osg::Uniform("useDiffuseMapForShadowAlpha", true));
}
}
const osg::StateSet::AttributeList& attributes = stateset->getAttributeList();
@ -230,8 +236,29 @@ namespace Shader
mRequirements.back().mMaterialOverridden = true;
const osg::Material* mat = static_cast<const osg::Material*>(it->second.first.get());
mRequirements.back().mColorMaterial = (mat->getColorMode() != osg::Material::OFF);
mRequirements.back().mVertexColorMode = mat->getColorMode();
if (!writableStateSet)
writableStateSet = getWritableStateSet(node);
int colorMode;
switch (mat->getColorMode())
{
case osg::Material::OFF:
colorMode = 0;
break;
case GL_AMBIENT:
colorMode = 3;
break;
default:
case GL_AMBIENT_AND_DIFFUSE:
colorMode = 2;
break;
case GL_EMISSION:
colorMode = 1;
break;
}
mRequirements.back().mColorMode = colorMode;
}
}
}
@ -272,30 +299,13 @@ namespace Shader
defineMap[texIt->second + std::string("UV")] = std::to_string(texIt->first);
}
if (!reqs.mColorMaterial)
defineMap["colorMode"] = "0";
else
{
switch (reqs.mVertexColorMode)
{
case GL_AMBIENT:
defineMap["colorMode"] = "3";
break;
default:
case GL_AMBIENT_AND_DIFFUSE:
defineMap["colorMode"] = "2";
break;
case GL_EMISSION:
defineMap["colorMode"] = "1";
break;
}
}
defineMap["forcePPL"] = mForcePerPixelLighting ? "1" : "0";
defineMap["clamp"] = mClampLighting ? "1" : "0";
defineMap["parallax"] = reqs.mNormalHeight ? "1" : "0";
writableStateSet->addUniform(new osg::Uniform("colorMode", reqs.mColorMode));
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));

@ -81,9 +81,8 @@ namespace Shader
bool mShaderRequired;
bool mColorMaterial;
// osg::Material::ColorMode
int mVertexColorMode;
int mColorMode;
bool mMaterialOverridden;
bool mNormalHeight; // true if normal map has height info in alpha channel

@ -218,7 +218,6 @@ namespace Terrain
defineMap["clamp"] = clampLighting ? "1" : "0";
defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0";
defineMap["blendMap"] = !firstLayer ? "1" : "0";
defineMap["colorMode"] = "2";
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
@ -231,6 +230,7 @@ namespace Terrain
}
stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader));
stateset->addUniform(new osg::Uniform("colorMode", 2));
}
else
{

@ -134,7 +134,7 @@ ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv)
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
ViewData* vd = mViewDataMap->getViewData(cv->getCurrentCamera());
vd->setEyePoint(nv.getEyePoint());
vd->setEyePoint(nv.getViewPoint());
return vd;
}
else // INTERSECTION_VISITOR

@ -5,6 +5,7 @@
#include <sstream>
#include <components/misc/constants.hpp>
#include <components/sceneutil/mwshadowtechnique.hpp>
#include "quadtreenode.hpp"
#include "storage.hpp"
@ -332,7 +333,19 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, ChunkManager* chunk
void QuadTreeWorld::accept(osg::NodeVisitor &nv)
{
if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR)
{
if (nv.getName().find("AcceptedByComponentsTerrainQuadTreeWorld") != std::string::npos)
{
if (nv.getName().find("SceneUtil::MWShadowTechnique::ComputeLightSpaceBounds") != std::string::npos)
{
SceneUtil::MWShadowTechnique::ComputeLightSpaceBounds* clsb = static_cast<SceneUtil::MWShadowTechnique::ComputeLightSpaceBounds*>(&nv);
clsb->apply(*this);
}
else
nv.apply(*mRootNode);
}
return;
}
ViewData* vd = mRootNode->getView(nv);
@ -350,7 +363,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
traverseToCell(mRootNode.get(), vd, x,y);
}
else
traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getEyePoint(), true);
traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true);
}
else
mRootNode->traverse(nv);

@ -47,10 +47,22 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv)
osg::RefMatrix& matrix = *cv->getModelViewMatrix();
if (cv->getComputeNearFarMode() && bb.valid())
{
if (!cv->updateCalculatedNearFar(matrix, *this, false))
return;
}
float depth = bb.valid() ? distance(bb.center(),matrix) : 0.0f;
if (osg::isNaN(depth))
return;
if (cv->getCurrentCamera()->getName() == "ShadowCamera")
{
cv->addDrawableAndDepth(this, &matrix, depth);
return;
}
bool pushedLight = mLightListCallback && mLightListCallback->pushLightState(this, cv);
for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it)

@ -47,6 +47,7 @@ The ranges included with each setting are the physically possible ranges, not re
game
general
shaders
shadows
input
saves
sound

@ -1,6 +1,7 @@
Shaders Settings
################
.. _force-shaders-label:
force shaders
-------------

@ -0,0 +1,205 @@
Shadow Settings
###############
Main settings
*************
enable shadows
--------------
:Type: boolean
:Range: True/False
:Default: False
Enable or disable the rendering of shadows.
Unlike in the original Morrowind engine, 'Shadow Mapping' is used, which can have a performance impact, but has more realistic results.
Bear in mind that this will force OpenMW to use shaders as if :ref:`force-shaders-label` was enabled.
A keen developer may be able to implement compatibility with fixed-function mode using the advice of `this post <https://github.com/OpenMW/openmw/pull/1547#issuecomment-369657381>`_, but it may be more difficult than it seems.
number of shadow maps
---------------------
:Type: integer
:Range: 1 to 8, but higher values may conflict with other texture effects
:Default: 3
Control how many shadow maps to use - more of these means each shadow map texel covers less area, producing better-looking shadows, but may decrease performance.
Using too many shadow maps will lead to them overriding texture slots used for other effects, producing unpleasant artefacts.
A value of three is recommended in most cases, but other values may produce better results or performance.
allow shadow map overlap
------------------------
:Type: boolean
:Range: True/False
:Default: True
If true, allow shadow maps to overlap.
Counter-intuitively, will produce much better results when the light is behind the camera.
When enabled, OpenMW uses Cascaded Shadow Maps and when disabled, it uses Parallel Split Shadow Maps.
enable debug hud
----------------
:Type: boolean
:Range: True/False
:Default: False
Enable or disable the debug hud to see what the shadow map(s) contain.
This setting is only recommended for developers, bug reporting and advanced users performing fine-tuning of shadow settings.
enable debug overlay
----------------
:Type: boolean
:Range: True/False
:Default: False
Enable or disable the debug overlay to see the area covered by each shadow map.
This setting is only recommended for developers, bug reporting and advanced users performing fine-tuning of shadow settings.
compute tight scene bounds
--------------------------
:Type: boolean
:Range: True/False
:Default: False
With this setting enabled, attempt to better use the shadow map(s) by making them cover a smaller area.
This can be especially helpful when looking downwards with a high viewing distance but will be less useful with the default value.
The performance impact of this may be very large.
shadow map resolution
---------------------
:Type: integer
:Range: Dependent on GPU/driver combination
:Default: 1024
Control How large to make the shadow map(s).
Higher values increase GPU load but can produce better-looking results.
Power-of-two values may turn out to be faster than smaller values which are not powers of two on some GPU/driver combinations.
actor shadows
-------------
:Type: boolean
:Range: True/False
:Default: False
Allow actors to cast shadows.
Potentially decreases performance.
player shadows
--------------
:Type: boolean
:Range: True/False
:Default: False
Allow the player to cast shadows.
Potentially decreases performance.
terrain shadows
---------------
:Type: boolean
:Range: True/False
:Default: False
Allow terrain to cast shadows.
Potentially decreases performance.
object shadows
--------------
:Type: boolean
:Range: True/False
:Default: False
Allow static objects to cast shadows.
Potentially decreases performance.
enable indoor shadows
---------------------
:Type: boolean
:Range: True/False
:Default: False
Allow shadows indoors.
Due to limitations with Morrowind's data, only actors can cast shadows indoors without the ceiling casting a shadow everywhere.
Some might feel this is distracting as shadows can be cast through other objects, so indoor shadows can be disabled completely.
Expert settings
***************
These settings are probably too complicated for regular users to judge what might be good values to set them to.
If you've got a good understanding of how shadow mapping works, or you've got enough time to try a large set of values, you may get better results tuning these yourself.
Copying values from another user who's done careful tuning is the recommended way of arriving at an optimal value for these settings.
Understanding what some of these do might be easier for people who've read `this paper on Parallel Split Shadow Maps <https://pdfs.semanticscholar.org/15a9/f2a7cf6b1494f45799617c017bd42659d753.pdf>`_ and understood how they interact with the transformation used with Light Space Perspective Shadow Maps.
polygon offset factor
---------------------
:Type: float
:Range: Theoretically the whole range of 32-bit floating point, but values just above 1.0 are most sensible.
:Default: 1.1
Used as the factor parameter for the polygon offset used for shadow map rendering.
Higher values reduce shadow flicker, but risk increasing Peter Panning.
See `the OpenGL documentation for glPolygonOffset <https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml>`_ for details.
polygon offset units
---------------------
:Type: float
:Range: Theoretically the whole range of 32-bit floating point, but values between 1 and 10 are most sensible.
:Default: 4.0
Used as the units parameter for the polygon offset used for shadow map rendering.
Higher values reduce shadow flicker, but risk increasing Peter Panning.
See `the OpenGL documentation for glPolygonOffset <https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml>`_ for details.
use front face culling
----------------------
:Type: boolean
:Range: True/False
:Default: True
Excludes theoretically unnecessary faces from shadow maps, slightly increasing performance.
In practice, Peter Panning can be much less visible with these faces included, so if you have high polygon offset values, disabling this may help minimise the side effects.
split point uniform logarithmic ratio
-------------------------------------
:Type: float
:Range: 0.0-1.0 for sensible results. Other values may 'work' but could behave bizarrely.
:Default: 0.5
Controls the ratio of :math:`C_i^{log}` versus :math:`C_i^{uniform}` used to form the Practical Split Scheme as described in the linked paper.
When using a larger-than-default viewing distance and distant terrain, and you have `allow shadow map overlap`_ enabled, larger values will prevent nearby shadows losing quality.
It is therefore recommended that this isn't left at the default when the viewing distance is changed.
split point bias
----------------
:Type: float
:Range: Any value supported by C++ floats on your platform, although undesirable behaviour is more likely to appear the further the value is from zero.
:Default: 0.0
The :math:`\delta_{bias}` parameter used to form the Practical Split Scheme as described in the linked paper.
minimum lispsm near far ratio
-----------------------------
:Type: float
:Range: Must be greater than zero.
:Default: 0.25
Controls the minimum near/far ratio for the Light Space Perspective Shadow Map transformation.
Helps prevent too much detail being brought towards the camera at the expense of detail further from the camera.
Increasing this pushes detail further away by moving the frustum apex further from the near plane.

@ -647,3 +647,62 @@ enable nav mesh render = false
# Render agents paths (true, false)
enable agents paths render = false
[Shadows]
# Enable or disable shadows. Bear in mind that this will force OpenMW to use shaders as if "[Shaders]/force shaders" was set to true.
enable shadows = false
# How many shadow maps to use - more of these means each shadow map texel covers less area, producing better looking shadows, but may decrease performance.
number of shadow maps = 3
# If true, allow shadow maps to overlap. Counter-intuitively, will produce better results when the light is behind the camera. When enabled, OpenMW uses Cascaded Shadow Maps and when disabled, it uses Parallel Split Shadow Maps.
allow shadow map overlap = true
# Indirectly controls where to split the shadow map(s). Values closer to 1.0 bring more detail closer to the camera (potentially excessively so), and values closer to 0.0 spread it more evenly across the whole viewing distance. 0.5 is recommended for most viewing distances by the original Parallel Split Shadow Maps paper, but this does not take into account use of a Light Space Perspective transformation, so other values may be preferable. If some of the terms used here go over your head, you might not want to change this, especially not without reading the associated papers first. When "allow shadow map overlap" is combined with a higher-than-default viewing distance, values closer to 1.0 will prevent nearby shadows losing a lot of quality.
split point uniform logarithmic ratio = 0.5
# Indirectly controls where to split the shadow map(s). Positive values move split points away from the camera and negative values move them towards the camera. Intended to be used in conjunction with changes to 'split point uniform logarithmic ratio' to counteract side effects, but may cause additional, more serious side effects. Read the Parallel Split Shadow Maps paper by F Zhang et al before changing.
split point bias = 0.0
# Enable the debug hud to see what the shadow map(s) contain.
enable debug hud = false
# Enable the debug overlay to see where each shadow map affects.
enable debug overlay = false
# Attempt to better use the shadow map by making them cover a smaller area. Especially helpful when looking downwards with a high viewing distance. The performance impact of this may be very large.
compute tight scene bounds = false
# How large to make the shadow map(s). Higher values increase GPU load, but can produce better-looking results. Power-of-two values may turn out to be faster on some GPU/driver combinations.
shadow map resolution = 1024
# Controls the minimum near/far ratio for the Light Space Perspective Shadow Map transformation. Helps prevent too much detail being brought towards the camera at the expense of detail further from the camera. Increasing this pushes detail further away.
minimum lispsm near far ratio = 0.25
# Used as the factor parameter for the polygon offset used for shadow map rendering. Higher values reduce shadow flicker, but risk increasing Peter Panning. See https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml for details.
polygon offset factor = 1.1
# Used as the units parameter for the polygon offset used for shadow map rendering. Higher values reduce shadow flicker, but risk increasing Peter Panning. See https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonOffset.xhtml for details.
polygon offset units = 4.0
# How far along the surface normal to project shadow coordinates. Higher values significantly reduce shadow flicker, usually with a lower increase of Peter Panning than the Polygon Offset settings. This value is in in-game units, so 1.0 is roughly 1.4 cm.
normal offset distance = 1.0
# Excludes theoretically unnecessary faces from shadow maps, slightly increasing performance. In practice, Peter Panning can be much less visible with these faces included, so if you have high polygon offset values, disabling this may help minimise the side effects.
use front face culling = true
# Allow actors to cast shadows. Potentially decreases performance.
actor shadows = false
# Allow the player to cast shadows. Potentially decreases performance.
player shadows = false
# Allow terrain to cast shadows. Potentially decreases performance.
terrain shadows = false
# Allow world objects to cast shadows. Potentially decreases performance.
object shadows = false
# Allow shadows indoors. Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.
enable indoor shadows = true

@ -18,6 +18,10 @@ set(SHADER_FILES
parallax.glsl
s360_fragment.glsl
s360_vertex.glsl
shadows_vertex.glsl
shadows_fragment.glsl
shadowcasting_vertex.glsl
shadowcasting_fragment.glsl
)
copy_all_resource_files(${CMAKE_CURRENT_SOURCE_DIR} ${OPENMW_SHADERS_ROOT} ${DDIRRELATIVE} "${SHADER_FILES}")

@ -1,38 +1,66 @@
#define MAX_LIGHTS 8
vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor)
uniform int colorMode;
void perLight(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal, vec4 diffuse, vec3 ambient)
{
vec3 lightDir;
float d;
#if @colorMode == 3
vec4 diffuse = gl_FrontMaterial.diffuse;
vec3 ambient = vertexColor.xyz;
#elif @colorMode == 2
vec4 diffuse = vertexColor;
vec3 ambient = vertexColor.xyz;
float lightDistance;
lightDir = gl_LightSource[lightIndex].position.xyz - (viewPos.xyz * gl_LightSource[lightIndex].position.w);
lightDistance = length(lightDir);
lightDir = normalize(lightDir);
float illumination = clamp(1.0 / (gl_LightSource[lightIndex].constantAttenuation + gl_LightSource[lightIndex].linearAttenuation * lightDistance + gl_LightSource[lightIndex].quadraticAttenuation * lightDistance * lightDistance), 0.0, 1.0);
ambientOut = ambient * gl_LightSource[lightIndex].ambient.xyz * illumination;
diffuseOut = diffuse.xyz * gl_LightSource[lightIndex].diffuse.xyz * max(dot(viewNormal.xyz, lightDir), 0.0) * illumination;
}
#if PER_PIXEL_LIGHTING
vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, float shadowing)
#else
vec4 diffuse = gl_FrontMaterial.diffuse;
vec3 ambient = gl_FrontMaterial.ambient.xyz;
vec4 doLighting(vec3 viewPos, vec3 viewNormal, vec4 vertexColor, out vec3 shadowDiffuse)
#endif
{
vec4 diffuse;
vec3 ambient;
if (colorMode == 3)
{
diffuse = gl_FrontMaterial.diffuse;
ambient = vertexColor.xyz;
}
else if (colorMode == 2)
{
diffuse = vertexColor;
ambient = vertexColor.xyz;
}
else
{
diffuse = gl_FrontMaterial.diffuse;
ambient = gl_FrontMaterial.ambient.xyz;
}
vec4 lightResult = vec4(0.0, 0.0, 0.0, diffuse.a);
vec3 diffuseLight, ambientLight;
perLight(ambientLight, diffuseLight, 0, viewPos, viewNormal, diffuse, ambient);
#if PER_PIXEL_LIGHTING
lightResult.xyz += diffuseLight * shadowing - diffuseLight; // This light gets added a second time in the loop to fix Mesa users' slowdown, so we need to negate its contribution here.
#else
shadowDiffuse = diffuseLight;
lightResult.xyz -= shadowDiffuse; // This light gets added a second time in the loop to fix Mesa users' slowdown, so we need to negate its contribution here.
#endif
for (int i=0; i<MAX_LIGHTS; ++i)
{
lightDir = gl_LightSource[i].position.xyz - (viewPos.xyz * gl_LightSource[i].position.w);
d = length(lightDir);
lightDir = normalize(lightDir);
lightResult.xyz += (ambient * gl_LightSource[i].ambient.xyz + diffuse.xyz * gl_LightSource[i].diffuse.xyz * max(dot(viewNormal.xyz, lightDir), 0.0)) * clamp(1.0 / (gl_LightSource[i].constantAttenuation + gl_LightSource[i].linearAttenuation * d + gl_LightSource[i].quadraticAttenuation * d * d), 0.0, 1.0);
perLight(ambientLight, diffuseLight, i, viewPos, viewNormal, diffuse, ambient);
lightResult.xyz += ambientLight + diffuseLight;
}
lightResult.xyz += gl_LightModel.ambient.xyz * ambient;
#if @colorMode == 1
lightResult.xyz += vertexColor.xyz;
#else
lightResult.xyz += gl_FrontMaterial.emission.xyz;
#endif
if (colorMode == 1)
lightResult.xyz += vertexColor.xyz;
else
lightResult.xyz += gl_FrontMaterial.emission.xyz;
#if @clamp
lightResult = clamp(lightResult, vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0));

@ -48,12 +48,14 @@ varying float depth;
#if !PER_PIXEL_LIGHTING
centroid varying vec4 lighting;
centroid varying vec3 shadowDiffuseLighting;
#else
centroid varying vec4 passColor;
#endif
varying vec3 passViewPos;
varying vec3 passNormal;
#include "shadows_fragment.glsl"
#include "lighting.glsl"
#include "parallax.glsl"
@ -112,10 +114,12 @@ void main()
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, decalTex.xyz, decalTex.a);
#endif
float shadowing = unshadowedLightRatio();
#if !PER_PIXEL_LIGHTING
gl_FragData[0] *= lighting;
gl_FragData[0] *= lighting + vec4(shadowDiffuseLighting * shadowing, 0);
#else
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor);
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing);
#endif
#if @emissiveMap
@ -147,8 +151,10 @@ void main()
vec3 matSpec = gl_FrontMaterial.specular.xyz;
#endif
gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec);
gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos.xyz), shininess, matSpec) * shadowing;
float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
applyShadowDebugOverlay();
}

@ -39,12 +39,15 @@ varying float depth;
#if !PER_PIXEL_LIGHTING
centroid varying vec4 lighting;
centroid varying vec3 shadowDiffuseLighting;
#else
centroid varying vec4 passColor;
#endif
varying vec3 passViewPos;
varying vec3 passNormal;
#include "shadows_vertex.glsl"
#include "lighting.glsl"
void main(void)
@ -93,10 +96,12 @@ void main(void)
#endif
#if !PER_PIXEL_LIGHTING
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color);
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
#else
passColor = gl_Color;
#endif
passViewPos = viewPos.xyz;
passNormal = gl_Normal.xyz;
setupShadowCoords(viewPos, viewNormal);
}

@ -0,0 +1,21 @@
#version 120
uniform sampler2D diffuseMap;
varying vec2 diffuseMapUV;
varying float alphaPassthrough;
uniform bool useDiffuseMapForShadowAlpha;
void main()
{
gl_FragData[0].rgb = vec3(1.0);
if (useDiffuseMapForShadowAlpha)
gl_FragData[0].a = texture2D(diffuseMap, diffuseMapUV).a * alphaPassthrough;
else
gl_FragData[0].a = alphaPassthrough;
// Prevent translucent things casting shadow (including the player using an invisibility effect)
if (gl_FragData[0].a <= 0.5)
discard;
}

@ -0,0 +1,27 @@
#version 120
varying vec2 diffuseMapUV;
varying float alphaPassthrough;
uniform int colorMode;
uniform bool useDiffuseMapForShadowAlpha;
void main(void)
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);
gl_ClipVertex = viewPos;
if (useDiffuseMapForShadowAlpha)
diffuseMapUV = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;
else
diffuseMapUV = vec2(0.0); // Avoid undefined behaviour if running on hardware predating the concept of dynamically uniform expressions
if (colorMode == 2)
alphaPassthrough = gl_Color.a;
else
// This is uniform, so if it's too low, we might be able to put the position/clip vertex outside the view frustum and skip the fragment shader and rasteriser
alphaPassthrough = gl_FrontMaterial.diffuse.a;
}

@ -0,0 +1,78 @@
#define SHADOWS @shadows_enabled
#if SHADOWS
@foreach shadow_texture_unit_index @shadow_texture_unit_list
uniform sampler2DShadow shadowTexture@shadow_texture_unit_index;
varying vec4 shadowSpaceCoords@shadow_texture_unit_index;
#if @perspectiveShadowMaps
varying vec4 shadowRegionCoords@shadow_texture_unit_index;
#endif
@endforeach
#endif // SHADOWS
float unshadowedLightRatio()
{
float shadowing = 1.0;
#if SHADOWS
#if @shadowMapsOverlap
bool doneShadows = false;
@foreach shadow_texture_unit_index @shadow_texture_unit_list
if (!doneShadows)
{
vec3 shadowXYZ = shadowSpaceCoords@shadow_texture_unit_index.xyz / shadowSpaceCoords@shadow_texture_unit_index.w;
#if @perspectiveShadowMaps
vec3 shadowRegionXYZ = shadowRegionCoords@shadow_texture_unit_index.xyz / shadowRegionCoords@shadow_texture_unit_index.w;
#endif
if (all(lessThan(shadowXYZ.xy, vec2(1.0, 1.0))) && all(greaterThan(shadowXYZ.xy, vec2(0.0, 0.0))))
{
shadowing = min(shadow2DProj(shadowTexture@shadow_texture_unit_index, shadowSpaceCoords@shadow_texture_unit_index).r, shadowing);
doneShadows = all(lessThan(shadowXYZ, vec3(0.95, 0.95, 1.0))) && all(greaterThan(shadowXYZ, vec3(0.05, 0.05, 0.0)));
#if @perspectiveShadowMaps
doneShadows = doneShadows && all(lessThan(shadowRegionXYZ, vec3(1.0, 1.0, 1.0))) && all(greaterThan(shadowRegionXYZ.xy, vec2(-1.0, -1.0)));
#endif
}
}
@endforeach
#else
@foreach shadow_texture_unit_index @shadow_texture_unit_list
shadowing = min(shadow2DProj(shadowTexture@shadow_texture_unit_index, shadowSpaceCoords@shadow_texture_unit_index).r, shadowing);
@endforeach
#endif
#endif // SHADOWS
return shadowing;
}
void applyShadowDebugOverlay()
{
#if SHADOWS && @useShadowDebugOverlay
bool doneOverlay = false;
float colourIndex = 0.0;
@foreach shadow_texture_unit_index @shadow_texture_unit_list
if (!doneOverlay)
{
vec3 shadowXYZ = shadowSpaceCoords@shadow_texture_unit_index.xyz / shadowSpaceCoords@shadow_texture_unit_index.w;
#if @perspectiveShadowMaps
vec3 shadowRegionXYZ = shadowRegionCoords@shadow_texture_unit_index.xyz / shadowRegionCoords@shadow_texture_unit_index.w;
#endif
if (all(lessThan(shadowXYZ.xy, vec2(1.0, 1.0))) && all(greaterThan(shadowXYZ.xy, vec2(0.0, 0.0))))
{
colourIndex = mod(@shadow_texture_unit_index.0, 3.0);
if (colourIndex < 1.0)
gl_FragData[0].x += 0.1;
else if (colourIndex < 2.0)
gl_FragData[0].y += 0.1;
else
gl_FragData[0].z += 0.1;
doneOverlay = all(lessThan(shadowXYZ, vec3(0.95, 0.95, 1.0))) && all(greaterThan(shadowXYZ, vec3(0.05, 0.05, 0.0)));
#if @perspectiveShadowMaps
doneOverlay = doneOverlay && all(lessThan(shadowRegionXYZ.xyz, vec3(1.0, 1.0, 1.0))) && all(greaterThan(shadowRegionXYZ.xy, vec2(-1.0, -1.0)));
#endif
}
}
@endforeach
#endif // SHADOWS
}

@ -0,0 +1,53 @@
#define SHADOWS @shadows_enabled
#if SHADOWS
@foreach shadow_texture_unit_index @shadow_texture_unit_list
uniform int shadowTextureUnit@shadow_texture_unit_index;
varying vec4 shadowSpaceCoords@shadow_texture_unit_index;
#if @perspectiveShadowMaps
uniform mat4 validRegionMatrix@shadow_texture_unit_index;
varying vec4 shadowRegionCoords@shadow_texture_unit_index;
#endif
@endforeach
// Enabling this may reduce peter panning. Probably unnecessary.
const bool onlyNormalOffsetUV = false;
#endif // SHADOWS
void setupShadowCoords(vec4 viewPos, vec3 viewNormal)
{
#if SHADOWS
// This matrix has the opposite handedness to the others used here, so multiplication must have the vector to the left. Alternatively it could be transposed after construction, but that's extra work for the GPU just to make the code look a tiny bit cleaner.
mat4 eyePlaneMat;
vec4 shadowOffset;
@foreach shadow_texture_unit_index @shadow_texture_unit_list
eyePlaneMat = mat4(gl_EyePlaneS[shadowTextureUnit@shadow_texture_unit_index], gl_EyePlaneT[shadowTextureUnit@shadow_texture_unit_index], gl_EyePlaneR[shadowTextureUnit@shadow_texture_unit_index], gl_EyePlaneQ[shadowTextureUnit@shadow_texture_unit_index]);
#if @perspectiveShadowMaps
shadowRegionCoords@shadow_texture_unit_index = validRegionMatrix@shadow_texture_unit_index * viewPos;
#endif
#if @disableNormalOffsetShadows
shadowSpaceCoords@shadow_texture_unit_index = viewPos * eyePlaneMat;
#else
shadowOffset = vec4(viewNormal * @shadowNormalOffset, 0.0);
if (onlyNormalOffsetUV)
{
shadowSpaceCoords@shadow_texture_unit_index = viewPos * eyePlaneMat;
vec4 lightSpaceXY = viewPos + shadowOffset;
lightSpaceXY = lightSpaceXY * eyePlaneMat;
shadowSpaceCoords@shadow_texture_unit_index.xy = lightSpaceXY.xy;
}
else
{
vec4 offsetViewPosition = viewPos + shadowOffset;
shadowSpaceCoords@shadow_texture_unit_index = offsetViewPosition * eyePlaneMat;
}
#endif
@endforeach
#endif // SHADOWS
}

@ -18,12 +18,14 @@ varying float depth;
#if !PER_PIXEL_LIGHTING
centroid varying vec4 lighting;
centroid varying vec3 shadowDiffuseLighting;
#else
centroid varying vec4 passColor;
#endif
varying vec3 passViewPos;
varying vec3 passNormal;
#include "shadows_fragment.glsl"
#include "lighting.glsl"
#include "parallax.glsl"
@ -64,10 +66,12 @@ void main()
gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a;
#endif
float shadowing = unshadowedLightRatio();
#if !PER_PIXEL_LIGHTING
gl_FragData[0] *= lighting;
gl_FragData[0] *= lighting + vec4(shadowDiffuseLighting * shadowing, 0);
#else
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor);
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor, shadowing);
#endif
#if @specularMap
@ -78,8 +82,10 @@ void main()
vec3 matSpec = gl_FrontMaterial.specular.xyz;
#endif
gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec);
gl_FragData[0].xyz += getSpecular(normalize(viewNormal), normalize(passViewPos), shininess, matSpec) * shadowing;
float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
applyShadowDebugOverlay();
}

@ -7,12 +7,15 @@ varying float depth;
#if !PER_PIXEL_LIGHTING
centroid varying vec4 lighting;
centroid varying vec3 shadowDiffuseLighting;
#else
centroid varying vec4 passColor;
#endif
varying vec3 passViewPos;
varying vec3 passNormal;
#include "shadows_vertex.glsl"
#include "lighting.glsl"
void main(void)
@ -22,10 +25,11 @@ void main(void)
vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);
gl_ClipVertex = viewPos;
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
#if !PER_PIXEL_LIGHTING
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color);
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color, shadowDiffuseLighting);
#else
passColor = gl_Color;
#endif
@ -33,4 +37,6 @@ void main(void)
passViewPos = viewPos.xyz;
uv = gl_MultiTexCoord0.xy;
setupShadowCoords(viewPos, viewNormal);
}

@ -147,6 +147,8 @@ uniform vec3 nodePosition;
uniform float rainIntensity;
#include "shadows_fragment.glsl"
float frustumDepth;
float linearizeDepth(float depth) // takes <0,1> non-linear depth value and returns <0,1> linearized value
@ -163,7 +165,7 @@ void main(void)
vec2 UV = worldPos.xy / (8192.0*5.0) * 3.0;
UV.y *= -1.0;
float shadow = 1.0;
float shadow = unshadowedLightRatio();
vec2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z;
screenCoords.y = (1.0-screenCoords.y);
@ -288,4 +290,6 @@ void main(void)
#else
gl_FragData[0].w = clamp(fresnel*6.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0);
#endif
applyShadowDebugOverlay();
}

@ -4,14 +4,16 @@ varying vec3 screenCoordsPassthrough;
varying vec4 position;
varying float depthPassthrough;
#include "shadows_vertex.glsl"
void main(void)
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
mat4 scalemat = mat4(0.5, 0.0, 0.0, 0.0,
0.0, -0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0);
0.0, -0.5, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.5, 0.5, 0.5, 1.0);
vec4 texcoordProj = ((scalemat) * ( gl_Position));
screenCoordsPassthrough = vec3(texcoordProj.x, texcoordProj.y, texcoordProj.w);
@ -19,4 +21,6 @@ void main(void)
position = gl_Vertex;
depthPassthrough = gl_Position.z;
setupShadowCoords(gl_ModelViewMatrix * gl_Vertex, normalize((gl_NormalMatrix * gl_Normal).xyz));
}

Loading…
Cancel
Save