Lighting Patch

Fixes build errors with older OSG builds and some issues with 'shared' layout.
Bring back ambient in inventory through lightmodel instead of sun ambient, mirrors scene ambient/sunlight relationship.
Forces shaders when certain lighting methods are enabled and finalize settings.
Correctly override sun for localmap.
pull/593/head
glassmancody.info 4 years ago
parent a77e1f1812
commit 16856d45c5

@ -111,20 +111,17 @@ namespace
max = MyGUI::utility::parseFloat(widget->getUserString(settingMax));
}
const char* getLightingMethodCaptionText(SceneUtil::LightingMethod lightingMethod)
{
switch (lightingMethod)
{
case SceneUtil::LightingMethod::FFP:
return "Emulates fixed function pipeline lighting, advanced light settings are disabled when this mode is active";
case SceneUtil::LightingMethod::PerObjectUniform:
return "Removes limit of 8 lights per object, fixes lighting attenuation, and enables groundcover lighting and light fade."
"\n\nDesigned for compatibility across hardware, and is not meant for large max light counts.";
case SceneUtil::LightingMethod::SingleUBO:
return "Removes limit of 8 lights per object, fixes lighting attenuation, and enables groundcover lighting and light fade."
"\n\nDesigned for more modern hardware and large max light counts.";
}
return "";
void updateMaxLightsComboBox(MyGUI::ComboBox* box)
{
constexpr int min = 8;
constexpr int max = 32;
constexpr int increment = 8;
int maxLights = Settings::Manager::getInt("max lights", "Shaders");
// show increments of 8 in dropdown
if (maxLights >= min && maxLights <= max && !(maxLights % increment))
box->setIndexSelected((maxLights / increment)-1);
else
box->setIndexSelected(MyGUI::ITEM_NONE);
}
}
@ -235,9 +232,9 @@ namespace MWGui
getWidget(mControllerSwitch, "ControllerButton");
getWidget(mWaterTextureSize, "WaterTextureSize");
getWidget(mWaterReflectionDetail, "WaterReflectionDetail");
getWidget(mLightingMethodText, "LightingMethodText");
getWidget(mLightingMethodButton, "LightingMethodButton");
getWidget(mLightsResetButton, "LightsResetButton");
getWidget(mLightSettingOverlay, "LightSettingOverlay");
getWidget(mMaxLights, "MaxLights");
#ifndef WIN32
// hide gamma controls since it currently does not work under Linux
@ -263,7 +260,9 @@ namespace MWGui
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged);
mLightingMethodButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onLightingMethodButtonChanged);
mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked);
mMaxLights->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onMaxLightsChanged);
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
@ -310,6 +309,8 @@ namespace MWGui
waterReflectionDetail = std::min(5, std::max(0, waterReflectionDetail));
mWaterReflectionDetail->setIndexSelected(waterReflectionDetail);
updateMaxLightsComboBox(mMaxLights);
mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video"));
mKeyboardSwitch->setStateSelected(true);
@ -396,19 +397,50 @@ namespace MWGui
apply();
}
void SettingsWindow::onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos)
{
if (pos == MyGUI::ITEM_NONE)
return;
std::string message = "This change requires a restart to take effect.";
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, {"#{sOK}"}, true);
Settings::Manager::setString("lighting method", "Shaders", _sender->getItemNameAt(pos));
apply();
}
void SettingsWindow::onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos)
{
int count = 8 * (pos + 1);
Settings::Manager::setInt("max lights", "Shaders", count);
apply();
configureWidgets(mMainWidget, false);
}
void SettingsWindow::onLightsResetButtonClicked(MyGUI::Widget* _sender)
{
std::vector<std::string> buttons = {"#{sYes}", "#{sNo}"};
std::string message = "Resets to default values, would you like to continue?";
std::string message = "Resets to default values, would you like to continue? Changes to lighting method will require a restart.";
MWBase::Environment::get().getWindowManager()->interactiveMessageBox(message, buttons, true);
int selectedButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
if (selectedButton == 1 || selectedButton == -1)
return;
constexpr std::array<const char*, 5> settings = {"light bounds multiplier", "maximum light distance", "light fade start", "minimum interior brightness", "max lights"};
constexpr std::array<const char*, 6> settings = {
"light bounds multiplier",
"maximum light distance",
"light fade start",
"minimum interior brightness",
"max lights",
"lighting method",
};
for (const auto& setting : settings)
Settings::Manager::setString(setting, "Shaders", Settings::Manager::mDefaultSettings[{"Shaders", setting}]);
mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(Settings::Manager::mDefaultSettings[{"Shaders", "lighting method"}]));
updateMaxLightsComboBox(mMaxLights);
apply();
configureWidgets(mMainWidget, false);
}
@ -614,20 +646,25 @@ namespace MWGui
void SettingsWindow::updateLightSettings()
{
auto lightingMethod = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getLightingMethod();
mLightingMethodText->setCaption(SceneUtil::LightManager::getLightingMethodString(lightingMethod));
std::string lightingMethodStr = SceneUtil::LightManager::getLightingMethodString(lightingMethod);
mLightingMethodButton->removeAllItems();
std::array<SceneUtil::LightingMethod, 3> methods = {
SceneUtil::LightingMethod::FFP,
SceneUtil::LightingMethod::PerObjectUniform,
SceneUtil::LightingMethod::SingleUBO,
};
if (lightingMethod == SceneUtil::LightingMethod::FFP || !Settings::Manager::getBool("force shaders", "Shaders"))
for (const auto& method : methods)
{
MyGUI::Widget* parent = mLightSettingOverlay->getParent();
mLightSettingOverlay->setEnabled(false);
mLightSettingOverlay->setAlpha(0.8);
parent->setUserString("ToolTipType", "Layout");
parent->setUserString("ToolTipLayout", "TextToolTip");
parent->setUserString("Caption_Text", "Unavailable with current settings.");
parent->setEnabled(true);
if (!MWBase::Environment::get().getResourceSystem()->getSceneManager()->isSupportedLightingMethod(method))
continue;
mLightingMethodButton->addItem(SceneUtil::LightManager::getLightingMethodString(method));
}
mLightingMethodText->setUserString("Caption_Text", getLightingMethodCaptionText(lightingMethod));
mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(lightingMethodStr));
}
void SettingsWindow::layoutControlsBox()

