More formatting, OpenCS cells are unbroken

pull/3065/head
glassmancody.info 4 years ago
parent 142c6d2993
commit 690995988b

@ -83,7 +83,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
defines["clamp"] = "1"; // Clamp lighting
defines["preLightEnv"] = "0"; // Apply environment maps after lighting like Morrowind
defines["radialFog"] = "0";
defines["ffpLighting"] = "0";
defines["ffpLighting"] = "1";
for (const auto& define : shadowDefines)
defines[define.first] = define.second;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);

@ -199,8 +199,8 @@ namespace MWRender
, mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f)
{
auto lightingModelString = Settings::Manager::getString("lighting method", "Shaders");
bool usingFFPLighting = lightingModelString == "legacy" && SceneUtil::LightManager::isValidLightingModelString(lightingModelString);
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
bool usingFFPLighting = lightingMethod == SceneUtil::LightingMethod::FFP;
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
@ -217,9 +217,9 @@ namespace MWRender
resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders"));
resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders"));
resourceSystem->getSceneManager()->setConvertAlphaTestToAlphaToCoverage(Settings::Manager::getBool("antialias alpha test", "Shaders") && Settings::Manager::getInt("antialiasing", "Video") > 1);
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(!forceShaders || usingFFPLighting);
// Let LightManager choose which backend to use based, mostly depends on support for UBOs
// Let LightManager choose which backend to use based on our hint, mostly depends on support for UBOs
resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod());
@ -262,7 +262,7 @@ namespace MWRender
float groundcoverDistance = (Constants::CellSizeInUnits * std::max(1, Settings::Manager::getInt("distance", "Groundcover")) - 1024) * 0.93;
globalDefines["groundcoverFadeStart"] = std::to_string(groundcoverDistance * 0.9f);
globalDefines["groundcoverFadeEnd"] = std::to_string(groundcoverDistance);
// It is unnecessary to stop/start the viewer as no frames are being rendered yet.
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(globalDefines);

