Add shared UBO

pull/3065/head
glassmancody.info 4 years ago
parent c5ea966f24
commit 9d9074c244

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

@ -72,6 +72,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f)
mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager; SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager;
lightMgr->setStartLight(1);
lightMgr->setLightingMask(Mask_Lighting); lightMgr->setLightingMask(Mask_Lighting);
mRootNode = lightMgr; mRootNode = lightMgr;

@ -1613,7 +1613,7 @@ namespace MWRender
{ {
bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior(); bool exterior = mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior();
SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior); SceneUtil::addLight(parent, esmLight, Mask_ParticleSystem, Mask_Lighting, exterior, mResourceSystem->getSceneManager()->getFFPLighting());
} }
void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture) void Animation::addEffect (const std::string& model, int effectId, bool loop, const std::string& bonename, const std::string& texture)

@ -213,9 +213,11 @@ namespace MWRender
resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders")); resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders"));
resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders")); resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders"));
resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders")); resourceSystem->getSceneManager()->setApplyLightingToEnvMaps(Settings::Manager::getBool("apply lighting to environment maps", "Shaders"));
resourceSystem->getSceneManager()->setFFPLighting(clampLighting || !forceShaders || !SceneUtil::LightManager::queryNonFFPLightingSupport()); bool useFFP = clampLighting || !forceShaders || !SceneUtil::LightManager::queryNonFFPLightingSupport();
resourceSystem->getSceneManager()->setFFPLighting(useFFP);
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(mResourceSystem->getSceneManager()->getFFPLighting()); resourceSystem->getSceneManager()->getShaderManager().setFFPLighting(useFFP);
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager(useFFP);
sceneRoot->setLightingMask(Mask_Lighting); sceneRoot->setLightingMask(Mask_Lighting);
mSceneRoot = sceneRoot; mSceneRoot = sceneRoot;
sceneRoot->setStartLight(1); sceneRoot->setStartLight(1);

@ -646,7 +646,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
program->addShader(vertexShader); program->addShader(vertexShader);
program->addShader(fragmentShader); program->addShader(fragmentShader);
if (!mResourceSystem->getSceneManager()->getFFPLighting()) if (!mResourceSystem->getSceneManager()->getFFPLighting())
program->addBindUniformBlock("SunlightBuffer", 9); program->addBindUniformBlock("SunlightBuffer", 0);
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON); shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
node->setStateSet(shaderStateset); node->setStateSet(shaderStateset);

@ -220,7 +220,7 @@ namespace Resource
SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager)
: ResourceManager(vfs) : ResourceManager(vfs)
, mShaderManager(new Shader::ShaderManager(this)) , mShaderManager(new Shader::ShaderManager)
, mForceShaders(false) , mForceShaders(false)
, mClampLighting(true) , mClampLighting(true)
, mAutoUseNormalMaps(false) , mAutoUseNormalMaps(false)

@ -11,13 +11,14 @@
namespace SceneUtil namespace SceneUtil
{ {
LightController::LightController() LightController::LightController(bool useFFPLighting)
: mType(LT_Normal) : mType(LT_Normal)
, mPhase(0.25f + Misc::Rng::rollClosedProbability() * 0.75f) , mPhase(0.25f + Misc::Rng::rollClosedProbability() * 0.75f)
, mBrightness(0.675f) , mBrightness(0.675f)
, mStartTime(0.0) , mStartTime(0.0)
, mLastTime(0.0) , mLastTime(0.0)
, mTicksToAdvance(0.f) , mTicksToAdvance(0.f)
, mFFPLighting(useFFPLighting)
{ {
} }
@ -62,7 +63,11 @@ namespace SceneUtil
mPhase = mPhase <= 0.5f ? 1.f : 0.25f; 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);
if (mFFPLighting)
lightSource->getLight(nv->getTraversalNumber())->setDiffuse(mDiffuseColor * mBrightness);
else
lightSource->setBrightness(nv->getTraversalNumber(), mBrightness);
traverse(node, nv); traverse(node, nv);
} }

@ -20,7 +20,7 @@ namespace SceneUtil
LT_PulseSlow LT_PulseSlow
}; };
LightController(); LightController(bool useFFPLighting=true);
void setType(LightType type); void setType(LightType type);
@ -36,6 +36,7 @@ namespace SceneUtil
double mStartTime; double mStartTime;
double mLastTime; double mLastTime;
float mTicksToAdvance; float mTicksToAdvance;
bool mFFPLighting;
}; };
} }