@ -32,8 +32,8 @@ namespace MWGui
MyGUI::ComboBox* mWaterTextureSize;
MyGUI::ComboBox* mWaterReflectionDetail;
MyGUI::Widget* mLightSettingOverlay;
MyGUI::TextBox* mLightingMethodText;
MyGUI::ComboBox* mMaxLights;
MyGUI::ComboBox* mLightingMethodButton;
MyGUI::Button* mLightsResetButton;
// controls
@ -56,7 +56,9 @@ namespace MWGui
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
void onLightingMethodButtonChanged(MyGUI::ComboBox* _sender, size_t pos);
void onLightsResetButtonClicked(MyGUI::Widget* _sender);
void onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos);
void onRebindAction(MyGUI::Widget* _sender);
void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel);

@ -233,7 +233,16 @@ namespace MWRender
float positionZ = std::cos(altitude);
light->setPosition(osg::Vec4(positionX,positionY,positionZ, 0.0));
light->setDiffuse(osg::Vec4(diffuseR,diffuseG,diffuseB,1));
light->setAmbient(osg::Vec4(ambientR,ambientG,ambientB,1));
osg::Vec4 ambientRGBA = osg::Vec4(ambientR,ambientG,ambientB,1);
if (mResourceSystem->getSceneManager()->getForceShaders())
{
// When using shaders, we now skip the ambient sun calculation as this is the only place it's used.
// Using the scene ambient will give identical results.
lightmodel->setAmbientIntensity(ambientRGBA);
light->setAmbient(osg::Vec4(0,0,0,1));
}
else
light->setAmbient(ambientRGBA);
light->setSpecular(osg::Vec4(0,0,0,0));
light->setLightNum(0);
light->setConstantAttenuation(1.f);

@ -224,8 +224,7 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
// override sun for local map
auto lightingMethod = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getLightingMethod();
SceneUtil::configureStateSetSunOverride(lightingMethod, light, stateset);
SceneUtil::configureStateSetSunOverride(static_cast<SceneUtil::LightManager*>(mSceneRoot.get()), light, stateset);
camera->addChild(lightSource);
camera->setStateSet(stateset);