@ -14,8 +14,6 @@
#include <components/debug/debuglog.hpp>
#include "apps/openmw/mwrender/vismask.hpp"
namespace
{
/* similar to the boost::hash_combine */
@ -28,7 +26,8 @@ namespace
bool sortLights(const SceneUtil::LightManager::LightSourceViewBound* left, const SceneUtil::LightManager::LightSourceViewBound* right)
{
return left->mViewBound.center().length2() - left->mViewBound.radius2()*81 < right->mViewBound.center().length2() - right->mViewBound.radius2()*81;
static auto constexpr illuminationBias = 81.f;
return left->mViewBound.center().length2() - left->mViewBound.radius2()*illuminationBias < right->mViewBound.center().length2() - right->mViewBound.radius2()*illuminationBias;
}
float getLightRadius(const osg::Light* light)
@ -99,8 +98,15 @@ namespace SceneUtil
return (*mData)[3*index+1];
}
auto& getData() { return mData; }
void dirty() { mData->dirty(); }
auto& getData()
{
return mData;
}
void dirty()
{
mData->dirty();
}
static constexpr int queryBlockSize(int sz)
{
@ -135,6 +141,7 @@ namespace SceneUtil
{
switch (method)
{
case LightingMethod::Undefined:
case LightingMethod::FFP:
{
break;
@ -232,7 +239,7 @@ namespace SceneUtil
bool getModeUsage(ModeUsage & usage) const override
{
for (size_t i=0; i<mLights.size(); ++i)
for (size_t i = 0; i < mLights.size(); ++i)
usage.usesMode(GL_LIGHT0 + mIndex + i);
return true;
}
@ -254,7 +261,7 @@ namespace SceneUtil
LightStateCache* cache = getLightStateCache(state.getContextID());
for (size_t i=0; i<mLights.size(); ++i)
for (size_t i = 0; i < mLights.size(); ++i)
{
osg::Light* current = cache->lastAppliedLight[i+mIndex];
if (current != mLights[i].get())
@ -269,29 +276,28 @@ namespace SceneUtil
void applyLight(GLenum lightNum, const osg::Light* light) const
{
glLightfv( lightNum, GL_AMBIENT, light->getAmbient().ptr() );
glLightfv( lightNum, GL_DIFFUSE, light->getDiffuse().ptr() );
glLightfv( lightNum, GL_SPECULAR, light->getSpecular().ptr() );
glLightfv( lightNum, GL_POSITION, light->getPosition().ptr() );
glLightfv(lightNum, GL_AMBIENT, light->getAmbient().ptr());
glLightfv(lightNum, GL_DIFFUSE, light->getDiffuse().ptr());
glLightfv(lightNum, GL_SPECULAR, light->getSpecular().ptr());
glLightfv(lightNum, GL_POSITION, light->getPosition().ptr());
// TODO: enable this once spot lights are supported
// need to transform SPOT_DIRECTION by the world matrix?
//glLightfv( lightNum, GL_SPOT_DIRECTION, light->getDirection().ptr() );
//glLightf ( lightNum, GL_SPOT_EXPONENT, light->getSpotExponent() );
//glLightf ( lightNum, GL_SPOT_CUTOFF, light->getSpotCutoff() );
glLightf ( lightNum, GL_CONSTANT_ATTENUATION, light->getConstantAttenuation() );
glLightf ( lightNum, GL_LINEAR_ATTENUATION, light->getLinearAttenuation() );
glLightf ( lightNum, GL_QUADRATIC_ATTENUATION, light->getQuadraticAttenuation() );
//glLightfv(lightNum, GL_SPOT_DIRECTION, light->getDirection().ptr());
//glLightf(lightNum, GL_SPOT_EXPONENT, light->getSpotExponent());
//glLightf(lightNum, GL_SPOT_CUTOFF, light->getSpotCutoff());
glLightf(lightNum, GL_CONSTANT_ATTENUATION, light->getConstantAttenuation());
glLightf(lightNum, GL_LINEAR_ATTENUATION, light->getLinearAttenuation());
glLightf(lightNum, GL_QUADRATIC_ATTENUATION, light->getQuadraticAttenuation());
}
private:
size_t mIndex;
std::vector<osg::ref_ptr<osg::Light>> mLights;
};
LightManager* findLightManager(const osg::NodePath& path)
{
for (size_t i=0;i<path.size(); ++i)
for (size_t i = 0; i < path.size(); ++i)
{
if (LightManager* lightManager = dynamic_cast<LightManager*>(path[i]))
return lightManager;
@ -406,8 +412,8 @@ namespace SceneUtil
return stateset;
}
// Cached statesets must be re-validated in case the light indicies change. There is no actual link between
// a lights ID and the buffer index it will eventually be assigned (or reassigned) to.
// Cached statesets must be revalidated in case the light indices change. There is no actual link between
// a light's ID and the buffer index it will eventually be assigned (or reassigned) to.
void update(osg::StateSet* stateset, const LightManager::LightList& lightList, size_t frameNum) override
{
int newCount = 0;
@ -587,104 +593,88 @@ namespace SceneUtil
LightManager* mLightManager;
};
const std::unordered_map<std::string, LightingMethod> LightManager::mLightingMethodSettingMap = {
{"legacy", LightingMethod::FFP}
,{"shaders compatibility", LightingMethod::PerObjectUniform}
,{"shaders", LightingMethod::SingleUBO}
};
bool LightManager::isValidLightingModelString(const std::string& value)
{
static const std::unordered_set<std::string> validLightingModels = {"legacy", "default", "experimental"};
return validLightingModels.count(value) != 0;
return LightManager::mLightingMethodSettingMap.find(value) != LightManager::mLightingMethodSettingMap.end();
}
LightingMethod LightManager::getLightingMethodFromString(const std::string& value)
{
auto it = LightManager::mLightingMethodSettingMap.find(value);
if (it != LightManager::mLightingMethodSettingMap.end())
return it->second;
else
return LightingMethod::Undefined;
}
LightManager::LightManager(bool ffp)
: mStartLight(0)
, mLightingMask(~0u)
, mSun(nullptr)
, mPointLightRadiusMultiplier(std::clamp(Settings::Manager::getFloat("light bounds multiplier", "Shaders"), 0.f, 10.f))
, mPointLightRadiusMultiplier(1.f)
, mPointLightFadeEnd(0.f)
, mPointLightFadeStart(0.f)
{
mPointLightFadeEnd = std::max(0.f, Settings::Manager::getFloat("maximum light distance", "Shaders"));
if (mPointLightFadeEnd > 0)
if (ffp)
{
mPointLightFadeStart = std::clamp(Settings::Manager::getFloat("light fade start", "Shaders"), 0.f, 1.f);
mPointLightFadeStart = mPointLightFadeEnd * mPointLightFadeStart;
setLightingMethod(LightingMethod::FFP);
initFFP(LightManager::mFFPMaxLights);
return;
}
auto lightingModelString = Settings::Manager::getString("lighting method", "Shaders");
bool validLightingModel = isValidLightingModelString(lightingModelString);
if (!validLightingModel)
Log(Debug::Error) << "Invalid option for 'lighting model': got '" << lightingModelString
<< "', expected legacy, default, or experimental.";
if (ffp || !validLightingModel)
std::string lightingMethodString = Settings::Manager::getString("lighting method", "Shaders");
auto lightingMethod = LightManager::getLightingMethodFromString(lightingMethodString);
if (lightingMethod == LightingMethod::Undefined)
{
setMaxLights(LightManager::mFFPMaxLights);
setLightingMethod(LightingMethod::FFP);
Log(Debug::Error) << "Invalid option for 'lighting method': got '" << lightingMethodString
<< "', expected legacy, shaders compatible, or shaders. Falling back to 'shaders compatible'.";
setLightingMethod(LightingMethod::PerObjectUniform);
}
else
{
setLightingMethod(lightingMethod);
}
for (int i=0; i<getMaxLights(); ++i)
mDummies.push_back(new FFPLightStateAttribute(i, std::vector<osg::ref_ptr<osg::Light> >()));
mPointLightRadiusMultiplier = std::clamp(Settings::Manager::getFloat("light bounds multiplier", "Shaders"), 0.f, 10.f);
setUpdateCallback(new LightManagerUpdateCallback);
return;
mPointLightFadeEnd = std::max(0.f, Settings::Manager::getFloat("maximum light distance", "Shaders"));
if (mPointLightFadeEnd > 0)
{
mPointLightFadeStart = std::clamp(Settings::Manager::getFloat("light fade start", "Shaders"), 0.f, 1.f);
mPointLightFadeStart = mPointLightFadeEnd * mPointLightFadeStart;
}
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
bool supportsUBO = exts && exts->isUniformBufferObjectSupported;
bool supportsGPU4 = exts && exts->isGpuShader4Supported;
if (!supportsUBO)
Log(Debug::Info) << "GL_ARB_uniform_buffer_object not supported: using fallback uniforms";
else if (!supportsGPU4)
Log(Debug::Info) << "GL_EXT_gpu_shader4 not supported: using fallback uniforms";
if (getLightingMethod() == LightingMethod::SingleUBO)
{
if (!supportsUBO)
Log(Debug::Info) << "GL_ARB_uniform_buffer_object not supported: using fallback uniforms";
else if (!supportsGPU4)
Log(Debug::Info) << "GL_EXT_gpu_shader4 not supported: using fallback uniforms";
}
int targetLights = Settings::Manager::getInt("max lights", "Shaders");
auto* stateset = getOrCreateStateSet();
if (!supportsUBO || !supportsGPU4 || lightingModelString == "default")
if (!supportsUBO || !supportsGPU4 || getLightingMethod() == LightingMethod::PerObjectUniform)
{
setLightingMethod(LightingMethod::PerObjectUniform);
setMaxLights(std::max(2, targetLights));
mLightUniforms.resize(getMaxLights()+1);
for (size_t i = 0; i < mLightUniforms.size(); ++i)
{
osg::ref_ptr<osg::Uniform> udiffuse = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].diffuse").c_str());
osg::ref_ptr<osg::Uniform> uspecular = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].specular").c_str());
osg::ref_ptr<osg::Uniform> uambient = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].ambient").c_str());
osg::ref_ptr<osg::Uniform> uposition = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].position").c_str());
osg::ref_ptr<osg::Uniform> uattenuation = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].attenuation").c_str());
mLightUniforms[i].emplace(UniformKey::Diffuse, udiffuse);
mLightUniforms[i].emplace(UniformKey::Ambient, uambient);
mLightUniforms[i].emplace(UniformKey::Specular, uspecular);
mLightUniforms[i].emplace(UniformKey::Position, uposition);
mLightUniforms[i].emplace(UniformKey::Attenuation, uattenuation);
stateset->addUniform(udiffuse);
stateset->addUniform(uambient);
stateset->addUniform(uposition);
stateset->addUniform(uattenuation);
// specular isn't used besides sun, complete waste to upload it
if (i == 0)
stateset->addUniform(uspecular);
}
initPerObjectUniform(targetLights);
}
else
{
setLightingMethod(LightingMethod::SingleUBO);
setMaxLights(std::clamp(targetLights, 2, getMaxLightsInScene() / 2));
for (int i = 0; i < 2; ++i)
{
mLightBuffers[i] = new LightBuffer(getMaxLightsInScene());
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
ubo->setUsage(GL_STREAM_DRAW);
mLightBuffers[i]->getData()->setBufferObject(ubo);
}
stateset->setAttribute(new LightManagerStateAttribute(this), osg::StateAttribute::ON);
initSingleUBO(targetLights);
}
stateset->addUniform(new osg::Uniform("PointLightCount", 0));
getOrCreateStateSet()->addUniform(new osg::Uniform("PointLightCount", 0));
setUpdateCallback(new LightManagerUpdateCallback);
addCullCallback(new LightManagerCullCallback(this));
@ -726,7 +716,7 @@ namespace SceneUtil
int LightManager::getMaxLightsInScene() const
{
static constexpr int max = 16384 / LightBuffer::queryBlockSize(1);
return max;
return max;
}
Shader::ShaderManager::DefineMap LightManager::getLightDefines() const
@ -746,6 +736,66 @@ namespace SceneUtil
return defines;
}
void LightManager::initFFP(int targetLights)
{
setMaxLights(targetLights);
for (int i = 0; i < getMaxLights(); ++i)
mDummies.push_back(new FFPLightStateAttribute(i, std::vector<osg::ref_ptr<osg::Light>>()));
setUpdateCallback(new LightManagerUpdateCallback);
}
void LightManager::initPerObjectUniform(int targetLights)
{
auto* stateset = getOrCreateStateSet();
setMaxLights(std::max(2, targetLights));
mLightUniforms.resize(getMaxLights()+1);
for (size_t i = 0; i < mLightUniforms.size(); ++i)
{
osg::ref_ptr<osg::Uniform> udiffuse = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].diffuse").c_str());
osg::ref_ptr<osg::Uniform> uspecular = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].specular").c_str());
osg::ref_ptr<osg::Uniform> uambient = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].ambient").c_str());
osg::ref_ptr<osg::Uniform> uposition = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].position").c_str());
osg::ref_ptr<osg::Uniform> uattenuation = new osg::Uniform(osg::Uniform::FLOAT_VEC4, ("LightBuffer[" + std::to_string(i) + "].attenuation").c_str());
mLightUniforms[i].emplace(UniformKey::Diffuse, udiffuse);
mLightUniforms[i].emplace(UniformKey::Ambient, uambient);
mLightUniforms[i].emplace(UniformKey::Specular, uspecular);
mLightUniforms[i].emplace(UniformKey::Position, uposition);
mLightUniforms[i].emplace(UniformKey::Attenuation, uattenuation);
stateset->addUniform(udiffuse);
stateset->addUniform(uambient);
stateset->addUniform(uposition);
stateset->addUniform(uattenuation);
// specular isn't used besides sun, complete waste to upload it
if (i == 0)
stateset->addUniform(uspecular);
}
}
void LightManager::initSingleUBO(int targetLights)
{
setMaxLights(std::clamp(targetLights, 2, getMaxLightsInScene() / 2));
for (int i = 0; i < 2; ++i)
{
mLightBuffers[i] = new LightBuffer(getMaxLightsInScene());
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
ubo->setUsage(GL_STREAM_DRAW);
mLightBuffers[i]->getData()->setBufferObject(ubo);
}
getOrCreateStateSet()->setAttribute(new LightManagerStateAttribute(this), osg::StateAttribute::ON);
}
void LightManager::setLightingMethod(LightingMethod method)
{
mLightingMethod = method;
@ -760,6 +810,9 @@ namespace SceneUtil
case LightingMethod::PerObjectUniform:
mStateSetGenerator = std::make_unique<StateSetGeneratorPerObjectUniform>();
break;
case LightingMethod::Undefined:
mStateSetGenerator = nullptr;
break;
}
mStateSetGenerator->mLightManager = this;
}
@ -783,7 +836,7 @@ namespace SceneUtil
// Set default light state to zero
// This is necessary because shaders don't respect glDisable(GL_LIGHTX) so in addition to disabling
// we'll have to set a light state that has no visible effect
for (int i=start; i<getMaxLights(); ++i)
for (int i = start; i < getMaxLights(); ++i)
{
osg::ref_ptr<DisableLight> defaultLight (new DisableLight(i));
getOrCreateStateSet()->setAttributeAndModes(defaultLight, osg::StateAttribute::OFF);
@ -802,7 +855,7 @@ namespace SceneUtil
mLightsInViewSpace.clear();
// Do an occasional cleanup for orphaned lights.
for (int i=0; i<2; ++i)
for (int i = 0; i < 2; ++i)
{
if (mStateSetCache[i].size() > 5000)
mStateSetCache[i].clear();
@ -836,7 +889,7 @@ namespace SceneUtil
{
// possible optimization: return a StateSet containing all requested lights plus some extra lights (if a suitable one exists)
size_t hash = 0;
for (size_t i=0; i<lightList.size();++i)
for (size_t i = 0; i < lightList.size(); ++i)
{
auto id = lightList[i]->mLightSource->getId();
hash_combine(hash, id);
@ -860,13 +913,10 @@ namespace SceneUtil
mStateSetGenerator->update(found->second, lightList, frameNum);
return found->second;
}
else
{
auto stateset = mStateSetGenerator->generate(lightList, frameNum);
stateSetCache.emplace(hash, stateset);
return stateset;
}
return new osg::StateSet;
auto stateset = mStateSetGenerator->generate(lightList, frameNum);
stateSetCache.emplace(hash, stateset);
return stateset;
}
const std::vector<LightManager::LightSourceViewBound>& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix, size_t frameNum)
@ -946,7 +996,7 @@ namespace SceneUtil
{
mId = sLightId++;
for (int i=0; i<2; ++i)
for (int i = 0; i < 2; ++i)
mLight[i] = new osg::Light(*copy.mLight[i].get(), copyop);
}
@ -981,7 +1031,6 @@ namespace SceneUtil
// makes sure we don't update it more than once per frame when rendering with multiple cameras
if (mLastFrameNumber != cv->getTraversalNumber())
{
mLastFrameNumber = cv->getTraversalNumber();
// Don't use Camera::getViewMatrix, that one might be relative to another camera!
@ -994,7 +1043,7 @@ namespace SceneUtil
osg::Transform* transform = node->asTransform();
if (transform)
{
for (size_t i=0; i<transform->getNumChildren(); ++i)
for (size_t i = 0; i < transform->getNumChildren(); ++i)
nodeBound.expandBy(transform->getChild(i)->getBound());
}
else
@ -1003,7 +1052,7 @@ namespace SceneUtil
transformBoundingSphere(mat, nodeBound);
mLightList.clear();
for (size_t i=0; i<lights.size(); ++i)
for (size_t i = 0; i < lights.size(); ++i)
{
const LightManager::LightSourceViewBound& l = lights[i];
@ -1014,7 +1063,6 @@ namespace SceneUtil
mLightList.push_back(&l);
}
}
if (!mLightList.empty())
{
size_t maxLights = mLightManager->getMaxLights() - mLightManager->getStartLight();
@ -1025,7 +1073,7 @@ namespace SceneUtil
{
// remove lights culled by this camera
LightManager::LightList lightList = mLightList;
for (auto it = lightList.begin(); it != lightList.end() && lightList.size() > maxLights; )
for (auto it = lightList.begin(); it != lightList.end() && lightList.size() > maxLights;)
{
osg::CullStack::CullingStack& stack = cv->getModelViewCullingStack();
@ -1053,6 +1101,7 @@ namespace SceneUtil
else
stateset = mLightManager->getLightListStateSet(mLightList, cv->getTraversalNumber(), cv->getCurrentRenderStage()->getInitialViewMatrix());
cv->pushStateSet(stateset);
return true;
}

@ -2,12 +2,12 @@
#define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H
#include <set>
#include <cassert>
#include <unordered_map>
#include <unordered_set>
#include <unordered_map>
#include <memory>
#include <osg/Light>
#include <osg/Group>
#include <osg/NodeVisitor>
#include <osg/observer_ptr>
@ -18,6 +18,7 @@ namespace osgUtil
{
class CullVisitor;
}
namespace SceneUtil
{
class LightBuffer;
@ -27,7 +28,8 @@ namespace SceneUtil
{
FFP,
SingleUBO,
PerObjectUniform
PerObjectUniform,
Undefined
};
void configureStateSetSunOverride(LightingMethod method, const osg::Light* light, osg::StateSet* stateset, int mode = osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
@ -98,8 +100,8 @@ namespace SceneUtil
class LightManager : public osg::Group
{
public:
static bool isValidLightingModelString(const std::string& value);
static LightingMethod getLightingMethodFromString(const std::string& value);
enum class UniformKey
{
@ -136,7 +138,7 @@ namespace SceneUtil
/// By default, it's ~0u i.e. always on.
/// If you have some views that do not require lighting, then set the Camera's cull mask to not include
/// the lightingMask for a much faster cull and rendering.
void setLightingMask (size_t mask);
void setLightingMask(size_t mask);
size_t getLightingMask() const;
/// Set the first light index that should be used by this manager, typically the number of directional lights in the scene.
@ -167,7 +169,7 @@ namespace SceneUtil
auto& getDummies() { return mDummies; }
auto& getLightIndexMap(size_t frameNum) { return mLightIndexMaps[frameNum%2]; }
auto& getLightBuffer(size_t frameNum) { return mLightBuffers[frameNum%2]; }
auto& getLightUniform(int index, UniformKey key) { return mLightUniforms[index][key]; }
@ -175,10 +177,13 @@ namespace SceneUtil
std::map<std::string, std::string> getLightDefines() const;
private:
friend class LightManagerStateAttribute;
friend class LightManagerCullCallback;
void initFFP(int targetLights);
void initPerObjectUniform(int targetLights);
void initSingleUBO(int targetLights);
void setLightingMethod(LightingMethod method);
void setMaxLights(int value);
@ -188,7 +193,7 @@ namespace SceneUtil
using LightSourceViewBoundCollection = std::vector<LightSourceViewBound>;
std::map<osg::observer_ptr<osg::Camera>, LightSourceViewBoundCollection> mLightsInViewSpace;
// < Light list hash , StateSet >
using LightStateSetMap = std::map<size_t, osg::ref_ptr<osg::StateSet>>;
LightStateSetMap mStateSetCache[2];
@ -221,6 +226,8 @@ namespace SceneUtil
int mMaxLights;
static constexpr auto mFFPMaxLights = 8;
static const std::unordered_map<std::string, LightingMethod> mLightingMethodSettingMap;
};
/// To receive lighting, objects must be decorated by a LightListCallback. Light list callbacks must be added via
@ -259,7 +266,8 @@ namespace SceneUtil
size_t mLastFrameNumber;
LightManager::LightList mLightList;
std::set<SceneUtil::LightSource*> mIgnoredLightSources;
};
};
}
#endif

@ -10,7 +10,6 @@
#include <boost/filesystem/fstream.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/debug/debuglog.hpp>
#include <components/misc/stringops.hpp>

@ -40,7 +40,7 @@ Only affects objects that render with shaders (see 'force shaders' option).
Always affects terrain.
Leaving this option at its default makes the lighting compatible with Morrowind's fixed-function method,
but the lighting may appear dull and there might be colour shifts.
but the lighting may appear dull and there might be colour shifts.
Setting this option to 'false' results in more dynamic lighting.
auto use object normal maps
@ -152,32 +152,51 @@ lighting method
---------------
:Type: string
:Range: legacy|default|experimental
:Range: legacy|shaders compatibility|shaders
:Default: default
Sets the internal handling of light sources.
Sets the internal handling of light sources.
'legacy' is restricted to a maximum of 8 lights per object and guarantees fixed function pipeline compatible lighting.
'legacy' is restricted to 8 lights per object and emulates fixed function
pipeline compatible lighting.
'default' removes the light limit via :ref:`max lights` and follows a new attenuation formula which can drastically reduce light popping and seams.
This mode also enables vertex lighting on groundcover, which is otherwise completely disabled with 'legacy'.
It is recommended to use this mode with older hardware, as the technique ensures a range of compatibility equal to that of 'legacy'.
'shaders compatibility' removes the light limit via :ref:`max lights` and
follows a modifed attenuation formula which can drastically reduce light popping
and seams. This mode also enables lighting on groundcover and a configurable
light fade. It is recommended to use this with older hardware and a light limit
closer to 8. Because of its wide range of compatibility it is set as the
default.
'experimental' carries all of the benefits that 'legacy' has, but uses a modern approach that allows for a higher 'max lights' count with little to no performance penalties on modern hardware.
'shaders' carries all of the benefits that 'shaders compatibility' does, but
uses a modern approach that allows for a higher :ref:`max lights` count with
little to no performance penalties on modern hardware. It is recommended to use
this mode when supported and where the GPU is not a bottleneck. On some weaker
devices, using this mode along with :ref:`force per pixel lighting` can carry
performance penalties.
Note that when enabled, groundcover lighting is forced to be vertex lighting,
unless normal maps are provided. This is due to some groundcover mods using the
Z-Up Normals technique to avoid some common issues with shading. As a
consequence, per pixel lighting would give undesirable results.
This setting has no effect if :ref:`force shaders` is 'false'.
light bounds multiplier
-----------------------
:Type: float
:Range: 0.0-10.0
:Default: 2.0
:Default: 1.75
Controls the bounding sphere radius of point lights, which is used to determine if an object should receive lighting from a particular light source.
Note, this has no direct effect on the overall illumination of lights.
Larger multipliers will allow for smoother transitions of light sources, but may require an increase in :ref:`max lights` and thus carries a performance penalty.
This especially helps with abrupt light popping with handheld light sources such as torches and lanterns.
Controls the bounding sphere radius of point lights, which is used to determine
if an object should receive lighting from a particular light source. Note, this
has no direct effect on the overall illumination of lights. Larger multipliers
will allow for smoother transitions of light sources, but may require an
increase in :ref:`max lights` and thus carries a performance penalty. This
especially helps with abrupt light popping with handheld light sources such as
torches and lanterns.
It is recommended to keep this at 1.0 if :ref:`lighting method` is set to 'legacy', as the number of lights is fixed in that mode.
This setting has no effect if :ref:`lighting method` is 'legacy'.
maximum light distance
----------------------
@ -186,9 +205,11 @@ maximum light distance
:Range: The whole range of 32-bit floating point
:Default: 8192
The maximum distance from the camera that lights will be illuminated, applies to both interiors and exteriors.
A lower distance will improve performance.
Set this to a non-positive value to disable fading.
The maximum distance from the camera that lights will be illuminated, applies to
both interiors and exteriors. A lower distance will improve performance. Set
this to a non-positive value to disable fading.
This setting has no effect if :ref:`lighting method` is 'legacy'.
light fade start
----------------
@ -199,18 +220,23 @@ light fade start
The fraction of the maximum distance at which lights will begin to fade away.
Tweaking it will make the transition proportionally more or less smooth.
This setting has no effect if the maximum light distance is non-positive.
This setting has no effect if the :ref:`maximum light distance` is non-positive
or :ref:`lighting method` is 'legacy'.
max lights
----------
:Type: integer
:Range: >=2
:Default: 16
:Default: 8
Sets the maximum number of lights that each object can receive lighting from.
Has no effect if :ref:`force shaders` option is off or :ref:`lighting method` is 'legacy'. In this case the maximum number of lights is fixed at 8.
Increasing this too much can cause significant performance loss, especially if :ref:`lighting method` is not set to 'experimental' or :ref:`force per pixel lighting` is on.
Increasing this too much can cause significant performance loss, especially if
:ref:`lighting method` is not set to 'shaders' or :ref:`force per pixel
lighting` is on.
This setting has no effect if :ref:`lighting method` is 'legacy'.
antialias alpha test
---------------------------------------

@ -442,23 +442,32 @@ apply lighting to environment maps = false
# This makes fogging independent from the viewing angle. Shaders will be used to render all objects.
radial fog = false
# Internal handling of lights, values are 'legacy', 'default', 'experimental'
lighting method = experimental
# Sets the bounding sphere multiplier of light sources, which are used to determine if an object should
# receive lighting. Higher values will allow for smoother transitions of light sources, but may have a performance cost and
# requires a higher number of 'max lights' set. It is recommended to keep this at 1.0 with 'legacy' lighting enabled.
light bounds multiplier = 1.0
# The distance from the camera at which lights fade away completely. Set to 0 to disable fading.
# Internal handling of lights, ignored if 'force shaders' is off. "legacy"
# provides fixed function pipeline emulation."shaders compatibility" (default)
# uncaps the light limit, enables groundcover lighting, and uses a modified
# attenuation formula to reduce popping and light seams. "shaders" comes with
# all these benefits and is meant for larger light limits, but may not be
# supported on older hardware and may be less performant on weaker hardware when
# 'force per pixel lighting' is enabled.
lighting method = shaders compatibility
# Sets the bounding sphere multiplier of light sources, which are used to
# determine if an object should receive lighting. Higher values will allow for
# smoother transitions of light sources, but may have a performance cost and
# requires a higher number of 'max lights' set. It is recommended to keep this
# at 1.0 with 'legacy' lighting enabled.
light bounds multiplier = 1.75
# The distance from the camera at which lights fade away completely.
# Set to 0 to disable fading.
maximum light distance = 8192
# Fraction of the maximum distance at which lights begin to gradually fade away.
light fade start = 0.85
# Set maximum number of lights per object.
# Only used when 'lighting method' is not set to 'legacy'
max lights = 16
# When 'lighting method' is set to 'legacy', this setting will have no effect.
max lights = 8
# Convert the alpha test (cutout/punchthrough alpha) to alpha-to-coverage.
# This allows MSAA to work with alpha-tested meshes, producing better-looking edges without pixelation.

@ -69,7 +69,7 @@ uniform int PointLightCount;
#endif
void perLightSun(out vec3 ambientOut, out vec3 diffuseOut, vec3 viewPos, vec3 viewNormal)
{
{
vec3 lightDir = normalize(getLight[0].position.xyz);
#if @lightingModel == LIGHTING_MODEL_SINGLE_UBO
@ -98,9 +98,9 @@ void perLightSun(out vec3 ambientOut, out vec3 diffuseOut, vec3 viewPos, vec3 vi
void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal)
{
vec3 lightDir = getLight[lightIndex].position.xyz - viewPos;
vec3 lightPos = getLight[lightIndex].position.xyz - viewPos;
float lightDistance = length(lightDir);
float lightDistance = length(lightPos);
#if !@ffpLighting
// This has a *considerable* performance uplift where GPU is a bottleneck
@ -112,7 +112,7 @@ void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec
}
#endif
lightDir = normalize(lightDir);
lightPos = normalize(lightPos);
#if @ffpLighting
float illumination = clamp(1.0 / (getLight[lightIndex].constantAttenuation + getLight[lightIndex].linearAttenuation * lightDistance + getLight[lightIndex].quadraticAttenuation * lightDistance * lightDistance), 0.0, 1.0);
@ -128,8 +128,8 @@ void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec
ambientOut = getLight[lightIndex].ambient.xyz * illumination;
#endif
float lambert = dot(viewNormal.xyz, lightDir) * illumination;
float lambert = dot(viewNormal.xyz, lightPos) * illumination;
#ifndef GROUNDCOVER
lambert = max(lambert, 0.0);
#else
@ -179,7 +179,7 @@ void doLighting(vec3 viewPos, vec3 viewNormal, out vec3 diffuseLight, out vec3 a
for (int i=1; i <= PointLightCount; ++i)
{
perLightPoint(ambientOut, diffuseOut, i, viewPos, viewNormal);
#else
#else
for (int i=0; i < PointLightCount; ++i)
{
perLightPoint(ambientOut, diffuseOut, PointLightIndex[i], viewPos, viewNormal);

@ -151,6 +151,8 @@ uniform vec3 nodePosition;
uniform float rainIntensity;
#define PER_PIXEL_LIGHTING 0
#include "shadows_fragment.glsl"
#include "lighting.glsl"

Loading…
Cancel
Save