@ -1,6 +1,7 @@
#include "lightmanager.hpp" #include "lightmanager.hpp"
#include <osg/BufferObject> #include <osg/BufferObject>
#include <osg/BufferIndexBinding>
#include <osgUtil/CullVisitor> #include <osgUtil/CullVisitor>
@ -12,6 +13,8 @@
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include "apps/openmw/mwrender/vismask.hpp"
namespace namespace
{ {
/* similar to the boost::hash_combine */ /* similar to the boost::hash_combine */
@ -32,6 +35,10 @@ namespace SceneUtil
{ {
static int sLightId = 0; static int sLightId = 0;
////////////////////////////////////////////////////////////////////////////////
// Internal Data Structures
////////////////////////////////////////////////////////////////////////////////
class LightBuffer : public osg::Referenced class LightBuffer : public osg::Referenced
{ {
public: public:
@ -59,11 +66,6 @@ namespace SceneUtil
mDirty = false; mDirty = false;
} }
int blockSize() const
{
return mData->getNumElements() * sizeof(GL_FLOAT) * osg::Vec4::num_components;
}
protected: protected:
size_t mNumElements; size_t mNumElements;
osg::ref_ptr<osg::Vec4Array> mData; osg::ref_ptr<osg::Vec4Array> mData;
@ -114,7 +116,7 @@ namespace SceneUtil
{ {
public: public:
enum Type {Diffuse, Ambient, Position, Attenuation}; enum Type {Position, Diffuse, Ambient, Attenuation};
PointLightBuffer(int count) PointLightBuffer(int count)
: LightBuffer(4, count) : LightBuffer(4, count)
@ -124,9 +126,10 @@ namespace SceneUtil
{ {
if (getValue(index, type) == value) if (getValue(index, type) == value)
return; return;
(*mData)[mNumElements * index + type] = value; int indexOffset = mNumElements * index + type;
(*mData)[indexOffset] = value;
mDirty = true; mDirty = true;
} }
@ -155,12 +158,16 @@ namespace SceneUtil
return &cacheVector[contextid]; return &cacheVector[contextid];
} }
////////////////////////////////////////////////////////////////////////////////
// State Attributes
////////////////////////////////////////////////////////////////////////////////
SunlightStateAttribute::SunlightStateAttribute() SunlightStateAttribute::SunlightStateAttribute()
: mBuffer(new SunlightBuffer) : mBuffer(new SunlightBuffer)
{ {
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject; osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
mBuffer->getData()->setBufferObject(ubo); mBuffer->getData()->setBufferObject(ubo);
mUbb = new osg::UniformBufferBinding(9 , mBuffer->getData().get(), 0, mBuffer->blockSize()); mUbb = new osg::UniformBufferBinding(0, mBuffer->getData().get(), 0, mBuffer->getData()->getTotalDataSize());
} }
SunlightStateAttribute::SunlightStateAttribute(const SunlightStateAttribute &copy, const osg::CopyOp &copyop) SunlightStateAttribute::SunlightStateAttribute(const SunlightStateAttribute &copy, const osg::CopyOp &copyop)
@ -235,56 +242,6 @@ namespace SceneUtil
osg::Vec4f mnullptr; osg::Vec4f mnullptr;
}; };
class LightStateAttribute : public osg::StateAttribute
{
public:
LightStateAttribute() : mBuffer(nullptr) {}
LightStateAttribute(const std::vector<osg::ref_ptr<osg::Light> >& lights, const osg::ref_ptr<PointLightBuffer>& buffer) : mLights(lights), mBuffer(buffer) {}
LightStateAttribute(const LightStateAttribute& copy,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY)
: osg::StateAttribute(copy,copyop), mLights(copy.mLights), mBuffer(copy.mBuffer) {}
int compare(const StateAttribute &sa) const override
{
throw std::runtime_error("LightStateAttribute::compare: unimplemented");
}
META_StateAttribute(NifOsg, LightStateAttribute, osg::StateAttribute::LIGHT)
void apply(osg::State& state) const override
{
if (mLights.empty())
return;
osg::Matrix modelViewMatrix = state.getModelViewMatrix();
state.applyModelViewMatrix(state.getInitialViewMatrix());
if (mBuffer)
{
for (size_t i = 0; i < mLights.size(); ++i)
{
mBuffer->setValue(i, PointLightBuffer::Diffuse, mLights[i]->getDiffuse());
mBuffer->setValue(i, PointLightBuffer::Ambient, mLights[i]->getAmbient());
mBuffer->setValue(i, PointLightBuffer::Position, mLights[i]->getPosition() * state.getModelViewMatrix());
mBuffer->setValue(i, PointLightBuffer::Attenuation,
osg::Vec4(mLights[i]->getConstantAttenuation(),
mLights[i]->getLinearAttenuation(),
mLights[i]->getQuadraticAttenuation(), 0.0));
}
if (mBuffer && mBuffer->isDirty())
mBuffer->dirty();
}
state.applyModelViewMatrix(modelViewMatrix);
}
private:
std::vector<osg::ref_ptr<osg::Light>> mLights;
osg::ref_ptr<PointLightBuffer> mBuffer;
};
class FFPLightStateAttribute : public osg::StateAttribute class FFPLightStateAttribute : public osg::StateAttribute
{ {
public: public:
@ -368,6 +325,10 @@ namespace SceneUtil
return nullptr; return nullptr;
} }
////////////////////////////////////////////////////////////////////////////////
// Node Callbacks
////////////////////////////////////////////////////////////////////////////////
// Set on a LightSource. Adds the light source to its light manager for the current frame. // Set on a LightSource. Adds the light source to its light manager for the current frame.
// This allows us to keep track of the current lights in the scene graph without tying creation & destruction to the manager. // This allows us to keep track of the current lights in the scene graph without tying creation & destruction to the manager.
class CollectLightCallback : public osg::NodeCallback class CollectLightCallback : public osg::NodeCallback
@ -460,6 +421,42 @@ namespace SceneUtil
size_t mLastFrameNumber; size_t mLastFrameNumber;
}; };
class LightManagerStateAttribute : public osg::StateAttribute
{
public:
LightManagerStateAttribute() : mLightManager(nullptr) {}
LightManagerStateAttribute(LightManager* lightManager) : mLightManager(lightManager) {}
LightManagerStateAttribute(const LightManagerStateAttribute& 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("LightManagerStateAttribute::compare: unimplemented");
}
META_StateAttribute(NifOsg, LightManagerStateAttribute, osg::StateAttribute::LIGHT)
void apply(osg::State& state) const override
{
osg::Matrix modelViewMatrix = state.getModelViewMatrix();
state.applyModelViewMatrix(state.getInitialViewMatrix());
for (size_t i = 0; i < mLightManager->mPointLightProxyData.size(); i++)
{
auto& data = mLightManager->mPointLightProxyData[i];
auto pos = data.mPosition * state.getInitialViewMatrix();
pos[3] = data.mBrightness;
mLightManager->mPointBuffer->setValue(i, PointLightBuffer::Position, pos);
}
state.applyModelViewMatrix(modelViewMatrix);
mLightManager->mPointBuffer->dirty();
}
LightManager* mLightManager;
};
LightManager::LightManager(bool ffp) LightManager::LightManager(bool ffp)
: mStartLight(0) : mStartLight(0)
, mLightingMask(~0u) , mLightingMask(~0u)
@ -467,28 +464,44 @@ namespace SceneUtil
, mSunBuffer(nullptr) , mSunBuffer(nullptr)
, mFFP(ffp) , mFFP(ffp)
{ {
setUpdateCallback(new LightManagerUpdateCallback);
if (usingFFP()) if (usingFFP())
{ {
for (int i=0; i<getMaxLights(); ++i) for (int i=0; i<getMaxLights(); ++i)
mDummies.push_back(new FFPLightStateAttribute(i, std::vector<osg::ref_ptr<osg::Light> >())); mDummies.push_back(new FFPLightStateAttribute(i, std::vector<osg::ref_ptr<osg::Light> >()));
setUpdateCallback(new LightManagerUpdateCallback);
return; return;
} }
auto* stateset = getOrCreateStateSet(); auto* stateset = getOrCreateStateSet();
mSunBuffer = new SunlightBuffer(); { // sunlight UBO data
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject; mSunBuffer = new SunlightBuffer();
mSunBuffer->getData()->setBufferObject(ubo); osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(9 , mSunBuffer->getData().get(), 0, mSunBuffer->blockSize()); mSunBuffer->getData()->setBufferObject(ubo);
stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON); osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(0, mSunBuffer->getData().get(), 0, mSunBuffer->getData()->getTotalDataSize());
stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON);
}
{ // point lights UBO data
mPointBuffer = new PointLightBuffer(getMaxLightsInScene());
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject;
mPointBuffer->getData()->setBufferObject(ubo);
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(1, mPointBuffer->getData().get(), 0, mPointBuffer->getData()->getTotalDataSize());
stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON);
}
mPointLightProxyData.resize(getMaxLightsInScene());
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
stateset->setAttribute(new LightManagerStateAttribute(this), osg::StateAttribute::ON);
stateset->addUniform(new osg::Uniform("PointLightCount", 0)); stateset->addUniform(new osg::Uniform("PointLightCount", 0));
setUpdateCallback(new LightManagerUpdateCallback);
addCullCallback(new SunlightCallback(this)); addCullCallback(new SunlightCallback(this));
} }
////////////////////////////////////////////////////////////////////////////////
LightManager::LightManager(const LightManager &copy, const osg::CopyOp &copyop) LightManager::LightManager(const LightManager &copy, const osg::CopyOp &copyop)
: osg::Group(copy, copyop) : osg::Group(copy, copyop)
, mStartLight(copy.mStartLight) , mStartLight(copy.mStartLight)
@ -507,7 +520,7 @@ namespace SceneUtil
int LightManager::getMaxLights() const int LightManager::getMaxLights() const
{ {
if (usingFFP()) return LightManager::mFFPMaxLights; if (usingFFP()) return LightManager::mFFPMaxLights;
return std::clamp(Settings::Manager::getInt("max lights", "Shaders"), 0, getMaxLightsInScene()); return std::clamp(Settings::Manager::getInt("max lights", "Shaders"),LightManager::mFFPMaxLights , getMaxLightsInScene());
} }
int LightManager::getMaxLightsInScene() const int LightManager::getMaxLightsInScene() const
@ -579,16 +592,18 @@ namespace SceneUtil
} }
void LightManager::update() void LightManager::update()
{ {
mLights.clear(); mLights.clear();
mLightsInViewSpace.clear(); mLightsInViewSpace.clear();
// do an occasional cleanup for orphaned lights // Do an occasional cleanup for orphaned lights.
for (int i=0; i<2; ++i) for (int i=0; i<2; ++i)
{ {
if (mStateSetCache[i].size() > 5000) if (mStateSetCache[i].size() > 5000 || mIndexNeedsRecompiling)
mStateSetCache[i].clear(); mStateSetCache[i].clear();
} }
mIndexNeedsRecompiling = false;
} }
void LightManager::addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum) void LightManager::addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum)
@ -599,7 +614,8 @@ namespace SceneUtil
lightSource->getLight(frameNum)->setPosition(osg::Vec4f(worldMat.getTrans().x(), lightSource->getLight(frameNum)->setPosition(osg::Vec4f(worldMat.getTrans().x(),
worldMat.getTrans().y(), worldMat.getTrans().y(),
worldMat.getTrans().z(), 1.f)); worldMat.getTrans().z(), 1.f));
mLights.push_back(l); if (mLights.size() < static_cast<size_t>(getMaxLightsInScene()))
mLights.emplace_back(l);
} }
void LightManager::setSunlight(osg::ref_ptr<osg::Light> sun) void LightManager::setSunlight(osg::ref_ptr<osg::Light> sun)
@ -627,20 +643,21 @@ namespace SceneUtil
hash_combine(hash, lightList[i]->mLightSource->getId()); hash_combine(hash, lightList[i]->mLightSource->getId());
LightStateSetMap& stateSetCache = mStateSetCache[frameNum%2]; LightStateSetMap& stateSetCache = mStateSetCache[frameNum%2];
LightStateSetMap::iterator found = stateSetCache.find(hash); LightStateSetMap::iterator found = stateSetCache.find(hash);
if (found != stateSetCache.end()) if (found != stateSetCache.end())
return found->second; return found->second;
else else
{ {
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet; osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
std::vector<osg::ref_ptr<osg::Light> > lights;
lights.reserve(lightList.size());
for (size_t i=0; i<lightList.size();++i)
lights.emplace_back(lightList[i]->mLightSource->getLight(frameNum));
if (usingFFP()) if (usingFFP())
{ {
std::vector<osg::ref_ptr<osg::Light> > lights;
lights.reserve(lightList.size());
for (size_t i=0; i<lightList.size();++i)
lights.emplace_back(lightList[i]->mLightSource->getLight(frameNum));
// the first light state attribute handles the actual state setting for all lights // the first light state attribute handles the actual state setting for all lights
// it's best to batch these up so that we don't need to touch the modelView matrix more than necessary // it's best to batch these up so that we don't need to touch the modelView matrix more than necessary
// don't use setAttributeAndModes, that does not support light indices! // don't use setAttributeAndModes, that does not support light indices!
@ -656,14 +673,18 @@ namespace SceneUtil
} }
else else
{ {
osg::ref_ptr<PointLightBuffer> buffer = new PointLightBuffer(lights.size()); osg::ref_ptr<osg::IntArray> indices = new osg::IntArray(getMaxLights());
osg::ref_ptr<osg::UniformBufferObject> ubo = new osg::UniformBufferObject; osg::ref_ptr<osg::Uniform> indicesUni = new osg::Uniform(osg::Uniform::Type::INT, "PointLightIndex", indices->size());
buffer->getData()->setBufferObject(ubo); int validCount = 0;
osg::ref_ptr<osg::UniformBufferBinding> ubb = new osg::UniformBufferBinding(8 ,buffer->getData().get(), 0, buffer->blockSize()); for (size_t i = 0; i < lightList.size(); ++i)
stateset->addUniform(new osg::Uniform("PointLightCount", (int)lights.size())); {
stateset->setAttributeAndModes(ubb.get(), osg::StateAttribute::ON); auto it = mLightData.find(lightList[i]->mLightSource->getId());
if (it != mLightData.end())
stateset->setAttribute(new LightStateAttribute(std::move(lights), buffer), osg::StateAttribute::ON); indices->at(validCount++) = it->second;
}
indicesUni->setArray(indices);
stateset->addUniform(indicesUni);
stateset->addUniform(new osg::Uniform("PointLightCount", validCount));
} }
stateSetCache.emplace(hash, stateset); stateSetCache.emplace(hash, stateset);
@ -671,11 +692,6 @@ namespace SceneUtil
} }
} }
const std::vector<LightManager::LightSourceTransform>& LightManager::getLights() const
{
return mLights;
}
const std::vector<LightManager::LightSourceViewBound>& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix, size_t frameNum) const std::vector<LightManager::LightSourceViewBound>& LightManager::getLightsInViewSpace(osg::Camera *camera, const osg::RefMatrix* viewMatrix, size_t frameNum)
{ {
osg::observer_ptr<osg::Camera> camPtr (camera); osg::observer_ptr<osg::Camera> camPtr (camera);
@ -695,13 +711,47 @@ namespace SceneUtil
l.mLightSource = lightIt->mLightSource; l.mLightSource = lightIt->mLightSource;
l.mViewBound = viewBound; l.mViewBound = viewBound;
it->second.push_back(l); it->second.push_back(l);
if (usingFFP()) continue;
auto* light = l.mLightSource->getLight(frameNum);
auto dataIt = mLightData.find(l.mLightSource->getId());
if (dataIt != mLightData.end())
{
mPointLightProxyData[dataIt->second].mPosition = light->getPosition();
mPointLightProxyData[dataIt->second].mBrightness = l.mLightSource->getBrightness(frameNum);
mPointBuffer->setValue(dataIt->second, PointLightBuffer::Diffuse, light->getDiffuse());
continue;
}
if (mLightData.size() >= static_cast<size_t>(getMaxLightsInScene()))
{
mIndexNeedsRecompiling = true;
mLightData.clear();
}
int index = mLightData.size();
updateGPUPointLight(index, l.mLightSource, frameNum);
mLightData.emplace(l.mLightSource->getId(), index);
} }
} }
return it->second; return it->second;
} }
void LightManager::updateGPUPointLight(int index, LightSource* lightSource, size_t frameNum)
{
auto* light = lightSource->getLight(frameNum);
mPointLightProxyData[index].mPosition = light->getPosition();
mPointLightProxyData[index].mBrightness = lightSource->getBrightness(frameNum);
mPointBuffer->setValue(index, PointLightBuffer::Diffuse, light->getDiffuse());
mPointBuffer->setValue(index, PointLightBuffer::Ambient, light->getAmbient());
mPointBuffer->setValue(index, PointLightBuffer::Attenuation, osg::Vec4(light->getConstantAttenuation(), light->getLinearAttenuation(), light->getQuadraticAttenuation(), lightSource->getRadius()));
}
LightSource::LightSource() LightSource::LightSource()
: mRadius(0.f) : mRadius(0.f)
, mBrightness{1.0,1.0}
{ {
setUpdateCallback(new CollectLightCallback); setUpdateCallback(new CollectLightCallback);
mId = sLightId++; mId = sLightId++;
@ -753,7 +803,7 @@ namespace SceneUtil
// Don't use Camera::getViewMatrix, that one might be relative to another camera! // Don't use Camera::getViewMatrix, that one might be relative to another camera!
const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix(); const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix();
const std::vector<LightManager::LightSourceViewBound>& lights = mLightManager->getLightsInViewSpace(cv->getCurrentCamera(), viewMatrix, mLastFrameNumber); const std::vector<LightManager::LightSourceViewBound>& lights = mLightManager->getLightsInViewSpace(cv->getCurrentCamera(), viewMatrix, mLastFrameNumber);
// get the node bounds in view space // get the node bounds in view space
// NB do not node->getBound() * modelView, that would apply the node's transformation twice // NB do not node->getBound() * modelView, that would apply the node's transformation twice
osg::BoundingSphere nodeBound; osg::BoundingSphere nodeBound;
@ -818,7 +868,6 @@ namespace SceneUtil
else else
stateset = mLightManager->getLightListStateSet(mLightList, cv->getTraversalNumber()); stateset = mLightManager->getLightListStateSet(mLightList, cv->getTraversalNumber());
cv->pushStateSet(stateset); cv->pushStateSet(stateset);
return true; return true;
} }

