1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-19 22:53:53 +00:00
openmw/components/sceneutil/shadow.cpp
AnyOldName3 0edc8fc77d Don't use FFP-friendly texture image units for shadow maps
This more-or-less gets rid of the shadow system's only depencency on FFP
stuff. All that remains is it using OSG cameras, which OSG provides a
uniform-based implementation of, too, which we can trivially migrate to.

This should mean we're not eating any of the ~8 FPP-friendly texture
units, which is good as Morrowind models can use all of those on their
(although they very rarely do), and instead use some of the ~160
shader-only texture image units. This just requires not calling
glEnable(GL_TEXTURE_2D), accomplished by changing
setTextureAttributeAndModes to setTextureAttribute.

Also changes from using glTexGen and its eye plane matrices to pass the
shadow space matrix for each light to explicit uniforms. Thankfully, the
maths was a simple combination of the valid region matrix and eye plane
matrix maths.

As of this commit, I believe this kills shadows in one eye for stereo
rendering.
2023-02-05 00:40:33 +00:00

211 lines
8.8 KiB
C++

#include "shadow.hpp"
#include <osgShadow/ShadowSettings>
#include <osgShadow/ShadowedScene>
#include <components/misc/strings/algorithm.hpp>
#include <components/settings/settings.hpp>
#include <components/stereo/stereomanager.hpp>
#include "mwshadowtechnique.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);
const int numberOfShadowMapsPerLight
= std::clamp(Settings::Manager::getInt("number of shadow maps", "Shadows"), 1, 8);
mShadowSettings->setNumShadowMapsPerLight(numberOfShadowMapsPerLight);
mShadowSettings->setBaseShadowTextureUnit(osg::GLExtensions::Get(0, false)->glMaxTextureUnits - numberOfShadowMapsPerLight);
const float maximumShadowMapDistance = Settings::Manager::getFloat("maximum shadow map distance", "Shadows");
if (maximumShadowMapDistance > 0)
{
const float shadowFadeStart
= std::clamp(Settings::Manager::getFloat("shadow fade start", "Shadows"), 0.f, 1.f);
mShadowSettings->setMaximumShadowMapDistance(maximumShadowMapDistance);
mShadowTechnique->setShadowFadeStart(maximumShadowMapDistance * shadowFadeStart);
}
mShadowSettings->setMinimumShadowMapNearFarRatio(
Settings::Manager::getFloat("minimum lispsm near far ratio", "Shadows"));
const std::string& computeSceneBounds = Settings::Manager::getString("compute scene bounds", "Shadows");
if (Misc::StringUtils::ciEqual(computeSceneBounds, "primitives"))
mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES);
else if (Misc::StringUtils::ciEqual(computeSceneBounds, "bounds"))
mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
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)
{
if (!Settings::Manager::getBool("enable shadows", "Shadows"))
return;
const int numberOfShadowMapsPerLight
= std::clamp(Settings::Manager::getInt("number of shadow maps", "Shadows"), 1, 8);
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, unsigned int worldMask,
Shader::ShaderManager& shaderManager)
: mShadowedScene(new osgShadow::ShadowedScene)
, mShadowTechnique(new MWShadowTechnique)
, mOutdoorShadowCastingMask(outdoorShadowCastingMask)
, mIndoorShadowCastingMask(indoorShadowCastingMask)
{
mShadowedScene->setShadowTechnique(mShadowTechnique);
Stereo::Manager::instance().setShadowTechnique(mShadowTechnique);
mShadowedScene->addChild(sceneRoot);
rootNode->addChild(mShadowedScene);
mShadowedScene->setNodeMask(sceneRoot->getNodeMask());
mShadowSettings = mShadowedScene->getShadowSettings();
setupShadowSettings();
mShadowTechnique->setupCastingShader(shaderManager);
mShadowTechnique->setWorldMask(worldMask);
enableOutdoorMode();
}
ShadowManager::~ShadowManager()
{
Stereo::Manager::instance().setShadowTechnique(nullptr);
}
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"));
definesWithShadows["limitShadowMapDistance"]
= Settings::Manager::getFloat("maximum shadow map distance", "Shadows") > 0 ? "1" : "0";
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";
definesWithoutShadows["limitShadowMapDistance"] = "0";
return definesWithoutShadows;
}
void ShadowManager::enableIndoorMode()
{
if (Settings::Manager::getBool("enable indoor shadows", "Shadows"))
mShadowSettings->setCastsShadowTraversalMask(mIndoorShadowCastingMask);
else
mShadowTechnique->disableShadows(true);
}
void ShadowManager::enableOutdoorMode()
{
if (mEnableShadows)
mShadowTechnique->enableShadows();
mShadowSettings->setCastsShadowTraversalMask(mOutdoorShadowCastingMask);
}
}