@ -201,11 +201,15 @@ namespace MWRender
, mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f)
{
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
bool explicitlyForceShaders = Settings::Manager::getBool("force shaders", "Shaders");
// Shadows and radial fog have problems with fixed-function mode
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || explicitlyForceShaders || Settings::Manager::getBool("enable shadows", "Shadows");
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders")
|| Settings::Manager::getBool("force shaders", "Shaders")
|| Settings::Manager::getBool("enable shadows", "Shadows")
|| lightingMethod != SceneUtil::LightingMethod::FFP;
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
@ -217,12 +221,11 @@ namespace MWRender
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);
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
// Let LightManager choose which backend to use based on our hint. For methods besides legacy lighting, this depends on support for various OpenGL extensions.
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(!explicitlyForceShaders || lightingMethod == SceneUtil::LightingMethod::FFP);
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(lightingMethod == SceneUtil::LightingMethod::FFP);
resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod());
resourceSystem->getSceneManager()->setSupportedLightingMethods(sceneRoot->getSupportedLightingMethods());
mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
sceneRoot->setLightingMask(Mask_Lighting);
@ -554,7 +557,7 @@ namespace MWRender
// brighten ambient so it reaches the minimum threshold but no more, we want to mess with content data as least we can
float targetBrightnessIncreaseFactor = mMinimumAmbientLuminance / relativeLuminance;
if (ambient.r() == 0.f && ambient.g() == 0.f && ambient.b() == 0.f)
ambient = osg::Vec4(targetBrightnessIncreaseFactor, targetBrightnessIncreaseFactor, targetBrightnessIncreaseFactor, ambient.a());
ambient = osg::Vec4(mMinimumAmbientLuminance, mMinimumAmbientLuminance, mMinimumAmbientLuminance, ambient.a());
else
ambient *= targetBrightnessIncreaseFactor;
}