@ -2,13 +2,15 @@
#define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H #define OPENMW_COMPONENTS_SCENEUTIL_LIGHTMANAGER_H
#include <set> #include <set>
#include <cassert>
#include <unordered_map>
#include <unordered_set>
#include <osg/Light> #include <osg/Light>
#include <osg/Group> #include <osg/Group>
#include <osg/NodeVisitor> #include <osg/NodeVisitor>
#include <osg/observer_ptr> #include <osg/observer_ptr>
#include <osg/BufferIndexBinding>
#include <components/shader/shadermanager.hpp> #include <components/shader/shadermanager.hpp>
@ -17,9 +19,16 @@ namespace osgUtil
class CullVisitor; class CullVisitor;
} }
namespace osg
{
class UniformBufferBinding;
class UniformBufferObject;
}
namespace SceneUtil namespace SceneUtil
{ {
class SunlightBuffer; class SunlightBuffer;
class PointLightBuffer;
// Used to override sun. Rarely useful but necassary for local map. // Used to override sun. Rarely useful but necassary for local map.
class SunlightStateAttribute : public osg::StateAttribute class SunlightStateAttribute : public osg::StateAttribute
@ -59,6 +68,8 @@ namespace SceneUtil
int mId; int mId;
float mBrightness[2];
public: public:
META_Node(SceneUtil, LightSource) META_Node(SceneUtil, LightSource)
@ -78,6 +89,16 @@ namespace SceneUtil
mRadius = radius; mRadius = radius;
} }
float getBrightness(size_t frame)
{
return mBrightness[frame % 2];
}
void setBrightness(size_t frame, float brightness)
{
mBrightness[frame % 2] = brightness;
}
/// Get the osg::Light safe for modification in the given frame. /// Get the osg::Light safe for modification in the given frame.
/// @par May be used externally to animate the light's color/attenuation properties, /// @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. /// and is used internally to synchronize the light's position with the position of the LightSource.
@ -119,7 +140,7 @@ namespace SceneUtil
osg::BoundingSphere mViewBound; osg::BoundingSphere mViewBound;
}; };
typedef std::vector<const LightSourceViewBound*> LightList; using LightList = std::vector<const LightSourceViewBound*>;
static bool queryNonFFPLightingSupport(); static bool queryNonFFPLightingSupport();
@ -146,8 +167,6 @@ namespace SceneUtil
/// Internal use only, called automatically by the LightSource's UpdateCallback /// Internal use only, called automatically by the LightSource's UpdateCallback
void addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum); void addLight(LightSource* lightSource, const osg::Matrixf& worldMat, size_t frameNum);
const std::vector<LightSourceTransform>& getLights() const;
const std::vector<LightSourceViewBound>& getLightsInViewSpace(osg::Camera* camera, const osg::RefMatrix* viewMatrix, 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); osg::ref_ptr<osg::StateSet> getLightListStateSet(const LightList& lightList, size_t frameNum);
@ -165,6 +184,11 @@ namespace SceneUtil
Shader::ShaderManager::DefineMap getLightDefines() const; Shader::ShaderManager::DefineMap getLightDefines() const;
private: private:
friend class LightManagerStateAttribute;
void updateGPUPointLight(int index, LightSource* lightSource, size_t frameNum);
// Lights collected from the scene graph. Only valid during the cull traversal. // Lights collected from the scene graph. Only valid during the cull traversal.
std::vector<LightSourceTransform> mLights; std::vector<LightSourceTransform> mLights;
@ -184,6 +208,21 @@ namespace SceneUtil
osg::ref_ptr<osg::Light> mSun; osg::ref_ptr<osg::Light> mSun;
osg::ref_ptr<SunlightBuffer> mSunBuffer; osg::ref_ptr<SunlightBuffer> mSunBuffer;
struct PointLightProxyData
{
osg::Vec4 mPosition;
float mBrightness;
};
std::vector<PointLightProxyData> mPointLightProxyData;
osg::ref_ptr<PointLightBuffer> mPointBuffer;
// < Light ID , Buffer Index >
using LightDataMap = std::unordered_map<int, int>;
LightDataMap mLightData;
bool mIndexNeedsRecompiling;
bool mFFP; bool mFFP;
static constexpr int mFFPMaxLights = 8; static constexpr int mFFPMaxLights = 8;

