Lighting Patch

Fixes build errors with older OSG builds and some issues with 'shared' layout.
Bring back ambient in inventory through lightmodel instead of sun ambient, mirrors scene ambient/sunlight relationship.
Forces shaders when certain lighting methods are enabled and finalize settings.
Correctly override sun for localmap.
This commit is contained in:
glassmancody.info 2021-04-16 11:55:40 -07:00
parent a77e1f1812
commit 16856d45c5
11 changed files with 280 additions and 219 deletions

View file

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