@ -305,6 +305,16 @@ namespace Resource
mApplyLightingToEnvMaps = apply;
}
void SceneManager::setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported)
{
mSupportedLightingMethods = supported;
}
bool SceneManager::isSupportedLightingMethod(SceneUtil::LightingMethod method) const
{
return mSupportedLightingMethods[static_cast<int>(method)];
}
void SceneManager::setLightingMethod(SceneUtil::LightingMethod method)
{
mLightingMethod = method;

@ -107,6 +107,9 @@ namespace Resource
void setApplyLightingToEnvMaps(bool apply);
void setSupportedLightingMethods(const SceneUtil::LightManager::SupportedMethods& supported);
bool isSupportedLightingMethod(SceneUtil::LightingMethod method) const;
void setLightingMethod(SceneUtil::LightingMethod method);
SceneUtil::LightingMethod getLightingMethod() const;
@ -197,6 +200,7 @@ namespace Resource
std::string mSpecularMapPattern;
bool mApplyLightingToEnvMaps;
SceneUtil::LightingMethod mLightingMethod;
SceneUtil::LightManager::SupportedMethods mSupportedLightingMethods;
bool mConvertAlphaTestToAlphaToCoverage;
osg::ref_ptr<MultiObjectCache> mInstanceCache;

@ -5,6 +5,8 @@
#include <osg/BufferObject>
#include <osg/BufferIndexBinding>
#include <osg/Endian>
#include <osg/Version>
#include <osg/ValueObject>
#include <osgUtil/CullVisitor>
@ -102,6 +104,7 @@ namespace SceneUtil
, mEndian(osg::getCpuByteOrder())
, mCount(count)
, mStride(12)
, mCachedSunPos(osg::Vec4())
{
mOffsets[Diffuse] = 0;
mOffsets[Ambient] = 1;
@ -118,6 +121,7 @@ namespace SceneUtil
, mCount(copy.mCount)
, mStride(copy.mStride)
, mOffsets(copy.mOffsets)
, mCachedSunPos(copy.mCachedSunPos)
{}
void setDiffuse(int index, const osg::Vec4& value)
@ -172,6 +176,17 @@ namespace SceneUtil
return 3 * osg::Vec4::num_components * sizeof(GL_FLOAT) * sz;
}
void setCachedSunPos(const osg::Vec4& pos)
{
mCachedSunPos = pos;
}
void uploadCachedSunPos(const osg::Matrix& viewMat)
{
osg::Vec4 viewPos = mCachedSunPos * viewMat;
std::memcpy(&(*mData)[getOffset(0, Position)], viewPos.ptr(), sizeof(osg::Vec4f));
}
unsigned int asRGBA(const osg::Vec4& value) const
{
return mEndian == osg::BigEndian ? value.asABGR() : value.asRGBA();
@ -187,6 +202,8 @@ namespace SceneUtil
constexpr auto sizeofFloat = sizeof(GL_FLOAT);
constexpr auto sizeofVec4 = sizeofFloat * osg::Vec4::num_components;
LightBuffer oldBuffer = LightBuffer(*this);
mOffsets[Diffuse] = offsetColors / sizeofFloat;
mOffsets[Ambient] = mOffsets[Diffuse] + 1;
mOffsets[Specular] = mOffsets[Diffuse] + 2;
@ -196,7 +213,6 @@ namespace SceneUtil
mStride = (offsetAttenuationRadius + sizeofVec4 + stride) / 4;
// Copy over previous buffers light data. Buffers populate before we know the layout.
LightBuffer oldBuffer = LightBuffer(*this);
mData->resize(size / sizeofFloat);
for (int i = 0; i < oldBuffer.mCount; ++i)
{
@ -212,6 +228,7 @@ namespace SceneUtil
int mCount;
int mStride;
std::array<std::size_t, 6> mOffsets;
osg::Vec4 mCachedSunPos;
};
class LightStateCache
@ -229,8 +246,9 @@ namespace SceneUtil
return &cacheVector[contextid];
}
void configureStateSetSunOverride(LightingMethod method, const osg::Light* light, osg::StateSet* stateset, int mode)
void configureStateSetSunOverride(LightManager* lightManager, const osg::Light* light, osg::StateSet* stateset, int mode)
{
auto method = lightManager->getLightingMethod();
switch (method)
{
case LightingMethod::FFP:
@ -244,12 +262,15 @@ namespace SceneUtil
configureAmbient(lightMat, light->getAmbient());
configureDiffuse(lightMat, light->getDiffuse());
configureSpecular(lightMat, light->getSpecular());
stateset->addUniform(new osg::Uniform("LightBuffer", lightMat), mode);
osg::ref_ptr<osg::Uniform> uni = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "LightBuffer", lightManager->getMaxLights());
uni->setElement(0, lightMat);
stateset->addUniform(uni, mode);
break;
}
case LightingMethod::SingleUBO:
{
osg::ref_ptr<LightBuffer> buffer = new LightBuffer(1);
osg::ref_ptr<LightBuffer> buffer = new LightBuffer(lightManager->getMaxLightsInScene());
buffer->setDiffuse(0, light->getDiffuse());
buffer->setAmbient(0, light->getAmbient());
@ -258,8 +279,11 @@ namespace SceneUtil
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
buffer->getData()->setBufferObject(ubo);
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), buffer->getData().get(), 0, buffer->getData()->getTotalDataSize());
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,7)
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), buffer->getData(), 0, buffer->getData()->getTotalDataSize());
#else
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), ubo, 0, buffer->getData()->getTotalDataSize());
#endif
stateset->setAttributeAndModes(ubb, mode);
break;
@ -433,6 +457,11 @@ namespace SceneUtil
lightUniform->setElement(i+1, lightMat);
}
auto sun = mLightManager->getSunlightBuffer(state.getFrameStamp()->getFrameNumber());
configurePosition(sun, osg::Vec4(sun(0,0), sun(0,1), sun(0,2), 0.0) * state.getInitialViewMatrix());
lightUniform->setElement(0, sun);
lightUniform->dirty();
}
@ -618,7 +647,6 @@ namespace SceneUtil
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
bool pop = false;
if (mLastFrameNumber != cv->getTraversalNumber())
{
@ -628,18 +656,24 @@ namespace SceneUtil
{
auto stateset = mLightManager->getStateSet();
auto bo = mLightManager->getLightBuffer(mLastFrameNumber);
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), bo->getData().get(), 0, bo->getData()->getTotalDataSize());
stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON);
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,7)
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), bo->getData(), 0, bo->getData()->getTotalDataSize());
#else
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(static_cast<int>(Shader::UBOBinding::LightBuffer), bo->getData()->getBufferObject(), 0, bo->getData()->getTotalDataSize());
#endif
stateset->setAttributeAndModes(ubb, osg::StateAttribute::ON);
}
auto sun = mLightManager->getSunlight();
if (sun)
{
// we must defer uploading the transformation to view-space position to deal with different cameras (e.g. reflection RTT).
if (mLightManager->getLightingMethod() == LightingMethod::PerObjectUniform)
{
osg::Matrixf lightMat;
configurePosition(lightMat, sun->getPosition() * (*cv->getCurrentRenderStage()->getInitialViewMatrix()));
configurePosition(lightMat, sun->getPosition());
configureAmbient(lightMat, sun->getAmbient());
configureDiffuse(lightMat, sun->getDiffuse());
configureSpecular(lightMat, sun->getSpecular());
@ -649,33 +683,15 @@ namespace SceneUtil
{
auto buf = mLightManager->getLightBuffer(mLastFrameNumber);
buf->setPosition(0, sun->getPosition() * (*cv->getCurrentRenderStage()->getInitialViewMatrix()));
buf->setCachedSunPos(sun->getPosition());
buf->setAmbient(0, sun->getAmbient());
buf->setDiffuse(0, sun->getDiffuse());
buf->setSpecular(0, sun->getSpecular());
}
}
}
else if (isReflectionCamera(cv->getCurrentCamera()))
{
auto sun = mLightManager->getSunlight();
if (sun)
{
osg::Vec4 originalPos = sun->getPosition();
sun->setPosition(originalPos * (*cv->getCurrentRenderStage()->getInitialViewMatrix()));
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
configureStateSetSunOverride(mLightManager->getLightingMethod(), sun, stateset);
sun->setPosition(originalPos);
cv->pushStateSet(stateset);
pop = true;
}
}
traverse(node, nv);
if (pop)
cv->popStateSet();
}
private:
@ -692,6 +708,7 @@ namespace SceneUtil
LightManagerStateAttribute(LightManager* lightManager)
: mLightManager(lightManager)
, mDummyProgram(new osg::Program)
, mInitLayout(false)
{
static const std::string dummyVertSource = generateDummyShader(mLightManager->getMaxLightsInScene());
@ -741,8 +758,7 @@ namespace SceneUtil
void apply(osg::State& state) const override
{
static bool init = false;
if (!init)
if (!mInitLayout)
{
auto handle = mDummyProgram->getPCP(state)->getHandle();
auto* ext = state.get<osg::GLExtensions>();
@ -754,13 +770,11 @@ namespace SceneUtil
if (activeUniformBlocks > 0)
{
initSharedLayout(ext, handle);
init = true;
mInitLayout = true;
}
}
else
{
mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->dirty();
}
mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->uploadCachedSunPos(state.getInitialViewMatrix());
mLightManager->getLightBuffer(state.getFrameStamp()->getFrameNumber())->dirty();
}
private:
@ -792,36 +806,7 @@ namespace SceneUtil
LightManager* mLightManager;
osg::ref_ptr<osg::Program> mDummyProgram;
};
class LightManagerStateAttributePerObjectUniform : public osg::StateAttribute
{
public:
LightManagerStateAttributePerObjectUniform()
: mLightManager(nullptr) {}
LightManagerStateAttributePerObjectUniform(LightManager* lightManager)
: mLightManager(lightManager)
{
}
LightManagerStateAttributePerObjectUniform(const LightManagerStateAttributePerObjectUniform& copy, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
: osg::StateAttribute(copy,copyop), mLightManager(copy.mLightManager) {}
int compare(const StateAttribute &sa) const override
{
throw std::runtime_error("LightManagerStateAttributePerObjectUniform::compare: unimplemented");
}
META_StateAttribute(NifOsg, LightManagerStateAttributePerObjectUniform, osg::StateAttribute::LIGHT)
void apply(osg::State& state) const override
{
mLightManager->getStateSet()->getUniform("LightBuffer")->setElement(0, mLightManager->getSunlightBuffer(state.getFrameStamp()->getFrameNumber()));
}
private:
LightManager* mLightManager;
mutable bool mInitLayout;
};
const std::unordered_map<std::string, LightingMethod> LightManager::mLightingMethodSettingMap = {
@ -857,6 +842,14 @@ namespace SceneUtil
, mPointLightFadeEnd(0.f)
, mPointLightFadeStart(0.f)
{
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
bool supportsUBO = exts && exts->isUniformBufferObjectSupported;
bool supportsGPU4 = exts && exts->isGpuShader4Supported;
mSupported[static_cast<int>(LightingMethod::FFP)] = true;
mSupported[static_cast<int>(LightingMethod::PerObjectUniform)] = true;
mSupported[static_cast<int>(LightingMethod::SingleUBO)] = supportsUBO && supportsGPU4;
setUpdateCallback(new LightManagerUpdateCallback);
if (ffp)
@ -870,10 +863,6 @@ namespace SceneUtil
updateSettings();
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
bool supportsUBO = exts && exts->isUniformBufferObjectSupported;
bool supportsGPU4 = exts && exts->isGpuShader4Supported;
static bool hasLoggedWarnings = false;
if (lightingMethod == LightingMethod::SingleUBO && !hasLoggedWarnings)
@ -1042,7 +1031,8 @@ namespace SceneUtil
setLightingMethod(LightingMethod::PerObjectUniform);
setMaxLights(std::clamp(targetLights, mMaxLightsLowerLimit, LightManager::mMaxLightsUpperLimit));
stateset->setAttributeAndModes(new LightManagerStateAttributePerObjectUniform(this), osg::StateAttribute::ON);
// ensures sunlight element in our uniform array is updated when there are no point lights in scene
stateset->setAttributeAndModes(new LightStateAttributePerObjectUniform({}, this), osg::StateAttribute::ON);
stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "LightBuffer", getMaxLights()));
}