@ -58,7 +58,7 @@ namespace SceneUtil
light->setQuadraticAttenuation(quadraticAttenuation); light->setQuadraticAttenuation(quadraticAttenuation);
} }
void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior) void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior, bool useFFPLighting)
{ {
SceneUtil::FindByNameVisitor visitor("AttachLight"); SceneUtil::FindByNameVisitor visitor("AttachLight");
node->accept(visitor); node->accept(visitor);
@ -85,17 +85,21 @@ namespace SceneUtil
attachTo = trans; 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), useFFPLighting);
attachTo->addChild(lightSource); attachTo->addChild(lightSource);
} }
osg::ref_ptr<LightSource> createLightSource(const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient) osg::ref_ptr<LightSource> createLightSource(const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient, bool useFFPLighting)
{ {
osg::ref_ptr<SceneUtil::LightSource> lightSource (new SceneUtil::LightSource); osg::ref_ptr<SceneUtil::LightSource> lightSource (new SceneUtil::LightSource);
osg::ref_ptr<osg::Light> light (new osg::Light); osg::ref_ptr<osg::Light> light (new osg::Light);
lightSource->setNodeMask(lightMask); lightSource->setNodeMask(lightMask);
float radius = esmLight->mData.mRadius; float radius = esmLight->mData.mRadius;
// arbitrary multipler to reduce light popping, this is hard to avoid with per-object lighting
// we offset this multipler in shaders
if (!useFFPLighting)
radius *= 2.0;
lightSource->setRadius(radius); lightSource->setRadius(radius);
configureLight(light, radius, isExterior); configureLight(light, radius, isExterior);
@ -112,7 +116,7 @@ namespace SceneUtil
lightSource->setLight(light); lightSource->setLight(light);
osg::ref_ptr<SceneUtil::LightController> ctrl (new SceneUtil::LightController); osg::ref_ptr<SceneUtil::LightController> ctrl (new SceneUtil::LightController(useFFPLighting));
ctrl->setDiffuse(light->getDiffuse()); ctrl->setDiffuse(light->getDiffuse());
if (esmLight->mData.mFlags & ESM::Light::Flicker) if (esmLight->mData.mFlags & ESM::Light::Flicker)
ctrl->setType(SceneUtil::LightController::LT_Flicker); ctrl->setType(SceneUtil::LightController::LT_Flicker);

@ -32,14 +32,14 @@ namespace SceneUtil
/// @param partsysMask Node mask to ignore when computing the sub graph's bounding box. /// @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 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. /// @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); void addLight (osg::Group* node, const ESM::Light* esmLight, unsigned int partsysMask, unsigned int lightMask, bool isExterior, bool useFFPLighting=true);
/// @brief Convert an ESM::Light to a SceneUtil::LightSource, and return it. /// @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. /// @param esmLight The light definition coming from the game files containing radius, color, flicker, etc.
/// @param lightMask Mask to assign to the newly created LightSource. /// @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. /// @param isExterior Is the light outside? May be used for deciding which attenuation settings to use.
/// @param ambient Ambient component of the light. /// @param ambient Ambient component of the light.
osg::ref_ptr<LightSource> createLightSource (const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient=osg::Vec4f(0,0,0,1)); osg::ref_ptr<LightSource> createLightSource (const ESM::Light* esmLight, unsigned int lightMask, bool isExterior, const osg::Vec4f& ambient=osg::Vec4f(0,0,0,1), bool useFFPLighting=true);
} }

