mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-15 08:39:43 +00:00
Merge branch 'why_are_the_christmas_lights_still_up' into 'master'
Remove 8 light limit and add configurable lighting enhancements See merge request OpenMW/openmw!618
This commit is contained in:
commit
f1cfdafd4d
40 changed files with 2041 additions and 273 deletions
AUTHORS.mdCHANGELOG.md
apps
launcher
opencs/model/world
openmw
components
misc
resource
sceneutil
shader
docs/source/reference/modding/settings
files
|
@ -49,6 +49,7 @@ Programmers
|
|||
Cédric Mocquillon
|
||||
Chris Boyce (slothlife)
|
||||
Chris Robinson (KittyCat)
|
||||
Cody Glassman (Wazabear)
|
||||
Coleman Smith (olcoal)
|
||||
Cory F. Cohen (cfcohen)
|
||||
Cris Mihalache (Mirceam)
|
||||
|
|
|
@ -152,6 +152,7 @@
|
|||
Feature #5771: ori command should report where a mesh is loaded from and whether the x version is used.
|
||||
Feature #5813: Instanced groundcover support
|
||||
Feature #5814: Bsatool should be able to create BSA archives, not only to extract it
|
||||
Feature #5828: Support more than 8 lights
|
||||
Feature #5910: Fall back to delta time when physics can't keep up
|
||||
Task #5480: Drop Qt4 support
|
||||
Task #5520: Improve cell name autocompleter implementation
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "advancedpage.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <components/config/gamesettings.hpp>
|
||||
#include <components/config/launchersettings.hpp>
|
||||
#include <QFileDialog>
|
||||
|
@ -138,6 +140,13 @@ bool Launcher::AdvancedPage::loadSettings()
|
|||
|
||||
loadSettingBool(activeGridObjectPagingCheckBox, "object paging active grid", "Terrain");
|
||||
viewingDistanceComboBox->setValue(convertToCells(mEngineSettings.getInt("viewing distance", "Camera")));
|
||||
|
||||
int lightingMethod = 1;
|
||||
if (mEngineSettings.getString("lighting method", "Shaders") == "legacy")
|
||||
lightingMethod = 0;
|
||||
else if (mEngineSettings.getString("lighting method", "Shaders") == "shaders")
|
||||
lightingMethod = 2;
|
||||
lightingMethodComboBox->setCurrentIndex(lightingMethod);
|
||||
}
|
||||
|
||||
// Audio
|
||||
|
@ -288,6 +297,9 @@ void Launcher::AdvancedPage::saveSettings()
|
|||
{
|
||||
mEngineSettings.setInt("viewing distance", "Camera", convertToUnits(viewingDistance));
|
||||
}
|
||||
|
||||
static std::array<std::string, 3> lightingMethodMap = {"legacy", "shaders compatibility", "shaders"};
|
||||
mEngineSettings.setString("lighting method", "Shaders", lightingMethodMap[lightingMethodComboBox->currentIndex()]);
|
||||
}
|
||||
|
||||
// Audio
|
||||
|
|
|
@ -83,6 +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["lightingModel"] = "0";
|
||||
for (const auto& define : shadowDefines)
|
||||
defines[define.first] = define.second;
|
||||
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
|
||||
|
|
|
@ -11,12 +11,16 @@
|
|||
|
||||
#include <iomanip>
|
||||
#include <numeric>
|
||||
#include <array>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/widgets/sharedstatebutton.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -106,11 +110,27 @@ namespace
|
|||
if (!widget->getUserString(settingMax).empty())
|
||||
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 "";
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
void SettingsWindow::configureWidgets(MyGUI::Widget* widget)
|
||||
void SettingsWindow::configureWidgets(MyGUI::Widget* widget, bool init)
|
||||
{
|
||||
MyGUI::EnumeratorWidgetPtr widgets = widget->getEnumerator();
|
||||
while (widgets.next())
|
||||
|
@ -124,7 +144,8 @@ namespace MWGui
|
|||
getSettingCategory(current))
|
||||
? "#{sOn}" : "#{sOff}";
|
||||
current->castType<MyGUI::Button>()->setCaptionWithReplacing(initialValue);
|
||||
current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
if (init)
|
||||
current->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
|
||||
}
|
||||
if (type == sliderType)
|
||||
{
|
||||
|
@ -144,6 +165,12 @@ namespace MWGui
|
|||
ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits;
|
||||
valueStr = ss.str();
|
||||
}
|
||||
else if (valueType == "Float")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(2) << value;
|
||||
valueStr = ss.str();
|
||||
}
|
||||
else
|
||||
valueStr = MyGUI::utility::toString(int(value));
|
||||
|
||||
|
@ -158,12 +185,13 @@ namespace MWGui
|
|||
valueStr = MyGUI::utility::toString(value);
|
||||
scroll->setScrollPosition(value);
|
||||
}
|
||||
scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
if (init)
|
||||
scroll->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition);
|
||||
if (scroll->getVisible())
|
||||
updateSliderLabel(scroll, valueStr);
|
||||
}
|
||||
|
||||
configureWidgets(current);
|
||||
configureWidgets(current, init);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +218,7 @@ namespace MWGui
|
|||
getWidget(unusedSlider, widgetName);
|
||||
unusedSlider->setVisible(false);
|
||||
|
||||
configureWidgets(mMainWidget);
|
||||
configureWidgets(mMainWidget, true);
|
||||
|
||||
setTitle("#{sOptions}");
|
||||
|
||||
|
@ -207,6 +235,9 @@ namespace MWGui
|
|||
getWidget(mControllerSwitch, "ControllerButton");
|
||||
getWidget(mWaterTextureSize, "WaterTextureSize");
|
||||
getWidget(mWaterReflectionDetail, "WaterReflectionDetail");
|
||||
getWidget(mLightingMethodText, "LightingMethodText");
|
||||
getWidget(mLightsResetButton, "LightsResetButton");
|
||||
getWidget(mLightSettingOverlay, "LightSettingOverlay");
|
||||
|
||||
#ifndef WIN32
|
||||
// hide gamma controls since it currently does not work under Linux
|
||||
|
@ -232,6 +263,8 @@ namespace MWGui
|
|||
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
|
||||
mWaterReflectionDetail->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterReflectionDetailChanged);
|
||||
|
||||
mLightsResetButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onLightsResetButtonClicked);
|
||||
|
||||
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
|
||||
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
|
||||
|
||||
|
@ -363,6 +396,23 @@ namespace MWGui
|
|||
apply();
|
||||
}
|
||||
|
||||
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?";
|
||||
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"};
|
||||
for (const auto& setting : settings)
|
||||
Settings::Manager::setString(setting, "Shaders", Settings::Manager::mDefaultSettings[{"Shaders", setting}]);
|
||||
|
||||
apply();
|
||||
configureWidgets(mMainWidget, false);
|
||||
}
|
||||
|
||||
void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender)
|
||||
{
|
||||
std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On");
|
||||
|
@ -465,6 +515,12 @@ namespace MWGui
|
|||
ss << std::fixed << std::setprecision(2) << value/Constants::CellSizeInUnits;
|
||||
valueStr = ss.str();
|
||||
}
|
||||
else if (valueType == "Float")
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(2) << value;
|
||||
valueStr = ss.str();
|
||||
}
|
||||
else
|
||||
valueStr = MyGUI::utility::toString(int(value));
|
||||
}
|
||||
|
@ -555,6 +611,25 @@ namespace MWGui
|
|||
layoutControlsBox();
|
||||
}
|
||||
|
||||
void SettingsWindow::updateLightSettings()
|
||||
{
|
||||
auto lightingMethod = MWBase::Environment::get().getResourceSystem()->getSceneManager()->getLightingMethod();
|
||||
mLightingMethodText->setCaption(SceneUtil::LightManager::getLightingMethodString(lightingMethod));
|
||||
|
||||
if (lightingMethod == SceneUtil::LightingMethod::FFP || !Settings::Manager::getBool("force shaders", "Shaders"))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
mLightingMethodText->setUserString("Caption_Text", getLightingMethodCaptionText(lightingMethod));
|
||||
}
|
||||
|
||||
void SettingsWindow::layoutControlsBox()
|
||||
{
|
||||
const int h = 18;
|
||||
|
@ -617,6 +692,7 @@ namespace MWGui
|
|||
{
|
||||
highlightCurrentResolution();
|
||||
updateControlsBox();
|
||||
updateLightSettings();
|
||||
resetScrollbars();
|
||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ namespace MWGui
|
|||
|
||||
void updateControlsBox();
|
||||
|
||||
void updateLightSettings();
|
||||
|
||||
void onResChange(int, int) override { center(); }
|
||||
|
||||
protected:
|
||||
|
@ -30,6 +32,10 @@ namespace MWGui
|
|||
MyGUI::ComboBox* mWaterTextureSize;
|
||||
MyGUI::ComboBox* mWaterReflectionDetail;
|
||||
|
||||
MyGUI::Widget* mLightSettingOverlay;
|
||||
MyGUI::TextBox* mLightingMethodText;
|
||||
MyGUI::Button* mLightsResetButton;
|
||||
|
||||
// controls
|
||||
MyGUI::ScrollView* mControlsBox;
|
||||
MyGUI::Button* mResetControlsButton;
|
||||
|
@ -50,6 +56,8 @@ namespace MWGui
|
|||
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||
|
||||
void onLightsResetButtonClicked(MyGUI::Widget* _sender);
|
||||
|
||||
void onRebindAction(MyGUI::Widget* _sender);
|
||||
void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel);
|
||||
void onResetDefaultBindings(MyGUI::Widget* _sender);
|
||||
|
@ -61,7 +69,7 @@ namespace MWGui
|
|||
|
||||
void apply();
|
||||
|
||||
void configureWidgets(MyGUI::Widget* widget);
|
||||
void configureWidgets(MyGUI::Widget* widget, bool init);
|
||||
void updateSliderLabel(MyGUI::ScrollBar* scroller, const std::string& value);
|
||||
|
||||
void layoutControlsBox();
|
||||
|
|
|
@ -499,6 +499,11 @@ namespace MWRender
|
|||
mAlpha = alpha;
|
||||
}
|
||||
|
||||
void setLightSource(const osg::ref_ptr<SceneUtil::LightSource>& lightSource)
|
||||
{
|
||||
mLightSource = lightSource;
|
||||
}
|
||||
|
||||
protected:
|
||||
void setDefaults(osg::StateSet* stateset) override
|
||||
{
|
||||
|
@ -521,10 +526,13 @@ namespace MWRender
|
|||
{
|
||||
osg::Material* material = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
|
||||
material->setAlpha(osg::Material::FRONT_AND_BACK, mAlpha);
|
||||
if (mLightSource)
|
||||
mLightSource->setActorFade(mAlpha);
|
||||
}
|
||||
|
||||
private:
|
||||
float mAlpha;
|
||||
osg::ref_ptr<SceneUtil::LightSource> mLightSource;
|
||||
};
|
||||
|
||||
struct Animation::AnimSource
|
||||
|
@ -1613,7 +1621,7 @@ namespace MWRender
|
|||
{
|
||||
bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
|
||||
|
||||
SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior);
|
||||
mExtraLightSource = SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior);
|
||||
}
|
||||
|
||||
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture)
|
||||
|
@ -1763,6 +1771,7 @@ namespace MWRender
|
|||
if (mTransparencyUpdater == nullptr)
|
||||
{
|
||||
mTransparencyUpdater = new TransparencyUpdater(alpha);
|
||||
mTransparencyUpdater->setLightSource(mExtraLightSource);
|
||||
mObjectRoot->addCullCallback(mTransparencyUpdater);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -278,6 +278,7 @@ protected:
|
|||
osg::ref_ptr<SceneUtil::LightSource> mGlowLight;
|
||||
osg::ref_ptr<SceneUtil::GlowUpdater> mGlowUpdater;
|
||||
osg::ref_ptr<TransparencyUpdater> mTransparencyUpdater;
|
||||
osg::ref_ptr<SceneUtil::LightSource> mExtraLightSource;
|
||||
|
||||
float mAlpha;
|
||||
|
||||
|
|
|
@ -174,7 +174,9 @@ namespace MWRender
|
|||
|
||||
mCamera->setNodeMask(Mask_RenderToTexture);
|
||||
|
||||
osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager;
|
||||
bool ffp = mResourceSystem->getSceneManager()->getLightingMethod() == SceneUtil::LightingMethod::FFP;
|
||||
|
||||
osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager(ffp);
|
||||
lightManager->setStartLight(1);
|
||||
osg::ref_ptr<osg::StateSet> stateset = lightManager->getOrCreateStateSet();
|
||||
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
|
||||
|
@ -237,6 +239,7 @@ namespace MWRender
|
|||
light->setConstantAttenuation(1.f);
|
||||
light->setLinearAttenuation(0.f);
|
||||
light->setQuadraticAttenuation(0.f);
|
||||
lightManager->setSunlight(light);
|
||||
|
||||
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource;
|
||||
lightSource->setLight(light);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <osg/VertexAttribDivisor>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
|
||||
#include "apps/openmw/mwworld/esmstore.hpp"
|
||||
#include "apps/openmw/mwbase/environment.hpp"
|
||||
|
@ -271,6 +272,8 @@ namespace MWRender
|
|||
group->getOrCreateStateSet()->setAttributeAndModes(alpha.get(), osg::StateAttribute::ON);
|
||||
group->getBound();
|
||||
group->setNodeMask(Mask_Groundcover);
|
||||
if (mSceneManager->getLightingMethod() != SceneUtil::LightingMethod::FFP)
|
||||
group->setCullCallback(new SceneUtil::LightListCallback);
|
||||
mSceneManager->recreateShaders(group, "groundcover", false, true);
|
||||
|
||||
return group;
|
||||
|
|
|
@ -19,7 +19,10 @@
|
|||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/sceneutil/shadow.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/files/memorystream.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
@ -220,6 +223,10 @@ 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);
|
||||
|
||||
camera->addChild(lightSource);
|
||||
camera->setStateSet(stateset);
|
||||
camera->setViewport(0, 0, mMapResolution, mMapResolution);
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "../mwgui/loadingscreen.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwmechanics/actorutil.hpp"
|
||||
|
||||
#include "sky.hpp"
|
||||
#include "effectmanager.hpp"
|
||||
|
@ -195,14 +196,16 @@ namespace MWRender
|
|||
, mWorkQueue(workQueue)
|
||||
, mUnrefQueue(new SceneUtil::UnrefQueue)
|
||||
, mNavigator(navigator)
|
||||
, mMinimumAmbientLuminance(0.f)
|
||||
, mNightEyeFactor(0.f)
|
||||
, mFieldOfViewOverridden(false)
|
||||
, mFieldOfViewOverride(0.f)
|
||||
{
|
||||
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") || Settings::Manager::getBool("force shaders", "Shaders") || Settings::Manager::getBool("enable shadows", "Shadows");
|
||||
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders") || explicitlyForceShaders || Settings::Manager::getBool("enable shadows", "Shadows");
|
||||
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"));
|
||||
|
@ -214,7 +217,14 @@ 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);
|
||||
|
||||
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager;
|
||||
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);
|
||||
resourceSystem->getSceneManager()->getShaderManager().setLightingMethod(sceneRoot->getLightingMethod());
|
||||
resourceSystem->getSceneManager()->setLightingMethod(sceneRoot->getLightingMethod());
|
||||
|
||||
mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
|
||||
|
||||
sceneRoot->setLightingMask(Mask_Lighting);
|
||||
mSceneRoot = sceneRoot;
|
||||
sceneRoot->setStartLight(1);
|
||||
|
@ -236,6 +246,7 @@ namespace MWRender
|
|||
mShadowManager.reset(new SceneUtil::ShadowManager(sceneRoot, mRootNode, shadowCastingTraversalMask, indoorShadowCastingTraversalMask, mResourceSystem->getSceneManager()->getShaderManager()));
|
||||
|
||||
Shader::ShaderManager::DefineMap shadowDefines = mShadowManager->getShadowDefines();
|
||||
Shader::ShaderManager::DefineMap lightDefines = sceneRoot->getLightDefines();
|
||||
Shader::ShaderManager::DefineMap globalDefines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
|
||||
|
||||
for (auto itr = shadowDefines.begin(); itr != shadowDefines.end(); itr++)
|
||||
|
@ -247,6 +258,9 @@ namespace MWRender
|
|||
globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
|
||||
globalDefines["useGPUShader4"] = "0";
|
||||
|
||||
for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
|
||||
globalDefines[itr->first] = itr->second;
|
||||
|
||||
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);
|
||||
|
@ -352,6 +366,7 @@ namespace MWRender
|
|||
mSunLight->setAmbient(osg::Vec4f(0,0,0,1));
|
||||
mSunLight->setSpecular(osg::Vec4f(0,0,0,0));
|
||||
mSunLight->setConstantAttenuation(1.f);
|
||||
sceneRoot->setSunlight(mSunLight);
|
||||
sceneRoot->addChild(source);
|
||||
|
||||
sceneRoot->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
|
||||
|
@ -520,7 +535,32 @@ namespace MWRender
|
|||
|
||||
void RenderingManager::configureAmbient(const ESM::Cell *cell)
|
||||
{
|
||||
setAmbientColour(SceneUtil::colourFromRGB(cell->mAmbi.mAmbient));
|
||||
bool needsAdjusting = false;
|
||||
if (mResourceSystem->getSceneManager()->getLightingMethod() != SceneUtil::LightingMethod::FFP)
|
||||
needsAdjusting = !cell->isExterior() && !(cell->mData.mFlags & ESM::Cell::QuasiEx);
|
||||
|
||||
auto ambient = SceneUtil::colourFromRGB(cell->mAmbi.mAmbient);
|
||||
|
||||
if (needsAdjusting)
|
||||
{
|
||||
constexpr float pR = 0.2126;
|
||||
constexpr float pG = 0.7152;
|
||||
constexpr float pB = 0.0722;
|
||||
|
||||
// we already work in linear RGB so no conversions are needed for the luminosity function
|
||||
float relativeLuminance = pR*ambient.r() + pG*ambient.g() + pB*ambient.b();
|
||||
if (relativeLuminance < mMinimumAmbientLuminance)
|
||||
{
|
||||
// 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());
|
||||
else
|
||||
ambient *= targetBrightnessIncreaseFactor;
|
||||
}
|
||||
}
|
||||
|
||||
setAmbientColour(ambient);
|
||||
|
||||
osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell->mAmbi.mSunlight);
|
||||
mSunLight->setDiffuse(diffuse);
|
||||
|
@ -1103,9 +1143,47 @@ namespace MWRender
|
|||
else if (it->first == "General" && (it->second == "texture filter" ||
|
||||
it->second == "texture mipmap" ||
|
||||
it->second == "anisotropy"))
|
||||
{
|
||||
updateTextureFiltering();
|
||||
}
|
||||
else if (it->first == "Water")
|
||||
{
|
||||
mWater->processChangedSettings(changed);
|
||||
}
|
||||
else if (it->first == "Shaders" && it->second == "minimum interior brightness")
|
||||
{
|
||||
mMinimumAmbientLuminance = std::clamp(Settings::Manager::getFloat("minimum interior brightness", "Shaders"), 0.f, 1.f);
|
||||
if (MWMechanics::getPlayer().isInCell())
|
||||
configureAmbient(MWMechanics::getPlayer().getCell()->getCell());
|
||||
}
|
||||
else if (it->first == "Shaders" && (it->second == "light bounds multiplier" ||
|
||||
it->second == "maximum light distance" ||
|
||||
it->second == "light fade start" ||
|
||||
it->second == "max lights"))
|
||||
{
|
||||
auto* lightManager = static_cast<SceneUtil::LightManager*>(getLightRoot());
|
||||
lightManager->processChangedSettings(changed);
|
||||
|
||||
if (it->second == "max lights" && !lightManager->usingFFP())
|
||||
{
|
||||
mViewer->stopThreading();
|
||||
|
||||
lightManager->updateMaxLights();
|
||||
|
||||
auto defines = mResourceSystem->getSceneManager()->getShaderManager().getGlobalDefines();
|
||||
for (const auto& [name, key] : lightManager->getLightDefines())
|
||||
defines[name] = key;
|
||||
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
|
||||
|
||||
mSceneRoot->removeUpdateCallback(mStateUpdater);
|
||||
mStateUpdater = new StateUpdater;
|
||||
mSceneRoot->addUpdateCallback(mStateUpdater);
|
||||
mStateUpdater->setFogEnd(mViewDistance);
|
||||
updateAmbient();
|
||||
|
||||
mViewer->startThreading();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -296,6 +296,7 @@ namespace MWRender
|
|||
osg::ref_ptr<StateUpdater> mStateUpdater;
|
||||
|
||||
osg::Vec4f mAmbientColor;
|
||||
float mMinimumAmbientLuminance;
|
||||
float mNightEyeFactor;
|
||||
|
||||
float mNearClip;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <components/sceneutil/shadow.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/sceneutil/waterutil.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
|
@ -670,6 +671,9 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
|||
osg::ref_ptr<osg::Program> program (new osg::Program);
|
||||
program->addShader(vertexShader);
|
||||
program->addShader(fragmentShader);
|
||||
auto method = mResourceSystem->getSceneManager()->getLightingMethod();
|
||||
if (method == SceneUtil::LightingMethod::SingleUBO)
|
||||
program->addBindUniformBlock("LightBufferBinding", static_cast<int>(Shader::UBOBinding::LightBuffer));
|
||||
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
||||
|
||||
node->setStateSet(shaderStateset);
|
||||
|
|
15
components/misc/hash.hpp
Normal file
15
components/misc/hash.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef MISC_HASH_H
|
||||
#define MISC_HASH_H
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
/// Implemented similar to the boost::hash_combine
|
||||
template <class T>
|
||||
inline void hashCombine(std::size_t& seed, const T& v)
|
||||
{
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -226,6 +226,7 @@ namespace Resource
|
|||
, mAutoUseNormalMaps(false)
|
||||
, mAutoUseSpecularMaps(false)
|
||||
, mApplyLightingToEnvMaps(false)
|
||||
, mLightingMethod(SceneUtil::LightingMethod::FFP)
|
||||
, mConvertAlphaTestToAlphaToCoverage(false)
|
||||
, mInstanceCache(new MultiObjectCache)
|
||||
, mSharedStateManager(new SharedStateManager)
|
||||
|
@ -304,6 +305,16 @@ namespace Resource
|
|||
mApplyLightingToEnvMaps = apply;
|
||||
}
|
||||
|
||||
void SceneManager::setLightingMethod(SceneUtil::LightingMethod method)
|
||||
{
|
||||
mLightingMethod = method;
|
||||
}
|
||||
|
||||
SceneUtil::LightingMethod SceneManager::getLightingMethod() const
|
||||
{
|
||||
return mLightingMethod;
|
||||
}
|
||||
|
||||
void SceneManager::setConvertAlphaTestToAlphaToCoverage(bool convert)
|
||||
{
|
||||
mConvertAlphaTestToAlphaToCoverage = convert;
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "resourcemanager.hpp"
|
||||
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
class ImageManager;
|
||||
|
@ -105,6 +107,9 @@ namespace Resource
|
|||
|
||||
void setApplyLightingToEnvMaps(bool apply);
|
||||
|
||||
void setLightingMethod(SceneUtil::LightingMethod method);
|
||||
SceneUtil::LightingMethod getLightingMethod() const;
|
||||
|
||||
void setConvertAlphaTestToAlphaToCoverage(bool convert);
|
||||
|
||||
void setShaderPath(const std::string& path);
|
||||
|
@ -191,6 +196,7 @@ namespace Resource
|
|||
bool mAutoUseSpecularMaps;
|
||||
std::string mSpecularMapPattern;
|
||||
bool mApplyLightingToEnvMaps;
|
||||
SceneUtil::LightingMethod mLightingMethod;
|
||||
bool mConvertAlphaTestToAlphaToCoverage;
|
||||
|
||||
osg::ref_ptr<MultiObjectCache> mInstanceCache;
|
||||
|
|
|
@ -62,7 +62,8 @@ namespace SceneUtil
|
|||
mPhase = mPhase <= 0.5f ? 1.f : 0.25f;
|
||||
}
|
||||
|
||||
static_cast<SceneUtil::LightSource*>(node)->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * mBrightness);
|
||||
auto* lightSource = static_cast<SceneUtil::LightSource*>(node);
|
||||
lightSource->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * mBrightness * lightSource->getActorFade());
|
||||
|
||||
traverse(node, nv);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,6 +2,9 @@
|
|||
#define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H
|
||||
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
#include <osg/Light>
|
||||
|
||||
|
@ -9,6 +12,10 @@
|
|||
#include <osg/NodeVisitor>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
namespace osgUtil
|
||||
{
|
||||
class CullVisitor;
|
||||
|
@ -16,6 +23,17 @@ namespace osgUtil
|
|||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class LightBuffer;
|
||||
class StateSetGenerator;
|
||||
|
||||
enum class LightingMethod
|
||||
{
|
||||
FFP,
|
||||
PerObjectUniform,
|
||||
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
|
||||
|
@ -35,6 +53,8 @@ namespace SceneUtil
|
|||
|
||||
int mId;
|
||||
|
||||
float mActorFade;
|
||||
|
||||
public:
|
||||
|
||||
META_Node(SceneUtil, LightSource)
|
||||
|
@ -54,10 +74,20 @@ namespace SceneUtil
|
|||
mRadius = radius;
|
||||
}
|
||||
|
||||
void setActorFade(float alpha)
|
||||
{
|
||||
mActorFade = alpha;
|
||||
}
|
||||
|
||||
float getActorFade() const
|
||||
{
|
||||
return mActorFade;
|
||||
}
|
||||
|
||||
/// Get the osg::Light safe for modification in the given frame.
|
||||
/// @par May be used externally to animate the light's color/attenuation properties,
|
||||
/// and is used internally to synchronize the light's position with the position of the LightSource.
|
||||
osg::Light* getLight(unsigned int frame)
|
||||
osg::Light* getLight(size_t frame)
|
||||
{
|
||||
return mLight[frame % 2];
|
||||
}
|
||||
|
@ -83,31 +113,9 @@ namespace SceneUtil
|
|||
class LightManager : public osg::Group
|
||||
{
|
||||
public:
|
||||
|
||||
META_Node(SceneUtil, LightManager)
|
||||
|
||||
LightManager();
|
||||
|
||||
LightManager(const LightManager& copy, const osg::CopyOp& copyop);
|
||||
|
||||
/// @param mask This mask is compared with the current Camera's cull mask to determine if lighting is desired.
|
||||
/// 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 (unsigned int mask);
|
||||
|
||||
unsigned int getLightingMask() const;
|
||||
|
||||
/// Set the first light index that should be used by this manager, typically the number of directional lights in the scene.
|
||||
void setStartLight(int start);
|
||||
|
||||
int getStartLight() const;
|
||||
|
||||
/// Internal use only, called automatically by the LightManager's UpdateCallback
|
||||
void update();
|
||||
|
||||
/// Internal use only, called automatically by the LightSource's UpdateCallback
|
||||
void addLight(LightSource* lightSource, const osg::Matrixf& worldMat, unsigned int frameNum);
|
||||
static LightingMethod getLightingMethodFromString(const std::string& value);
|
||||
/// Returns string as used in settings file, or the empty string if the method is undefined
|
||||
static std::string getLightingMethodString(LightingMethod method);
|
||||
|
||||
struct LightSourceTransform
|
||||
{
|
||||
|
@ -115,36 +123,120 @@ namespace SceneUtil
|
|||
osg::Matrixf mWorldMatrix;
|
||||
};
|
||||
|
||||
const std::vector<LightSourceTransform>& getLights() const;
|
||||
|
||||
struct LightSourceViewBound
|
||||
{
|
||||
LightSource* mLightSource;
|
||||
osg::BoundingSphere mViewBound;
|
||||
};
|
||||
|
||||
const std::vector<LightSourceViewBound>& getLightsInViewSpace(osg::Camera* camera, const osg::RefMatrix* viewMatrix);
|
||||
using LightList = std::vector<const LightSourceViewBound*>;
|
||||
|
||||
typedef std::vector<const LightSourceViewBound*> LightList;
|
||||
META_Node(SceneUtil, LightManager)
|
||||
|
||||
osg::ref_ptr<osg::StateSet> getLightListStateSet(const LightList& lightList, unsigned int frameNum);
|
||||
LightManager(bool ffp = true);
|
||||
|
||||
LightManager(const LightManager& copy, const osg::CopyOp& copyop);
|
||||
|
||||
/// @param mask This mask is compared with the current Camera's cull mask to determine if lighting is desired.
|
||||
/// 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);
|
||||
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.
|
||||
void setStartLight(int start);
|
||||
int getStartLight() const;
|
||||
|
||||
/// Internal use only, called automatically by the LightManager's UpdateCallback
|
||||
void update(size_t frameNum);
|
||||
|
||||
/// Internal use only, called automatically by the LightSource's UpdateCallback
|
||||
void addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum);
|
||||
|
||||
const std::vector<LightSourceViewBound>& getLightsInViewSpace(osg::Camera* camera, const osg::RefMatrix* viewMatrix, size_t frameNum);
|
||||
|
||||
osg::ref_ptr<osg::StateSet> getLightListStateSet(const LightList& lightList, size_t frameNum, const osg::RefMatrix* viewMatrix);
|
||||
|
||||
void setSunlight(osg::ref_ptr<osg::Light> sun);
|
||||
osg::ref_ptr<osg::Light> getSunlight();
|
||||
|
||||
bool usingFFP() const;
|
||||
|
||||
LightingMethod getLightingMethod() const;
|
||||
|
||||
int getMaxLights() const;
|
||||
|
||||
int getMaxLightsInScene() const;
|
||||
|
||||
auto& getDummies() { return mDummies; }
|
||||
|
||||
auto& getLightIndexMap(size_t frameNum) { return mLightIndexMaps[frameNum%2]; }
|
||||
|
||||
auto& getLightBuffer(size_t frameNum) { return mLightBuffers[frameNum%2]; }
|
||||
|
||||
osg::Matrixf getSunlightBuffer(size_t frameNum) const { return mSunlightBuffers[frameNum%2]; }
|
||||
void setSunlightBuffer(const osg::Matrixf& buffer, size_t frameNum) { mSunlightBuffers[frameNum%2] = buffer; }
|
||||
|
||||
std::map<std::string, std::string> getLightDefines() const;
|
||||
|
||||
void processChangedSettings(const Settings::CategorySettingVector& changed);
|
||||
|
||||
/// Not thread safe, it is the responsibility of the caller to stop/start threading on the viewer
|
||||
void updateMaxLights();
|
||||
|
||||
private:
|
||||
// Lights collected from the scene graph. Only valid during the cull traversal.
|
||||
void initFFP(int targetLights);
|
||||
void initPerObjectUniform(int targetLights);
|
||||
void initSingleUBO(int targetLights);
|
||||
|
||||
void updateSettings();
|
||||
|
||||
void setLightingMethod(LightingMethod method);
|
||||
void setMaxLights(int value);
|
||||
|
||||
void updateGPUPointLight(int index, LightSource* lightSource, size_t frameNum, const osg::RefMatrix* viewMatrix);
|
||||
|
||||
std::vector<LightSourceTransform> mLights;
|
||||
|
||||
typedef std::vector<LightSourceViewBound> LightSourceViewBoundCollection;
|
||||
using LightSourceViewBoundCollection = std::vector<LightSourceViewBound>;
|
||||
std::map<osg::observer_ptr<osg::Camera>, LightSourceViewBoundCollection> mLightsInViewSpace;
|
||||
|
||||
// < Light list hash , StateSet >
|
||||
typedef std::map<size_t, osg::ref_ptr<osg::StateSet> > LightStateSetMap;
|
||||
using LightStateSetMap = std::map<size_t, osg::ref_ptr<osg::StateSet>>;
|
||||
LightStateSetMap mStateSetCache[2];
|
||||
|
||||
std::vector<osg::ref_ptr<osg::StateAttribute>> mDummies;
|
||||
|
||||
int mStartLight;
|
||||
|
||||
unsigned int mLightingMask;
|
||||
size_t mLightingMask;
|
||||
|
||||
osg::ref_ptr<osg::Light> mSun;
|
||||
|
||||
osg::ref_ptr<LightBuffer> mLightBuffers[2];
|
||||
|
||||
osg::Matrixf mSunlightBuffers[2];
|
||||
|
||||
// < Light ID , Buffer Index >
|
||||
using LightIndexMap = std::unordered_map<int, int>;
|
||||
LightIndexMap mLightIndexMaps[2];
|
||||
|
||||
std::unique_ptr<StateSetGenerator> mStateSetGenerator;
|
||||
|
||||
LightingMethod mLightingMethod;
|
||||
|
||||
float mPointLightRadiusMultiplier;
|
||||
float mPointLightFadeEnd;
|
||||
float mPointLightFadeStart;
|
||||
|
||||
int mMaxLights;
|
||||
|
||||
static constexpr auto mMaxLightsLowerLimit = 2;
|
||||
static constexpr auto mMaxLightsUpperLimit = 64;
|
||||
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
|
||||
|
@ -180,7 +272,7 @@ namespace SceneUtil
|
|||
|
||||
private:
|
||||
LightManager* mLightManager;
|
||||
unsigned int mLastFrameNumber;
|
||||
size_t mLastFrameNumber;
|
||||
LightManager::LightList mLightList;
|
||||
std::set<SceneUtil::LightSource*> mIgnoredLightSources;
|
||||
};
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace SceneUtil
|
|||
light->setQuadraticAttenuation(quadraticAttenuation);
|
||||
}
|
||||
|
||||
void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior)
|
||||
osg::ref_ptr<LightSource> addLight(osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior)
|
||||
{
|
||||
SceneUtil::FindByNameVisitor visitor("AttachLight");
|
||||
node->accept(visitor);
|
||||
|
@ -85,8 +85,9 @@ namespace SceneUtil
|
|||
attachTo = trans;
|
||||
}
|
||||
|
||||
osg::ref_ptr<LightSource> lightSource = createLightSource(esmLight, lightMask, isExterior);
|
||||
osg::ref_ptr<LightSource> lightSource = createLightSource(esmLight, lightMask, isExterior, osg::Vec4f(0,0,0,1));
|
||||
attachTo->addChild(lightSource);
|
||||
return lightSource;
|
||||
}
|
||||
|
||||
osg::ref_ptr<LightSource> createLightSource(const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient)
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace SceneUtil
|
|||
/// @param partsysMask Node mask to ignore when computing the sub graph's bounding box.
|
||||
/// @param lightMask Mask to assign to the newly created LightSource.
|
||||
/// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use.
|
||||
void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior);
|
||||
osg::ref_ptr<LightSource> addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior);
|
||||
|
||||
/// @brief Convert an ESM::Light to a SceneUtil::LightSource, and return it.
|
||||
/// @param esmLight The light definition coming from the game files containing radius, color, flicker, etc.
|
||||
|
|
|
@ -9,17 +9,28 @@
|
|||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace Shader
|
||||
{
|
||||
|
||||
ShaderManager::ShaderManager()
|
||||
: mLightingMethod(SceneUtil::LightingMethod::FFP)
|
||||
{
|
||||
}
|
||||
|
||||
void ShaderManager::setShaderPath(const std::string &path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
void ShaderManager::setLightingMethod(SceneUtil::LightingMethod method)
|
||||
{
|
||||
mLightingMethod = method;
|
||||
}
|
||||
|
||||
bool addLineDirectivesAfterConditionalBlocks(std::string& source)
|
||||
{
|
||||
for (size_t position = 0; position < source.length(); )
|
||||
|
@ -344,6 +355,8 @@ namespace Shader
|
|||
program->addShader(fragmentShader);
|
||||
program->addBindAttribLocation("aOffset", 6);
|
||||
program->addBindAttribLocation("aRotation", 7);
|
||||
if (mLightingMethod == SceneUtil::LightingMethod::SingleUBO)
|
||||
program->addBindUniformBlock("LightBufferBinding", static_cast<int>(UBOBinding::LightBuffer));
|
||||
found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first;
|
||||
}
|
||||
return found->second;
|
||||
|
|
|
@ -11,16 +11,38 @@
|
|||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
|
||||
namespace Resource
|
||||
{
|
||||
class SceneManager;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
enum class LightingMethod;
|
||||
}
|
||||
|
||||
namespace Shader
|
||||
{
|
||||
|
||||
enum class UBOBinding
|
||||
{
|
||||
LightBuffer
|
||||
};
|
||||
|
||||
/// @brief Reads shader template files and turns them into a concrete shader, based on a list of define's.
|
||||
/// @par Shader templates can get the value of a define with the syntax @define.
|
||||
class ShaderManager
|
||||
{
|
||||
public:
|
||||
|
||||
ShaderManager();
|
||||
|
||||
void setShaderPath(const std::string& path);
|
||||
|
||||
void setLightingMethod(SceneUtil::LightingMethod method);
|
||||
|
||||
typedef std::map<std::string, std::string> DefineMap;
|
||||
|
||||
/// Create or retrieve a shader instance.
|
||||
|
@ -59,6 +81,8 @@ namespace Shader
|
|||
typedef std::map<std::pair<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> >, osg::ref_ptr<osg::Program> > ProgramMap;
|
||||
ProgramMap mPrograms;
|
||||
|
||||
SceneUtil::LightingMethod mLightingMethod;
|
||||
|
||||
std::mutex mMutex;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -148,6 +148,115 @@ By default, the fog becomes thicker proportionally to your distance from the cli
|
|||
This setting makes the fog use the actual eye point distance (or so called Euclidean distance) to calculate the fog, which makes the fog look less artificial, especially if you have a wide FOV.
|
||||
Note that the rendering will act as if you have 'force shaders' option enabled with this on, which means that shaders will be used to render all objects and the terrain.
|
||||
|
||||
lighting method
|
||||
---------------
|
||||
|
||||
:Type: string
|
||||
:Range: legacy|shaders compatibility|shaders
|
||||
:Default: default
|
||||
|
||||
Sets the internal handling of light sources.
|
||||
|
||||
'legacy' is restricted to 8 lights per object and emulates fixed function
|
||||
pipeline compatible lighting.
|
||||
|
||||
'shaders compatibility' removes the light limit controllable through :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.
|
||||
|
||||
'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-5.0
|
||||
:Default: 1.65
|
||||
|
||||
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.
|
||||
|
||||
This setting has no effect if :ref:`lighting method` is 'legacy'.
|
||||
|
||||
maximum light distance
|
||||
----------------------
|
||||
|
||||
:Type: float
|
||||
: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.
|
||||
|
||||
This setting has no effect if :ref:`lighting method` is 'legacy'.
|
||||
|
||||
light fade start
|
||||
----------------
|
||||
|
||||
:Type: float
|
||||
:Range: 0.0-1.0
|
||||
:Default: 0.85
|
||||
|
||||
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 :ref:`maximum light distance` is non-positive
|
||||
or :ref:`lighting method` is 'legacy'.
|
||||
|
||||
max lights
|
||||
----------
|
||||
|
||||
:Type: integer
|
||||
:Range: 2-64
|
||||
:Default: 8
|
||||
|
||||
Sets the maximum number of lights that each object can receive lighting from.
|
||||
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'.
|
||||
|
||||
minimum interior brightness
|
||||
------------------------
|
||||
|
||||
:Type: float
|
||||
:Range: 0.0-1.0
|
||||
:Default: 0.08
|
||||
|
||||
Sets the minimum interior ambient brightness for interior cells when
|
||||
:ref:`lighting method` is not 'legacy'. A consequence of the new lighting system
|
||||
is that interiors will sometimes be darker since light sources now have sensible
|
||||
fall-offs. A couple solutions are to either add more lights or increase their
|
||||
radii to compensate, but these require content changes. For best results it is
|
||||
recommended to set this to 0.0 to retain the colors that level designers
|
||||
intended. If brighter interiors are wanted, however, this setting should be
|
||||
increased. Note, it is advised to keep this number small (< 0.1) to avoid the
|
||||
aforementioned changes in visuals.
|
||||
|
||||
This setting has no effect if :ref:`lighting method` is 'legacy'.
|
||||
|
||||
antialias alpha test
|
||||
---------------------------------------
|
||||
|
||||
|
|
|
@ -457,6 +457,114 @@
|
|||
</Widget>
|
||||
|
||||
</Widget>
|
||||
|
||||
<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>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
<!--
|
||||
<Widget type="TabItem" skin="" position="0 28 352 268">
|
||||
<Widget type="Widget" skin="" position="0 28 352 268">
|
||||
|
|
|
@ -446,6 +446,38 @@ 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, 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 slower on weaker hardware when
|
||||
# 'force per pixel lighting' is enabled.
|
||||
lighting method = shaders compatibility
|
||||
|
||||
# Sets the bounding sphere multiplier of light sources if 'lighting method' is
|
||||
# not 'legacy'. These are used to determine if an object should receive
|
||||
# lighting. Higher values will allow for smoother transitions of light sources,
|
||||
# but may carry a performance cost and requires a higher number of 'max lights'
|
||||
# set.
|
||||
light bounds multiplier = 1.65
|
||||
|
||||
# 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.
|
||||
# When 'lighting method' is set to 'legacy', this setting will have no effect.
|
||||
max lights = 8
|
||||
|
||||
# Sets minimum ambient brightness of interior cells. Levels below this threshold will have their
|
||||
# ambient values adjusted to balance the darker interiors.
|
||||
# When 'lighting method' is set to 'legacy', this setting will have no effect.
|
||||
minimum interior brightness = 0.08
|
||||
|
||||
# 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.
|
||||
# When MSAA is off, this setting will have no visible effect, but might have a performance cost.
|
||||
|
|
|
@ -18,6 +18,7 @@ set(SHADER_FILES
|
|||
terrain_vertex.glsl
|
||||
terrain_fragment.glsl
|
||||
lighting.glsl
|
||||
lighting_util.glsl
|
||||
parallax.glsl
|
||||
s360_fragment.glsl
|
||||
s360_vertex.glsl
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#version 120
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
#endif
|
||||
|
||||
#if @useGPUShader4
|
||||
#extension GL_EXT_gpu_shader4: require
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#version 120
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
#endif
|
||||
|
||||
#if @useGPUShader4
|
||||
#extension GL_EXT_gpu_shader4: require
|
||||
#endif
|
||||
|
||||
#define GROUNDCOVER
|
||||
|
||||
attribute vec4 aOffset;
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
#define MAX_LIGHTS 8
|
||||
#include "lighting_util.glsl"
|
||||
|
||||
void perLight(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal)
|
||||
void perLightSun(out vec3 diffuseOut, vec3 viewPos, vec3 viewNormal)
|
||||
{
|
||||
vec3 lightDir = gl_LightSource[lightIndex].position.xyz - viewPos * gl_LightSource[lightIndex].position.w;
|
||||
float 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);
|
||||
vec3 lightDir = normalize(lcalcPosition(0));
|
||||
float lambert = dot(viewNormal.xyz, lightDir);
|
||||
|
||||
ambientOut = gl_LightSource[lightIndex].ambient.xyz * illumination;
|
||||
|
||||
float lambert = dot(viewNormal.xyz, lightDir) * illumination;
|
||||
#ifndef GROUNDCOVER
|
||||
lambert = max(lambert, 0.0);
|
||||
#else
|
||||
|
@ -21,7 +16,46 @@ void perLight(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 vie
|
|||
}
|
||||
lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0);
|
||||
#endif
|
||||
diffuseOut = gl_LightSource[lightIndex].diffuse.xyz * lambert;
|
||||
|
||||
diffuseOut = lcalcDiffuse(0).xyz * lambert;
|
||||
}
|
||||
|
||||
void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal)
|
||||
{
|
||||
vec3 lightPos = lcalcPosition(lightIndex) - viewPos;
|
||||
float lightDistance = length(lightPos);
|
||||
|
||||
// cull non-FFP point lighting by radius, light is guaranteed to not fall outside this bound with our cutoff
|
||||
#if !@lightingMethodFFP
|
||||
float radius = lcalcRadius(lightIndex);
|
||||
|
||||
if (lightDistance > radius * 2.0)
|
||||
{
|
||||
ambientOut = vec3(0.0);
|
||||
diffuseOut = vec3(0.0);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
lightPos = normalize(lightPos);
|
||||
|
||||
float illumination = lcalcIllumination(lightIndex, lightDistance);
|
||||
ambientOut = lcalcAmbient(lightIndex) * illumination;
|
||||
float lambert = dot(viewNormal.xyz, lightPos) * illumination;
|
||||
|
||||
#ifndef GROUNDCOVER
|
||||
lambert = max(lambert, 0.0);
|
||||
#else
|
||||
float eyeCosine = dot(normalize(viewPos), viewNormal.xyz);
|
||||
if (lambert < 0.0)
|
||||
{
|
||||
lambert = -lambert;
|
||||
eyeCosine = -eyeCosine;
|
||||
}
|
||||
lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0);
|
||||
#endif
|
||||
|
||||
diffuseOut = lcalcDiffuse(lightIndex) * lambert;
|
||||
}
|
||||
|
||||
#if PER_PIXEL_LIGHTING
|
||||
|
@ -31,31 +65,35 @@ void doLighting(vec3 viewPos, vec3 viewNormal, out vec3 diffuseLight, out vec3 a
|
|||
#endif
|
||||
{
|
||||
vec3 ambientOut, diffuseOut;
|
||||
// This light gets added a second time in the loop to fix Mesa users' slowdown, so we need to negate its contribution here.
|
||||
perLight(ambientOut, diffuseOut, 0, viewPos, viewNormal);
|
||||
|
||||
perLightSun(diffuseOut, viewPos, viewNormal);
|
||||
ambientLight = gl_LightModel.ambient.xyz;
|
||||
#if PER_PIXEL_LIGHTING
|
||||
diffuseLight = diffuseOut * shadowing - diffuseOut;
|
||||
diffuseLight = diffuseOut * shadowing;
|
||||
#else
|
||||
shadowDiffuse = diffuseOut;
|
||||
diffuseLight = -diffuseOut;
|
||||
diffuseLight = vec3(0.0);
|
||||
#endif
|
||||
ambientLight = gl_LightModel.ambient.xyz;
|
||||
for (int i=0; i<MAX_LIGHTS; ++i)
|
||||
|
||||
for (int i = @startLight; i < @endLight; ++i)
|
||||
{
|
||||
perLight(ambientOut, diffuseOut, i, viewPos, viewNormal);
|
||||
#if @lightingMethodUBO
|
||||
perLightPoint(ambientOut, diffuseOut, PointLightIndex[i], viewPos, viewNormal);
|
||||
#else
|
||||
perLightPoint(ambientOut, diffuseOut, i, viewPos, viewNormal);
|
||||
#endif
|
||||
ambientLight += ambientOut;
|
||||
diffuseLight += diffuseOut;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
vec3 getSpecular(vec3 viewNormal, vec3 viewDirection, float shininess, vec3 matSpec)
|
||||
{
|
||||
vec3 lightDir = normalize(gl_LightSource[0].position.xyz);
|
||||
vec3 lightDir = normalize(lcalcPosition(0));
|
||||
float NdotL = dot(viewNormal, lightDir);
|
||||
if (NdotL <= 0.0)
|
||||
return vec3(0.0);
|
||||
vec3 halfVec = normalize(lightDir - viewDirection);
|
||||
float NdotH = dot(viewNormal, halfVec);
|
||||
return pow(max(NdotH, 0.0), max(1e-4, shininess)) * gl_LightSource[0].specular.xyz * matSpec;
|
||||
return pow(max(NdotH, 0.0), max(1e-4, shininess)) * lcalcSpecular(0).xyz * matSpec;
|
||||
}
|
||||
|
|
131
files/shaders/lighting_util.glsl
Normal file
131
files/shaders/lighting_util.glsl
Normal file
|
@ -0,0 +1,131 @@
|
|||
#if !@lightingMethodFFP
|
||||
float quickstep(float x)
|
||||
{
|
||||
x = clamp(x, 0.0, 1.0);
|
||||
x = 1.0 - x*x;
|
||||
x = 1.0 - x*x;
|
||||
return x;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if @lightingMethodUBO
|
||||
|
||||
const int mask = int(0xff);
|
||||
const ivec4 shift = ivec4(int(0), int(8), int(16), int(24));
|
||||
|
||||
vec3 unpackRGB(int data)
|
||||
{
|
||||
return vec3( (float(((data >> shift.x) & mask)) / 255.0)
|
||||
,(float(((data >> shift.y) & mask)) / 255.0)
|
||||
,(float(((data >> shift.z) & mask)) / 255.0));
|
||||
}
|
||||
|
||||
vec4 unpackRGBA(int data)
|
||||
{
|
||||
return vec4( (float(((data >> shift.x) & mask)) / 255.0)
|
||||
,(float(((data >> shift.y) & mask)) / 255.0)
|
||||
,(float(((data >> shift.z) & mask)) / 255.0)
|
||||
,(float(((data >> shift.w) & mask)) / 255.0));
|
||||
}
|
||||
|
||||
/* Layout:
|
||||
packedColors: 8-bit unsigned RGB packed as (diffuse, ambient, specular).
|
||||
sign bit is stored in unused alpha component
|
||||
attenuation: constant, linear, quadratic, light radius (as defined in content)
|
||||
*/
|
||||
struct LightData
|
||||
{
|
||||
ivec4 packedColors;
|
||||
vec4 position;
|
||||
vec4 attenuation;
|
||||
};
|
||||
|
||||
uniform int PointLightIndex[@maxLights];
|
||||
uniform int PointLightCount;
|
||||
|
||||
// Defaults to shared layout. If we ever move to GLSL 140, std140 layout should be considered
|
||||
uniform LightBufferBinding
|
||||
{
|
||||
LightData LightBuffer[@maxLightsInScene];
|
||||
};
|
||||
|
||||
#elif @lightingMethodPerObjectUniform
|
||||
|
||||
/* Layout:
|
||||
--------------------------------------- -----------
|
||||
| pos_x | ambi_r | diff_r | spec_r |
|
||||
| pos_y | ambi_g | diff_g | spec_g |
|
||||
| pos_z | ambi_b | diff_b | spec_b |
|
||||
| att_c | att_l | att_q | radius/spec_a |
|
||||
--------------------------------------------------
|
||||
*/
|
||||
uniform mat4 LightBuffer[@maxLights];
|
||||
uniform int PointLightCount;
|
||||
|
||||
#endif
|
||||
|
||||
#if !@lightingMethodFFP
|
||||
float lcalcRadius(int lightIndex)
|
||||
{
|
||||
#if @lightingMethodPerObjectUniform
|
||||
return @getLight[lightIndex][3].w;
|
||||
#else
|
||||
return @getLight[lightIndex].attenuation.w;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
float lcalcIllumination(int lightIndex, float lightDistance)
|
||||
{
|
||||
#if @lightingMethodPerObjectUniform
|
||||
float illumination = clamp(1.0 / (@getLight[lightIndex][0].w + @getLight[lightIndex][1].w * lightDistance + @getLight[lightIndex][2].w * lightDistance * lightDistance), 0.0, 1.0);
|
||||
return (illumination * (1.0 - quickstep((lightDistance / lcalcRadius(lightIndex)) - 1.0)));
|
||||
#elif @lightingMethodUBO
|
||||
float illumination = clamp(1.0 / (@getLight[lightIndex].attenuation.x + @getLight[lightIndex].attenuation.y * lightDistance + @getLight[lightIndex].attenuation.z * lightDistance * lightDistance), 0.0, 1.0);
|
||||
return (illumination * (1.0 - quickstep((lightDistance / lcalcRadius(lightIndex)) - 1.0)));
|
||||
#else
|
||||
return clamp(1.0 / (@getLight[lightIndex].constantAttenuation + @getLight[lightIndex].linearAttenuation * lightDistance + @getLight[lightIndex].quadraticAttenuation * lightDistance * lightDistance), 0.0, 1.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 lcalcPosition(int lightIndex)
|
||||
{
|
||||
#if @lightingMethodPerObjectUniform
|
||||
return @getLight[lightIndex][0].xyz;
|
||||
#else
|
||||
return @getLight[lightIndex].position.xyz;
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 lcalcDiffuse(int lightIndex)
|
||||
{
|
||||
#if @lightingMethodPerObjectUniform
|
||||
return @getLight[lightIndex][2].xyz;
|
||||
#elif @lightingMethodUBO
|
||||
return unpackRGB(@getLight[lightIndex].packedColors.x) * float(@getLight[lightIndex].packedColors.w);
|
||||
#else
|
||||
return @getLight[lightIndex].diffuse.xyz;
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 lcalcAmbient(int lightIndex)
|
||||
{
|
||||
#if @lightingMethodPerObjectUniform
|
||||
return @getLight[lightIndex][1].xyz;
|
||||
#elif @lightingMethodUBO
|
||||
return unpackRGB(@getLight[lightIndex].packedColors.y);
|
||||
#else
|
||||
return @getLight[lightIndex].ambient.xyz;
|
||||
#endif
|
||||
}
|
||||
|
||||
vec4 lcalcSpecular(int lightIndex)
|
||||
{
|
||||
#if @lightingMethodPerObjectUniform
|
||||
return @getLight[lightIndex][3];
|
||||
#elif @lightingMethodUBO
|
||||
return unpackRGBA(@getLight[lightIndex].packedColors.z);
|
||||
#else
|
||||
return @getLight[lightIndex].specular;
|
||||
#endif
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
#version 120
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
#endif
|
||||
|
||||
#if @useGPUShader4
|
||||
#extension GL_EXT_gpu_shader4: require
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#version 120
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
#endif
|
||||
|
||||
#if @useGPUShader4
|
||||
#extension GL_EXT_gpu_shader4: require
|
||||
#endif
|
||||
|
||||
#if @diffuseMap
|
||||
varying vec2 diffuseMapUV;
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#version 120
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
#endif
|
||||
|
||||
#if @useGPUShader4
|
||||
#extension GL_EXT_gpu_shader4: require
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#version 120
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
#endif
|
||||
|
||||
#if @useGPUShader4
|
||||
#extension GL_EXT_gpu_shader4: require
|
||||
#endif
|
||||
|
||||
#if @diffuseMap
|
||||
varying vec2 diffuseMapUV;
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#version 120
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
#endif
|
||||
|
||||
#if @useGPUShader4
|
||||
#extension GL_EXT_gpu_shader4: require
|
||||
#endif
|
||||
|
||||
varying vec2 uv;
|
||||
|
||||
uniform sampler2D diffuseMap;
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#version 120
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
#endif
|
||||
|
||||
#if @useGPUShader4
|
||||
#extension GL_EXT_gpu_shader4: require
|
||||
#endif
|
||||
|
||||
varying vec2 uv;
|
||||
varying float euclideanDepth;
|
||||
varying float linearDepth;
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
#version 120
|
||||
|
||||
#if @useUBO
|
||||
#extension GL_ARB_uniform_buffer_object : require
|
||||
#endif
|
||||
|
||||
#if @useGPUShader4
|
||||
#extension GL_EXT_gpu_shader4: require
|
||||
#endif
|
||||
|
||||
#define REFRACTION @refraction_enabled
|
||||
|
||||
// Inspired by Blender GLSL Water by martinsh ( https://devlog-martinsh.blogspot.de/2012/07/waterundewater-shader-wip.html )
|
||||
|
@ -142,7 +150,10 @@ uniform vec3 nodePosition;
|
|||
|
||||
uniform float rainIntensity;
|
||||
|
||||
#define PER_PIXEL_LIGHTING 0
|
||||
|
||||
#include "shadows_fragment.glsl"
|
||||
#include "lighting.glsl"
|
||||
|
||||
float frustumDepth;
|
||||
|
||||
|
@ -192,8 +203,7 @@ void main(void)
|
|||
normal3 * midWaves.y + normal4 * smallWaves.x + normal5 * smallWaves.y + rippleAdd);
|
||||
normal = normalize(vec3(-normal.x * bump, -normal.y * bump, normal.z));
|
||||
|
||||
vec3 lVec = normalize((gl_ModelViewMatrixInverse * vec4(gl_LightSource[0].position.xyz, 0.0)).xyz);
|
||||
|
||||
vec3 lVec = normalize((gl_ModelViewMatrixInverse * vec4(lcalcPosition(0).xyz, 0.0)).xyz);
|
||||
vec3 cameraPos = (gl_ModelViewMatrixInverse * vec4(0,0,0,1)).xyz;
|
||||
vec3 vVec = normalize(position.xyz - cameraPos.xyz);
|
||||
|
||||
|
@ -228,6 +238,8 @@ void main(void)
|
|||
|
||||
vec3 waterColor = WATER_COLOR * sunFade;
|
||||
|
||||
vec4 sunSpec = lcalcSpecular(0);
|
||||
|
||||
#if REFRACTION
|
||||
// refraction
|
||||
vec3 refraction = texture2D(refractionMap, screenCoords - screenCoordsOffset).rgb;
|
||||
|
@ -247,11 +259,11 @@ void main(void)
|
|||
vec3 scatterColour = mix(SCATTER_COLOUR*vec3(1.0,0.4,0.0), SCATTER_COLOUR, clamp(1.0-exp(-sunHeight*SUN_EXT), 0.0, 1.0));
|
||||
vec3 lR = reflect(lVec, lNormal);
|
||||
float lightScatter = clamp(dot(lVec,lNormal)*0.7+0.3, 0.0, 1.0) * clamp(dot(lR, vVec)*2.0-1.2, 0.0, 1.0) * SCATTER_AMOUNT * sunFade * clamp(1.0-exp(-sunHeight), 0.0, 1.0);
|
||||
gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * gl_LightSource[0].specular.xyz + vec3(rainRipple.w) * 0.2;
|
||||
gl_FragData[0].xyz = mix( mix(refraction, scatterColour, lightScatter), reflection, fresnel) + specular * sunSpec.xyz + vec3(rainRipple.w) * 0.2;
|
||||
gl_FragData[0].w = 1.0;
|
||||
#else
|
||||
gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * gl_LightSource[0].specular.xyz + vec3(rainRipple.w) * 0.7;
|
||||
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);
|
||||
gl_FragData[0].xyz = mix(reflection, waterColor, (1.0-fresnel)*0.5) + specular * sunSpec.xyz + vec3(rainRipple.w) * 0.7;
|
||||
gl_FragData[0].w = clamp(fresnel*6.0 + specular * sunSpec.w, 0.0, 1.0); //clamp(fresnel*2.0 + specular * gl_LightSource[0].specular.w, 0.0, 1.0);
|
||||
#endif
|
||||
|
||||
// fog
|
||||
|
|
|
@ -283,6 +283,36 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<layout class="QHBoxLayout" name="lightingMethodLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="lightingMethodLabel">
|
||||
<property name="text">
|
||||
<string>Lighting Method:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="lightingMethodComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>legacy</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>shaders compatibility</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>shaders</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="smoothMovementCheckBox">
|
||||
<property name="toolTip">
|
||||
|
|
Loading…
Reference in a new issue