@ -5,6 +5,7 @@
#include <unordered_set>
#include <unordered_map>
#include <memory>
#include <array>
#include <osg/Light>
@ -24,7 +25,7 @@ namespace osgUtil
namespace SceneUtil
{
class LightBuffer;
class StateSetGenerator;
struct StateSetGenerator;
enum class LightingMethod
{
@ -33,8 +34,6 @@ namespace SceneUtil
SingleUBO,
};
void configureStateSetSunOverride(LightingMethod method, const osg::Light* light, osg::StateSet* stateset, int mode = osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
/// LightSource managed by a LightManager.
/// @par Typically used for point lights. Spot lights are not supported yet. Directional lights affect the whole scene
/// so do not need to be managed by a LightManager - so for directional lights use a plain osg::LightSource instead.
@ -130,6 +129,7 @@ namespace SceneUtil
};
using LightList = std::vector<const LightSourceViewBound*>;
using SupportedMethods = std::array<bool, 3>;
META_Node(SceneUtil, LightManager)
@ -178,6 +178,8 @@ namespace SceneUtil
osg::Matrixf getSunlightBuffer(size_t frameNum) const { return mSunlightBuffers[frameNum%2]; }
void setSunlightBuffer(const osg::Matrixf& buffer, size_t frameNum) { mSunlightBuffers[frameNum%2] = buffer; }
SupportedMethods getSupportedLightingMethods() { return mSupported; }
std::map<std::string, std::string> getLightDefines() const;
void processChangedSettings(const Settings::CategorySettingVector& changed);
@ -232,6 +234,8 @@ namespace SceneUtil
int mMaxLights;
SupportedMethods mSupported;
static constexpr auto mMaxLightsLowerLimit = 2;
static constexpr auto mMaxLightsUpperLimit = 64;
static constexpr auto mFFPMaxLights = 8;
@ -277,6 +281,8 @@ namespace SceneUtil
std::set<SceneUtil::LightSource*> mIgnoredLightSources;
};
void configureStateSetSunOverride(LightManager* lightManager, const osg::Light* light, osg::StateSet* stateset, int mode = osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
}
#endif