@ -17,10 +17,9 @@
namespace Shader namespace Shader
{ {
ShaderManager::ShaderManager(Resource::SceneManager* sceneManager) ShaderManager::ShaderManager()
: mSceneManager(sceneManager) : mFFPLighting(false)
{ {
} }
void ShaderManager::setShaderPath(const std::string &path) void ShaderManager::setShaderPath(const std::string &path)
@ -28,6 +27,11 @@ namespace Shader
mPath = path; mPath = path;
} }
void ShaderManager::setFFPLighting(bool useFFP)
{
mFFPLighting = useFFP;
}
bool addLineDirectivesAfterConditionalBlocks(std::string& source) bool addLineDirectivesAfterConditionalBlocks(std::string& source)
{ {
for (size_t position = 0; position < source.length(); ) for (size_t position = 0; position < source.length(); )
@ -352,10 +356,10 @@ namespace Shader
program->addShader(fragmentShader); program->addShader(fragmentShader);
program->addBindAttribLocation("aOffset", 6); program->addBindAttribLocation("aOffset", 6);
program->addBindAttribLocation("aRotation", 7); program->addBindAttribLocation("aRotation", 7);
if (!mSceneManager->getFFPLighting()) if (!mFFPLighting)
{ {
program->addBindUniformBlock("PointLightBuffer", 8); program->addBindUniformBlock("SunlightBuffer", 0);
program->addBindUniformBlock("SunlightBuffer", 9); program->addBindUniformBlock("PointLightBuffer", 1);
} }
found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first; found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first;
} }

