mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-23 15:40:42 +00:00
commit
cb5a57e41b
@ -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:
|
||||
|
3180
components/sceneutil/mwshadowtechnique.cpp
Normal file
3180
components/sceneutil/mwshadowtechnique.cpp
Normal file
File diff suppressed because it is too large
Load Diff
287
components/sceneutil/mwshadowtechnique.hpp
Normal file
287
components/sceneutil/mwshadowtechnique.hpp
Normal file
@ -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);
|
||||
|
158
components/sceneutil/shadow.cpp
Normal file
158
components/sceneutil/shadow.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
43
components/sceneutil/shadow.hpp
Normal file
43
components/sceneutil/shadow.hpp
Normal file
@ -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 = '@';
|
||||
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);
|
||||
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);
|
||||
if (endPos == std::string::npos)
|
||||
{
|
||||
Log(Debug::Error) << "Unexpected EOF";
|
||||
@ -88,15 +226,47 @@ 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;
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
-------------
|
||||
|
||||
|
205
docs/source/reference/modding/settings/shadows.rst
Normal file
205
docs/source/reference/modding/settings/shadows.rst
Normal file
@ -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;
|
||||
float lightDistance;
|
||||
|
||||
#if @colorMode == 3
|
||||
vec4 diffuse = gl_FrontMaterial.diffuse;
|
||||
vec3 ambient = vertexColor.xyz;
|
||||
#elif @colorMode == 2
|
||||
vec4 diffuse = vertexColor;
|
||||
vec3 ambient = vertexColor.xyz;
|
||||
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
|
||||
if (colorMode == 1)
|
||||
lightResult.xyz += vertexColor.xyz;
|
||||
#else
|
||||
else
|
||||
lightResult.xyz += gl_FrontMaterial.emission.xyz;
|
||||
#endif
|
||||
|
||||
#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);
|
||||
}
|
||||
|
21
files/shaders/shadowcasting_fragment.glsl
Normal file
21
files/shaders/shadowcasting_fragment.glsl
Normal file
@ -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;
|
||||
}
|
27
files/shaders/shadowcasting_vertex.glsl
Normal file
27
files/shaders/shadowcasting_vertex.glsl
Normal file
@ -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;
|
||||
}
|
78
files/shaders/shadows_fragment.glsl
Normal file
78
files/shaders/shadows_fragment.glsl
Normal file
@ -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
|
||||
}
|
53
files/shaders/shadows_vertex.glsl
Normal file
53
files/shaders/shadows_vertex.glsl
Normal file
@ -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)
|
||||
@ -23,9 +26,10 @@ void main(void)
|
||||
vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);
|
||||
gl_ClipVertex = viewPos;
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
|
||||
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color);
|
||||
|
||||
#if !PER_PIXEL_LIGHTING
|
||||
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,6 +4,8 @@ varying vec3 screenCoordsPassthrough;
|
||||
varying vec4 position;
|
||||
varying float depthPassthrough;
|
||||
|
||||
#include "shadows_vertex.glsl"
|
||||
|
||||
void main(void)
|
||||
{
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
@ -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…
x
Reference in New Issue
Block a user