@ -174,12 +174,14 @@ 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'.
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.
Note that the rendering will act as if you have 'force shaders' option enabled
when not set to 'legacy'. This means that shaders will be used to render all objects and
the terrain.
light bounds multiplier
-----------------------

@ -460,109 +460,108 @@
<Widget type="TabItem" skin="" position="4 32 360 308">
<Property key="Caption" value=" Lights "/>
<Widget type="Widget" skin="" position="0 0 360 315" align="Stretch">
<Widget type="Widget" skin="" position="0 0 360 315" align="Stretch" name="LightSettingOverlay">
<!-- Lighting Method -->
<Widget type="TextBox" skin="NormalText" position="0 4 170 18" align="Left Top">
<Property key="Caption" value="Lighting Method:"/>
</Widget>
<Widget type="TextBox" skin="SandText" position="0 28 170 18" align="Left Top" name="LightingMethodText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
</Widget>
<!-- Max Lights -->
<Widget type="TextBox" skin="NormalText" position="178 4 160 18" align="Left Top" name="MaxLightsText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 8\nMaximum number of lights per object.\n\nA low number near default will cause light popping similar to what you would see with legacy lighting."/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="178 30 160 18" align="Right Top HStretch" name="MaxLightsSlider">
<Property key="Range" value="24"/>
<Property key="Page" value="1"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingMin" value="4"/>
<UserString key="SettingMax" value="32"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="max lights"/>
<UserString key="SettingValueType" value="Integer"/>
<UserString key="SettingLabelWidget" value="MaxLightsText"/>
<UserString key="SettingLabelCaption" value="Max Lights (%s)"/>
</Widget>
<Widget type="ImageBox" skin="MW_HLine" position="0 54 360 18" align="Top HStretch"/>
<!-- Light Fade Start -->
<Widget type="TextBox" skin="NormalText" position="0 78 352 18" align="Left Top" name="MaxLightDistanceText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 8192 (units)\nMaximum distance at which lights will appear.\n\nSet this to 0.0 to disable it."/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 104 352 18" align="HStretch Top">
<Property key="Range" value="8192"/>
<Property key="Page" value="1"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingMin" value="0"/>
<UserString key="SettingMax" value="8192"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="maximum light distance"/>
<UserString key="SettingValueType" value="Integer"/>
<UserString key="SettingLabelWidget" value="MaxLightDistanceText"/>
<UserString key="SettingLabelCaption" value="Maximum Light Distance (%s)"/>
</Widget>
<!-- Light Fade Multiplier -->
<Widget type="TextBox" skin="NormalText" position="0 128 352 18" align="Left Top" name="LightFadeMultiplierText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 0.85\nFraction of maximum distance at which lights will start to fade.\n\nSet this to a low value for slower transitions or a high value of quicker transitions."/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 152 352 18" align="HStretch Top">
<Property key="Range" value="10000"/>
<Property key="Page" value="100"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="light fade start"/>
<UserString key="SettingValueType" value="Float"/>
<UserString key="SettingMin" value="0.0"/>
<UserString key="SettingMax" value="1.0"/>
<UserString key="SettingLabelWidget" value="LightFadeMultiplierText"/>
<UserString key="SettingLabelCaption" value="Fade Start Multiplier (%s)"/>
</Widget>
<!-- Bounding Sphere Multiplier -->
<Widget type="TextBox" skin="NormalText" position="0 176 352 18" align="Left Top" name="BoundingSphereMultText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 1.65\nMultipler for bounding sphere of lights.\nHigher numbers allows for smooth falloff but require an increase in number of max lights.\n\nDoes not effect the illumination or strength of lights."/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 200 352 18" align="HStretch Top">
<Property key="Range" value="500000"/>
<Property key="Page" value="1000"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingMin" value="0.0"/>
<UserString key="SettingMax" value="5.0"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="light bounds multiplier"/>
<UserString key="SettingValueType" value="Float"/>
<UserString key="SettingLabelWidget" value="BoundingSphereMultText"/>
<UserString key="SettingLabelCaption" value="Bounding Sphere Multiplier (%s)"/>
</Widget>
<!-- Minimum Ambient Brightness -->
<Widget type="TextBox" skin="NormalText" position="0 224 352 18" align="Left Top" name="MinimumBrightnessText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 0.08\nMinimum ambient interior brightness.\n\nIncrease this if you feel interiors are too dark."/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 248 352 18" align="HStretch Top">
<Property key="Range" value="10000"/>
<Property key="Page" value="100"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="minimum interior brightness"/>
<UserString key="SettingValueType" value="Float"/>
<UserString key="SettingLabelWidget" value="MinimumBrightnessText"/>
<UserString key="SettingLabelCaption" value="Minimum Interior Brightness (%s)"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" position="0 290 0 0" align="Top Left" name="LightsResetButton">
<Property key="Caption" value="Reset To Defaults"/>
</Widget>
<!-- Lighting Method -->
<Widget type="TextBox" skin="NormalText" position="0 4 170 18" align="Left Top">
<Property key="Caption" value="Lighting Method:"/>
</Widget>
<Widget type="ComboBox" skin="MW_ComboBox" position="0 28 170 24" align="Left Top" name="LightingMethodButton">
<Property key="AddItem" value="legacy"/>
<Property key="AddItem" value="shaders compatibility"/>
<Property key="AddItem" value="shaders"/>
</Widget>
<!-- Max Lights -->
<Widget type="TextBox" skin="NormalText" position="178 4 160 18" align="Left Top" name="MaxLightsText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 8\nMaximum number of lights per object.\n\nA low number near default will cause light popping similar to what you would see with legacy lighting."/>
</Widget>
<Widget type="ScrollBar" skin="" position="0 0 0 0">
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="max lights"/>
<UserString key="SettingValueType" value="Integer"/>
<UserString key="SettingLabelWidget" value="MaxLightsText"/>
<UserString key="SettingLabelCaption" value="Max Lights: (%s)"/>
</Widget>
<Widget type="ComboBox" skin="MW_ComboBox" position="178 28 170 24" align="Left Top" name="MaxLights">
<Property key="AddItem" value="8"/>
<Property key="AddItem" value="16"/>
<Property key="AddItem" value="24"/>
<Property key="AddItem" value="32"/>
</Widget>
<Widget type="ImageBox" skin="MW_HLine" position="0 54 360 18" align="Top HStretch"/>
<!-- Light Fade Start -->
<Widget type="TextBox" skin="NormalText" position="0 78 352 18" align="Left Top" name="MaxLightDistanceText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 8192 (units)\nMaximum distance at which lights will appear.\n\nSet this to 0.0 to disable it."/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 104 352 18" align="HStretch Top">
<Property key="Range" value="8192"/>
<Property key="Page" value="1"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingMin" value="0"/>
<UserString key="SettingMax" value="8192"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="maximum light distance"/>
<UserString key="SettingValueType" value="Integer"/>
<UserString key="SettingLabelWidget" value="MaxLightDistanceText"/>
<UserString key="SettingLabelCaption" value="Maximum Light Distance (%s)"/>
</Widget>
<!-- Light Fade Multiplier -->
<Widget type="TextBox" skin="NormalText" position="0 128 352 18" align="Left Top" name="LightFadeMultiplierText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 0.85\nFraction of maximum distance at which lights will start to fade.\n\nSet this to a low value for slower transitions or a high value for quicker transitions."/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 152 352 18" align="HStretch Top">
<Property key="Range" value="10000"/>
<Property key="Page" value="100"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="light fade start"/>
<UserString key="SettingValueType" value="Float"/>
<UserString key="SettingMin" value="0.0"/>
<UserString key="SettingMax" value="1.0"/>
<UserString key="SettingLabelWidget" value="LightFadeMultiplierText"/>
<UserString key="SettingLabelCaption" value="Fade Start Multiplier (%s)"/>
</Widget>
<!-- Bounding Sphere Multiplier -->
<Widget type="TextBox" skin="NormalText" position="0 176 352 18" align="Left Top" name="BoundingSphereMultText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 1.65\nMultipler for bounding sphere of lights.\nHigher numbers allows for smooth falloff but require an increase in number of max lights.\n\nDoes not effect the illumination or strength of lights."/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 200 352 18" align="HStretch Top">
<Property key="Range" value="500000"/>
<Property key="Page" value="1000"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingMin" value="0.0"/>
<UserString key="SettingMax" value="5.0"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="light bounds multiplier"/>
<UserString key="SettingValueType" value="Float"/>
<UserString key="SettingLabelWidget" value="BoundingSphereMultText"/>
<UserString key="SettingLabelCaption" value="Bounding Sphere Multiplier (%s)"/>
</Widget>
<!-- Minimum Ambient Brightness -->
<Widget type="TextBox" skin="NormalText" position="0 224 352 18" align="Left Top" name="MinimumBrightnessText">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="TextToolTip"/>
<UserString key="Caption_Text" value="Default: 0.08\nMinimum ambient interior brightness.\n\nIncrease this if you feel interiors are too dark."/>
</Widget>
<Widget type="ScrollBar" skin="MW_HScroll" position="0 248 352 18" align="HStretch Top">
<Property key="Range" value="10000"/>
<Property key="Page" value="100"/>
<UserString key="SettingType" value="Slider"/>
<UserString key="SettingCategory" value="Shaders"/>
<UserString key="SettingName" value="minimum interior brightness"/>
<UserString key="SettingValueType" value="Float"/>
<UserString key="SettingLabelWidget" value="MinimumBrightnessText"/>
<UserString key="SettingLabelCaption" value="Minimum Interior Brightness (%s)"/>
</Widget>
<Widget type="AutoSizedButton" skin="MW_Button" position="0 290 0 0" align="Top Left" name="LightsResetButton">
<Property key="Caption" value="Reset To Defaults"/>
</Widget>
</Widget>
<!--

Loading…
Cancel
Save