@ -25,10 +25,12 @@ namespace Shader
{ {
public: public:
ShaderManager(Resource::SceneManager* sceneManager); ShaderManager();
void setShaderPath(const std::string& path); void setShaderPath(const std::string& path);
void setFFPLighting(bool useFFP);
typedef std::map<std::string, std::string> DefineMap; typedef std::map<std::string, std::string> DefineMap;
/// Create or retrieve a shader instance. /// Create or retrieve a shader instance.
@ -67,7 +69,7 @@ namespace Shader
typedef std::map<std::pair<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> >, osg::ref_ptr<osg::Program> > ProgramMap; typedef std::map<std::pair<osg::ref_ptr<osg::Shader>, osg::ref_ptr<osg::Shader> >, osg::ref_ptr<osg::Program> > ProgramMap;
ProgramMap mPrograms; ProgramMap mPrograms;
Resource::SceneManager* mSceneManager; bool mFFPLighting;
std::mutex mMutex; std::mutex mMutex;
}; };

@ -6,19 +6,21 @@
struct PointLight struct PointLight
{ {
vec4 position;
vec4 diffuse; vec4 diffuse;
vec4 ambient; vec4 ambient;
vec4 position; float constantAttenuation;
vec4 attenuation; float linearAttenuation;
float quadraticAttenuation;
float radius;
}; };
uniform mat4 osg_ViewMatrix;
uniform int PointLightCount; uniform int PointLightCount;
uniform int PointLightIndex[@maxLights]; uniform int PointLightIndex[@maxLights];
layout(std140) uniform PointLightBuffer layout(std140) uniform PointLightBuffer
{ {
PointLight PointLights[@maxLights]; PointLight PointLights[@maxLightsInScene];
}; };
#else #else
@ -47,15 +49,26 @@ void perLightSun(out vec3 ambientOut, out vec3 diffuseOut, vec3 viewPos, vec3 vi
diffuseOut = @sunDiffuse.xyz * lambert; diffuseOut = @sunDiffuse.xyz * lambert;
} }
uniform float osg_SimulationTime;
void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal) void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec3 viewPos, vec3 viewNormal)
{ {
vec3 lightDir = getLight[lightIndex].position.xyz - viewPos; vec4 pos = getLight[lightIndex].position;
//vec3 lightDir = (osg_ViewMatrix * vec4(getLight[lightIndex].position, 1.0)).xyz - viewPos; vec3 lightDir = pos.xyz - viewPos;
float lightDistance = length(lightDir); float lightDistance = length(lightDir);
lightDir = normalize(lightDir); lightDir = normalize(lightDir);
float illumination = clamp(1.0 / (getLight[lightIndex].attenuation.x + getLight[lightIndex].attenuation.y * lightDistance + getLight[lightIndex].attenuation.z * lightDistance * lightDistance), 0.0, 1.0); float illumination = clamp(1.0 / (getLight[lightIndex].constantAttenuation + getLight[lightIndex].linearAttenuation * lightDistance + getLight[lightIndex].quadraticAttenuation * lightDistance * lightDistance), 0.0, 1.0);
// Add an artificial cutoff, otherwise effected objects will be brightly lit and adjacent objects not effected by this light will be dark by contrast
// This causes nasty artifacts, especially with active grid so it is necassary for now.
#if !@ffpLighting
float cutoff = getLight[lightIndex].radius * 0.5;
illumination *= 1.0 - smoothstep(0.0, 1.0, ((lightDistance / cutoff) - 1.0) * 0.887);
illumination = max(0.0, illumination);
#endif
ambientOut = getLight[lightIndex].ambient.xyz * illumination; ambientOut = getLight[lightIndex].ambient.xyz * illumination;
float lambert = dot(viewNormal.xyz, lightDir) * illumination; float lambert = dot(viewNormal.xyz, lightDir) * illumination;
@ -70,7 +83,12 @@ void perLightPoint(out vec3 ambientOut, out vec3 diffuseOut, int lightIndex, vec
} }
lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0); lambert *= clamp(-8.0 * (1.0 - 0.3) * eyeCosine + 1.0, 0.3, 1.0);
#endif #endif
#if @ffpLighting
diffuseOut = getLight[lightIndex].diffuse.xyz * lambert; diffuseOut = getLight[lightIndex].diffuse.xyz * lambert;
#else
diffuseOut = (getLight[lightIndex].diffuse.xyz * pos.w) * lambert;
#endif
} }
#if PER_PIXEL_LIGHTING #if PER_PIXEL_LIGHTING
@ -97,7 +115,7 @@ void doLighting(vec3 viewPos, vec3 viewNormal, out vec3 diffuseLight, out vec3 a
diffuseLight += diffuseOut; diffuseLight += diffuseOut;
for (int i=0; i<PointLightCount; ++i) for (int i=0; i<PointLightCount; ++i)
{ {
perLightPoint(ambientOut, diffuseOut, i, viewPos, viewNormal); perLightPoint(ambientOut, diffuseOut, PointLightIndex[i], viewPos, viewNormal);
#else #else
for (int i=0; i<@maxLights; ++i) for (int i=0; i<@maxLights; ++i)
{ {

Loading…
Cancel
Save