From e163286caa07f6fc42efe0e6a8355961fd27b05a Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Wed, 22 Jul 2026 18:19:10 +0200 Subject: [PATCH 1/5] RTSS: allow referencing shared_params --- .../include/OgreShaderParameter.h | 2 ++ .../include/OgreShaderProgram.h | 5 +++ .../src/OgreShaderCGProgramWriter.cpp | 27 ++++++++++++++++ .../src/OgreShaderCGProgramWriter.h | 1 + .../src/OgreShaderGLSLProgramWriter.cpp | 31 +++++++++++++++++++ .../src/OgreShaderGLSLProgramWriter.h | 2 ++ .../src/OgreShaderParameter.cpp | 6 ++++ .../src/OgreShaderProgramProcessor.cpp | 4 +++ OgreMain/include/OgreGpuProgramParams.h | 3 ++ OgreMain/src/OgreGpuProgramParams.cpp | 9 ++++++ 10 files changed, 90 insertions(+) diff --git a/Components/RTShaderSystem/include/OgreShaderParameter.h b/Components/RTShaderSystem/include/OgreShaderParameter.h index 4aff48b19ec..d9e0c71a759 100644 --- a/Components/RTShaderSystem/include/OgreShaderParameter.h +++ b/Components/RTShaderSystem/include/OgreShaderParameter.h @@ -577,6 +577,8 @@ class _OgreRTSSExport ParameterFactory static ParameterPtr createConstParam(const Vector4& val); static ParameterPtr createConstParam(float val); + static ParameterPtr createSharedParam(const GpuSharedParametersPtr& parent, const String& name); + static UniformParameterPtr createSampler(GpuConstantType type, int index); static UniformParameterPtr createSampler1D(int index); static UniformParameterPtr createSampler2D(int index); diff --git a/Components/RTShaderSystem/include/OgreShaderProgram.h b/Components/RTShaderSystem/include/OgreShaderProgram.h index 3da69023a25..ff1df325d3c 100644 --- a/Components/RTShaderSystem/include/OgreShaderProgram.h +++ b/Components/RTShaderSystem/include/OgreShaderProgram.h @@ -184,6 +184,9 @@ class _OgreRTSSExport Program : public RTShaderSystemAlloc void setUseLinearColours(bool useLinear) { mUseLinearColours = useLinear; } bool getUseLinearColours() const { return mUseLinearColours; } + void addSharedParameters(GpuSharedParametersPtr sharedParams) { mSharedParameters.push_back(sharedParams); } + const std::vector& getSharedParameters() const { return mSharedParameters; } + /** Class destructor */ ~Program(); // Protected methods. @@ -213,6 +216,8 @@ class _OgreRTSSExport Program : public RTShaderSystemAlloc StringVector mDependencies; /// preprocessor definitions String mPreprocessorDefines; + /// Shared parameter sets referenced by this program + std::vector mSharedParameters; // Skeletal animation calculation bool mSkeletalAnimation; // Whether to pass matrices as column-major. diff --git a/Components/RTShaderSystem/src/OgreShaderCGProgramWriter.cpp b/Components/RTShaderSystem/src/OgreShaderCGProgramWriter.cpp index 43260b87917..95cb153b6ac 100644 --- a/Components/RTShaderSystem/src/OgreShaderCGProgramWriter.cpp +++ b/Components/RTShaderSystem/src/OgreShaderCGProgramWriter.cpp @@ -86,6 +86,30 @@ void CGProgramWriter::initializeStringMaps() mParamSemanticMap[Parameter::SPS_LAYER] = "SV_RenderTargetArrayIndex"; } +void CGProgramWriter::writeSharedParams(std::ostream& os, const String& name, int registerIdx, const GpuSharedParametersPtr& params) +{ + // In HLSL/D3D11 constant buffers are bound to register(bN). + // Only SM4+ (ps_4_0 and up) understands cbuffer/register(b#). + bool useRegister = GpuProgramManager::getSingleton().isSyntaxSupported("ps_4_0"); + + os << "cbuffer " << name; + if (useRegister) + os << " : register(b" << registerIdx << ")"; + os << " {\n"; + + for (const auto& e : params->getConstantDefinitionsSorted()) + { + const GpuConstantDefinition& def = e.second; + + os << "\t" << mGpuConstTypeMap[def.constType] << " " << e.first; + if (def.arraySize > 1) + os << "[" << def.arraySize << "]"; + os << ";\n"; + } + + os << "};\n"; +} + //----------------------------------------------------------------------- void CGProgramWriter::writeSourceCode(std::ostream& os, Program* program) { @@ -108,6 +132,9 @@ void CGProgramWriter::writeSourceCode(std::ostream& os, Program* program) } os << std::endl; + int sharedRegister = 1; + for (const auto& shared : program->getSharedParameters()) + writeSharedParams(os, shared->getName(), sharedRegister++, shared); Function* curFunction = program->getMain(); diff --git a/Components/RTShaderSystem/src/OgreShaderCGProgramWriter.h b/Components/RTShaderSystem/src/OgreShaderCGProgramWriter.h index bd84632c9d4..19af0266a9a 100644 --- a/Components/RTShaderSystem/src/OgreShaderCGProgramWriter.h +++ b/Components/RTShaderSystem/src/OgreShaderCGProgramWriter.h @@ -77,6 +77,7 @@ class CGProgramWriter : public ProgramWriter // Protected methods. protected: + void writeSharedParams(std::ostream& os, const String& name, int registerIdx, const GpuSharedParametersPtr& params); /** Initialize string maps. */ void initializeStringMaps(); diff --git a/Components/RTShaderSystem/src/OgreShaderGLSLProgramWriter.cpp b/Components/RTShaderSystem/src/OgreShaderGLSLProgramWriter.cpp index a5082689b50..daa34e652d9 100644 --- a/Components/RTShaderSystem/src/OgreShaderGLSLProgramWriter.cpp +++ b/Components/RTShaderSystem/src/OgreShaderGLSLProgramWriter.cpp @@ -151,6 +151,33 @@ void GLSLProgramWriter::writeUniformBlock(std::ostream& os, const String& name, os << "};\n"; } +void GLSLProgramWriter::writeUniformBlock(std::ostream& os, const String& name, int binding, + const GpuSharedParametersPtr& params) +{ + // Explicit binding needs GLSL 420 / SPIR-V; otherwise the RS assigns it by block name. + bool explicitBinding = mIsVulkan || mGLSLVersion >= 420; + + os << "layout("; + if (explicitBinding) + os << "binding = " << binding << ", "; + os << "std140, row_major) uniform " << name << " {\n"; + + for (const auto& e : params->getConstantDefinitionsSorted()) + { + const GpuConstantDefinition& def = e.second; + + if (def.constType == GCT_MATRIX_3X4 || def.constType == GCT_MATRIX_2X4) + os << "layout(column_major) "; + + os << "\t" << mGpuConstTypeMap[def.constType] << " " << e.first; + if (def.arraySize > 1) + os << "[" << def.arraySize << "]"; + os << ";\n"; + } + + os << "};\n"; +} + void GLSLProgramWriter::writeMainSourceCode(std::ostream& os, Program* program) { GpuProgramType gpuType = program->getType(); @@ -188,6 +215,10 @@ void GLSLProgramWriter::writeMainSourceCode(std::ostream& os, Program* program) uniforms.clear(); } + int sharedBinding = GPT_FRAGMENT_PROGRAM + 1; + for (const auto& shared : program->getSharedParameters()) + writeUniformBlock(os, shared->getName(), sharedBinding++, shared); + int uniformLoc = 0; for (const auto& uparam : uniforms) { diff --git a/Components/RTShaderSystem/src/OgreShaderGLSLProgramWriter.h b/Components/RTShaderSystem/src/OgreShaderGLSLProgramWriter.h index 0434dab4a04..0839a2f1d42 100644 --- a/Components/RTShaderSystem/src/OgreShaderGLSLProgramWriter.h +++ b/Components/RTShaderSystem/src/OgreShaderGLSLProgramWriter.h @@ -88,6 +88,8 @@ class GLSLProgramWriter : public ProgramWriter void writeUniformBlock(std::ostream& os, const String& name, int binding, const UniformParameterList& uniforms); + void writeUniformBlock(std::ostream& os, const String& name, int binding, const GpuSharedParametersPtr& params); + protected: typedef std::map ParamSemanticToStringMap; diff --git a/Components/RTShaderSystem/src/OgreShaderParameter.cpp b/Components/RTShaderSystem/src/OgreShaderParameter.cpp index 36909a5a239..fc615dd30ca 100644 --- a/Components/RTShaderSystem/src/OgreShaderParameter.cpp +++ b/Components/RTShaderSystem/src/OgreShaderParameter.cpp @@ -635,6 +635,12 @@ ParameterPtr ParameterFactory::createConstParam(float val) Parameter::SPC_UNKNOWN)); } +ParameterPtr ParameterFactory::createSharedParam(const GpuSharedParametersPtr& parent, const String& name) +{ + auto p = parent->getConstantDefinition(name); + return std::make_shared(p.constType, name, Parameter::SPS_UNKNOWN, 0, p.arraySize); +} + //----------------------------------------------------------------------- UniformParameterPtr ParameterFactory::createUniform(GpuConstantType type, int index, uint16 variability, diff --git a/Components/RTShaderSystem/src/OgreShaderProgramProcessor.cpp b/Components/RTShaderSystem/src/OgreShaderProgramProcessor.cpp index 8e78ecf40f2..1078084c434 100644 --- a/Components/RTShaderSystem/src/OgreShaderProgramProcessor.cpp +++ b/Components/RTShaderSystem/src/OgreShaderProgramProcessor.cpp @@ -77,6 +77,10 @@ void ProgramProcessor::bindAutoParameters(Program* pCpuProgram, GpuProgramPtr pG { GpuProgramParametersSharedPtr pGpuParams = pGpuProgram->getDefaultParameters(); + // Forward shared parameter sets to the concrete GpuProgramParameters, + for (const auto& sharedParams : pCpuProgram->getSharedParameters()) + pGpuParams->addSharedParameters(sharedParams); + pGpuParams->setUseLinearColours(pCpuProgram->getUseLinearColours()); for (const auto& p : pCpuProgram->getParameters()) { diff --git a/OgreMain/include/OgreGpuProgramParams.h b/OgreMain/include/OgreGpuProgramParams.h index 78b524a1e83..3a995373247 100644 --- a/OgreMain/include/OgreGpuProgramParams.h +++ b/OgreMain/include/OgreGpuProgramParams.h @@ -489,6 +489,9 @@ namespace Ogre { */ const GpuNamedConstants& getConstantDefinitions() const; + /// Get the constant definitions ordered by their physical index. + std::vector> getConstantDefinitionsSorted() const; + /** @copydoc GpuProgramParameters::setNamedConstant(const String&, Real) */ template void setNamedConstant(const String& name, T val) { diff --git a/OgreMain/src/OgreGpuProgramParams.cpp b/OgreMain/src/OgreGpuProgramParams.cpp index ff8e4a67f26..982d06caff6 100644 --- a/OgreMain/src/OgreGpuProgramParams.cpp +++ b/OgreMain/src/OgreGpuProgramParams.cpp @@ -486,6 +486,15 @@ namespace Ogre { return mNamedConstants; } + std::vector> GpuSharedParameters::getConstantDefinitionsSorted() const + { + std::vector> ordered(mNamedConstants.map.begin(), + mNamedConstants.map.end()); + std::sort(ordered.begin(), ordered.end(), + [](const auto& a, const auto& b) { return a.second.physicalIndex < b.second.physicalIndex; }); + + return ordered; + } //--------------------------------------------------------------------- void GpuSharedParameters::setNamedConstant(const String& name, const Matrix4& m) { From 2bdf3e1262a952b1acf852ccaf3379be181e56d8 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Sun, 2 Aug 2026 00:25:08 +0200 Subject: [PATCH 2/5] Main: add support for clustered light culling --- OgreMain/include/OgreAutoParamDataSource.h | 10 + OgreMain/include/OgreGpuProgramParams.h | 6 + OgreMain/src/OgreAutoParamDataSource.cpp | 74 +++++++- OgreMain/src/OgreFroxelizer.cpp | 201 +++++++++++++++++++++ OgreMain/src/OgreFroxelizer.h | 62 +++++++ OgreMain/src/OgreGpuProgramParams.cpp | 13 +- 6 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 OgreMain/src/OgreFroxelizer.cpp create mode 100644 OgreMain/src/OgreFroxelizer.h diff --git a/OgreMain/include/OgreAutoParamDataSource.h b/OgreMain/include/OgreAutoParamDataSource.h index 3dc4b3bcf5a..cb0c889cb6f 100644 --- a/OgreMain/include/OgreAutoParamDataSource.h +++ b/OgreMain/include/OgreAutoParamDataSource.h @@ -37,6 +37,7 @@ namespace Ogre { // forward decls struct VisibleObjectsBoundsInfo; + struct Froxelizer; /** \addtogroup Core * @{ @@ -120,7 +121,10 @@ namespace Ogre { mutable bool mSceneDepthRangeDirty; mutable bool mLodCameraPositionDirty; mutable bool mLodCameraPositionObjectSpaceDirty; + mutable bool mFroxelStructureDirty; + std::unique_ptr mFroxelizer; + GpuSharedParametersPtr mFroxelParams; const Renderable* mCurrentRenderable; const Camera* mCurrentCamera; std::vector mCameraArray; @@ -144,6 +148,7 @@ namespace Ogre { bool mCurrentUseIdentityProj; public: AutoParamDataSource(); + ~AutoParamDataSource(); /** Updates the current renderable */ void setCurrentRenderable(const Renderable* rend); /** Sets the world matrices, avoid query from renderable again */ @@ -298,6 +303,11 @@ namespace Ogre { uint16 getGpuParamsDirty() const { return mGpuParamsDirty; } void resetGpuParamsDirty() { mGpuParamsDirty = 0; } void updateLightCustomGpuParameter(const GpuProgramParameters::AutoConstantEntry& constantEntry, GpuProgramParameters *params) const; + + const Vector4f& getFroxelTileParams() const; + const Vector4f& getFroxelDepthParams() const; + // Updates the froxel data in the shared parameters + void updateFroxelData(); }; /** @} */ /** @} */ diff --git a/OgreMain/include/OgreGpuProgramParams.h b/OgreMain/include/OgreGpuProgramParams.h index 3a995373247..b51e682b672 100644 --- a/OgreMain/include/OgreGpuProgramParams.h +++ b/OgreMain/include/OgreGpuProgramParams.h @@ -1146,6 +1146,12 @@ namespace Ogre { ACT_POINT_PARAMS, /// the LOD index as selected by the active LodStrategy ACT_MATERIAL_LOD_INDEX, + /// Clustered (froxel) lighting grid size parameters + /// packed as `(countX, countY, yFix, tileSizePx)` + ACT_FROXEL_TILE_PARAMS, + /// Clustered (froxel) lighting depth parameters + /// packed as `(scaleZ, biasZ, linZ, sliceCount)` + ACT_FROXEL_DEPTH_PARAMS, }; /** Defines the type of the extra data item used by the auto constant. diff --git a/OgreMain/src/OgreAutoParamDataSource.cpp b/OgreMain/src/OgreAutoParamDataSource.cpp index 92b2e6bea69..33faf32119a 100644 --- a/OgreMain/src/OgreAutoParamDataSource.cpp +++ b/OgreMain/src/OgreAutoParamDataSource.cpp @@ -31,6 +31,9 @@ THE SOFTWARE. #include "OgreRenderable.h" #include "OgreControllerManager.h" #include "OgreViewport.h" +#include "OgreFroxelizer.h" + +#include "OgreGpuProgramManager.h" namespace Ogre { //----------------------------------------------------------------------------- @@ -56,6 +59,7 @@ namespace Ogre { mSceneDepthRangeDirty(true), mLodCameraPositionDirty(true), mLodCameraPositionObjectSpaceDirty(true), + mFroxelStructureDirty(true), mCurrentRenderable(0), mCurrentCamera(0), mCameraRelativeRendering(false), @@ -75,6 +79,8 @@ namespace Ogre { mBlankLight.setSpecularColour(ColourValue::Black); mBlankLight.setAttenuation(0,1,0,0); mDummyNode.attachObject(&mBlankLight); + mFroxelizer = std::make_unique(); + for(size_t i = 0; i < OGRE_MAX_SIMULTANEOUS_LIGHTS; ++i) { mTextureViewProjMatrixDirty[i] = true; @@ -86,6 +92,7 @@ namespace Ogre { } } + AutoParamDataSource::~AutoParamDataSource() = default; //----------------------------------------------------------------------------- const Camera* AutoParamDataSource::getCurrentCamera() const { @@ -163,6 +170,7 @@ namespace Ogre { mCameraPositionDirty = true; mLodCameraPositionObjectSpaceDirty = true; mLodCameraPositionDirty = true; + mFroxelStructureDirty = true; } void AutoParamDataSource::setCameraArray(const std::vector cameras) { @@ -193,7 +201,6 @@ namespace Ogre { mSpotlightViewProjMatrixDirty[i] = true; mSpotlightWorldViewProjMatrixDirty[i] = true; } - } //--------------------------------------------------------------------- float AutoParamDataSource::getLightNumber(size_t index) const @@ -1290,5 +1297,70 @@ namespace Ogre { } } + const Vector4f& AutoParamDataSource::getFroxelTileParams() const + { + if((mGpuParamsDirty & GPV_LIGHTS) != 0 || mFroxelStructureDirty) + { + mFroxelizer->update(mCurrentCamera, mCurrentViewport, *mCurrentLightList); + mFroxelStructureDirty = false; + } + + return mFroxelizer->getTileParams(); + } + + const Vector4f& AutoParamDataSource::getFroxelDepthParams() const + { + if((mGpuParamsDirty & GPV_LIGHTS) != 0 || mFroxelStructureDirty) + { + mFroxelizer->update(mCurrentCamera, mCurrentViewport, *mCurrentLightList); + mFroxelStructureDirty = false; + } + + return mFroxelizer->getDepthParams(); + } + + void AutoParamDataSource::updateFroxelData() + { + // mGpuParamsDirty will be reset here, so we rely on the ACT_ to be read before this is called + if((mGpuParamsDirty & GPV_LIGHTS) != 0 || mFroxelStructureDirty) + { + mFroxelizer->update(mCurrentCamera, mCurrentViewport, *mCurrentLightList); + mFroxelStructureDirty = false; + } + + const auto& grid = mFroxelizer->getGrid(); + const auto& records = mFroxelizer->getRecords(); + + OgreAssert(grid.size() == Froxelizer::MAX_FROXELS, "Assuming a 16x16x16 froxel grid for now"); + OgreAssert(records.size() <= Froxelizer::MAX_FROXEL_RECORDS, "max of 16384 froxel records for now"); + + auto& mgr = GpuProgramManager::getSingleton(); + if(!mFroxelParams) + { + if(mgr.getAvailableSharedParameters().count("OgreFroxels") == 0) + { + mFroxelParams = mgr.createSharedParameters("OgreFroxels"); + // packed as uvec4 + mFroxelParams->addConstantDefinition("froxelGrid", GCT_UINT4, Froxelizer::MAX_FROXELS/4); + // 4 light bytes per uint, 16 per uvec4 + mFroxelParams->addConstantDefinition("froxelRecords", GCT_UINT4, Froxelizer::MAX_FROXEL_RECORDS/16); + } + else + { + mFroxelParams = mgr.getSharedParameters("OgreFroxels"); + } + } + + // Packs 16 light indices into one uvec4 + const uint32 recordWords = (std::max(records.size(), 1u) + 3) / 4; + + std::vector recordData(recordWords, 0); + for (uint32 i = 0; i < records.size(); ++i) + recordData[i >> 2] |= uint32(records[i]) << ((i & 3u) * 8u); + + // Upload grid (packed offset << 8 | count) + mFroxelParams->setNamedConstant("froxelGrid", grid.data(), grid.size()); + mFroxelParams->setNamedConstant("froxelRecords", recordData.data(), recordData.size()); + } } diff --git a/OgreMain/src/OgreFroxelizer.cpp b/OgreMain/src/OgreFroxelizer.cpp new file mode 100644 index 00000000000..32a81e1412a --- /dev/null +++ b/OgreMain/src/OgreFroxelizer.cpp @@ -0,0 +1,201 @@ +#include "OgreStableHeaders.h" + +#include "OgreFroxelizer.h" +#include "OgreViewport.h" + +namespace Ogre +{ +/// Compute the froxel layout (XY tile count + tile size) for a given viewport and buffer budget +static Vector4f computeFroxelLayout(const Viewport* viewport, int sliceCount, uint32 bufferEntryCount) +{ + int width = std::max(1, viewport->getActualWidth()); + int height = std::max(1, viewport->getActualHeight()); + sliceCount = std::max(2, sliceCount); + + // Number of froxels in the XY plane; the Z slices consume buffer entries: + // froxelPlaneCount = bufferEntryCount / sliceCount + const size_t froxelPlaneCount = bufferEntryCount / sliceCount; + + // Goal: countX * countY <= froxelPlaneCount with near-square froxels: + // countX / countY ~= aspect => countY <= sqrt(froxelPlaneCount / aspect) + const float aspect = float(width) / float(height); + + uint32 countYTmp = uint32(std::sqrt(double(froxelPlaneCount) / double(aspect))); + countYTmp = std::max(1u, countYTmp); + uint32 countXTmp = std::max(1u, uint32(froxelPlaneCount) / countYTmp); + + // Square froxel edge length in pixels (round up to the larger ratio so the + // resulting tile count never exceeds the budget) + uint32 tileSizePx = uint32(std::ceil(std::max(float(width) / float(countXTmp), float(height) / float(countYTmp)))); + tileSizePx = std::max(1u, tileSizePx); + + // Final tile count derived from the final tile size + uint32 countX = (width + tileSizePx - 1) / tileSizePx; + uint32 countY = (height + tileSizePx - 1) / tileSizePx; + + const RenderTarget* rt = viewport->getTarget(); + const float yFix = !rt->requiresTextureFlipping() ? float(height) : -1.0f; + + return Vector4f(countX, countY, yFix, tileSizePx); +} + +/// window-space depth (== gl_FragCoord.z) of an on-axis point at distance z +static float viewZToWindowDepth(const Camera* cam, float z) +{ + const Matrix4& P = cam->getProjectionMatrixWithRSDepth(); // incl. reverse-Z / API depth range + const Vector4 clip = P * Vector4(0.0f, 0.0f, -z, 1.0f); + const float ndc = float(clip.z / clip.w); + + RenderSystem* rs = Root::getSingleton().getRenderSystem(); + const float dMin = rs ? float(rs->getMinimumDepthInputValue()) : -1.0f; + const float dMax = rs ? float(rs->getMaximumDepthInputValue()) : 1.0f; + return (ndc - dMin) / (dMax - dMin); +} + +void Froxelizer::updateDepthParams(const Camera* cam) +{ + const float sliceCount = float(MAX_FROXEL_SLICES); + + mZLightNear = std::max(cam->getNearClipDistance() * 5.0f, 1e-4f); + mZLightFar = mZLightNear * 10; + + // 1/z = a*d + b (exact for perspective) + const float d0 = viewZToWindowDepth(cam, mZLightNear); + const float d1 = viewZToWindowDepth(cam, mZLightFar); + OgreAssertDbg(std::abs(d1 - d0) > 1e-6f, "degenerate depth range"); + const float a = (1.0f / mZLightFar - 1.0f / mZLightNear) / (d1 - d0); + const float b = 1.0f / mZLightNear - a * d0; + + // slice 0 == [0, zLightNear], slices 1..N-1 exponential over [zLightNear, zLightFar] + const float linearizer = std::log2(mZLightFar / mZLightNear) / (sliceCount - 1.0f); + + mDepthParams = Vector4f(mZLightFar * a, mZLightFar * b, -1.0f / linearizer, sliceCount); +} + +/// viewSpaceZ < 0 == in front of the camera. Exact inverse of the shader formula. +int Froxelizer::findSliceZ(float viewSpaceZ) const +{ + const float sliceCount = mDepthParams[3]; + + // recipViewZ == mZLightFar / (-viewSpaceZ) + float s = std::log2(mZLightFar / -viewSpaceZ) * mDepthParams[2] + sliceCount; + + // light center behind the camera (or z == 0) -> first slice + s = viewSpaceZ < 0.0f ? s : 0.0f; + + return int(Math::Clamp(s, 0.0f, sliceCount - 1.0f)); // clamp then truncate, like the shader +} + +//----------------------------------------------------------------------------- +void Froxelizer::rebuildLayout(const Camera* cam, const Viewport* viewport) +{ + mTileParams = computeFroxelLayout(viewport, MAX_FROXEL_SLICES, MAX_FROXELS); + + mLastWidth = viewport->getActualWidth(); + mLastHeight = viewport->getActualHeight(); +} + +//----------------------------------------------------------------------------- +void Froxelizer::binLights(const Camera* cam, const LightList& lights) +{ + auto cx = int(mTileParams[0]), cy = int(mTileParams[1]), cz = int(mDepthParams[3]); + const uint32 froxelCount = uint32(cx * cy * cz); + + mGrid.assign(MAX_FROXELS, 0); + mRecords.clear(); + mRecords.reserve(MAX_FROXEL_RECORDS); + + if (lights.empty() || froxelCount == 0) + return; + + mFroxelLights.resize(froxelCount); + for (auto& bucket : mFroxelLights) + { + bucket.clear(); + bucket.reserve(MAX_LIGHTS); + } + + const Affine3& view = cam->getViewMatrix(); + + const uint8 lightCount = std::min(lights.size(), MAX_LIGHTS); + for (uint8 li = 0; li < lightCount; ++li) + { + const Light* l = lights[li]; + + // Directional lights cover the whole frustum -> handle outside the cluster grid + if (l->getType() == Light::LT_DIRECTIONAL) + continue; + + const Real radius = l->getAttenuationRange(); + const Vector3 posWorld = l->getDerivedPosition(); + const Vector3 centerVS = view * posWorld; // view space (looking down -Z) + + if (centerVS.z - radius > 0.0f) // sphere entirely behind the camera + continue; + + // sphere spans z in [c.z - r, c.z + r]; c.z + r is the near side + int sliceMin = std::max(findSliceZ(float(centerVS.z + radius)) - 1, 0); + int sliceMax = std::min(findSliceZ(float(centerVS.z - radius)) + 1, cz - 1); + + // --- Screen-space XY tile range via sphere projection --- + RealRect lightRect; // NDC [-1,1], y up + cam->projectSphere(Sphere(posWorld, radius), lightRect); + + // NDC -> tile coords (flip Y so tile 0 is at the top) + const float w = float(mLastWidth), h = float(mLastHeight), ts = mTileParams[3]; + int tileXMin = int(std::floor(((lightRect.left * 0.5f + 0.5f) * w) / ts)); + int tileXMax = int(std::floor(((lightRect.right * 0.5f + 0.5f) * w) / ts)); + int tileYMin = int(std::floor(((-lightRect.top * 0.5f + 0.5f) * h) / ts)); + int tileYMax = int(std::floor(((-lightRect.bottom * 0.5f + 0.5f) * h) / ts)); + if (tileXMin > tileXMax) std::swap(tileXMin, tileXMax); + if (tileYMin > tileYMax) std::swap(tileYMin, tileYMax); + + // Reject lights entirely outside the screen boundaries + if (tileXMax < 0 || tileXMin >= cx || tileYMax < 0 || tileYMin >= cy) + continue; + + tileXMin = Math::Clamp(tileXMin, 0, cx - 1); + tileXMax = Math::Clamp(tileXMax, 0, cx - 1); + tileYMin = Math::Clamp(tileYMin, 0, cy - 1); + tileYMax = Math::Clamp(tileYMax, 0, cy - 1); + + // --- Assign to the froxels inside the conservative AABB --- + for (int z = sliceMin; z <= sliceMax; ++z) + for (int y = tileYMin; y <= tileYMax; ++y) + for (int x = tileXMin; x <= tileXMax; ++x) + { + const uint32 idx = (z * cy + y) * cx + x; + auto& bucket = mFroxelLights[idx]; + if (bucket.size() < MAX_LIGHTS) // count is stored in 8 bits + bucket.push_back(li); + } + } + + // --- Flatten into grid (offset << 8 | count) + records --- + for (uint32 f = 0; f < froxelCount; ++f) + { + uint32 offset = uint32(mRecords.size()); + uint32 count = uint32(mFroxelLights[f].size()); + if (mRecords.size() + count > MAX_FROXEL_RECORDS) + count = uint32(MAX_FROXEL_RECORDS - mRecords.size()); + + mGrid[f] = (offset << 8) | (count & 0xFFu); + mRecords.insert(mRecords.end(), mFroxelLights[f].begin(), mFroxelLights[f].begin() + count); + + if (mRecords.size() >= MAX_FROXEL_RECORDS) + break; + } +} + +//----------------------------------------------------------------------------- +void Froxelizer::update(const Camera* cam, const Viewport* viewport, const LightList& lights) +{ + if (viewport->getActualWidth() != mLastWidth || viewport->getActualHeight() != mLastHeight) + rebuildLayout(cam, viewport); + + updateDepthParams(cam); + + binLights(cam, lights); +} + +} // namespace Ogre \ No newline at end of file diff --git a/OgreMain/src/OgreFroxelizer.h b/OgreMain/src/OgreFroxelizer.h new file mode 100644 index 00000000000..c1041b66943 --- /dev/null +++ b/OgreMain/src/OgreFroxelizer.h @@ -0,0 +1,62 @@ +#ifndef OGRE_FROXELIZER_H +#define OGRE_FROXELIZER_H + +#include "OgreLight.h" +#include "OgrePrerequisites.h" +#include "OgreVector.h" +#include + +namespace Ogre +{ + +/** Builds a frustum-voxel ("froxel") grid for the active camera and bins + lights into it, in the style of Filament's Froxelizer. The result feeds + two GPU buffers (grid + records) consumed by clustered shading. */ +struct Froxelizer +{ + Froxelizer() = default; + + /// Rebuild the layout and re-bin the lights if needed + void update(const Camera* cam, const Viewport* viewport, const LightList& lights); + + // shader-facing scalars + const Vector4f& getTileParams() const { return mTileParams; } // (countX, countY, yFix, tileSizePx) + const Vector4f& getDepthParams() const { return mDepthParams; } // (scaleZ, biasZ, linZ, sliceCount) + + // GPU buffer data + const std::vector& getGrid() const { return mGrid; } // (offset << 8) | count per froxel + const std::vector& getRecords() const { return mRecords; } // flat light-index list + + enum : uint32 + { + /// 4096 froxels fit in a 16 KiB buffer, the minimum guaranteed in GLES 3.x and Vulkan 1.1 + MAX_FROXELS = 4096, + MAX_FROXEL_SLICES = 16, + /// 16384 light indices (uint8) fit in a 16 KiB buffer + MAX_FROXEL_RECORDS = 16384, + MAX_LIGHTS = 255 ///< @note stored as uint8 in the froxel records + }; + +private: + void rebuildLayout(const Camera* cam, const Viewport* viewport); + void binLights(const Camera* cam, const LightList& lights); + + void updateDepthParams(const Camera* cam); + int findSliceZ(float viewSpaceZ) const; + + int mLastWidth = 0, mLastHeight = 0; + float mZLightNear = 0.0f, mZLightFar = 0.0f; + + Vector4f mTileParams = Vector4f(0); + Vector4f mDepthParams = Vector4f(0); + // fixed 4096 entries: (offset << 8) | count + std::vector mGrid; + // one byte per light index (max 255 lights) + std::vector mRecords; + // temporary per-froxel light lists + std::vector> mFroxelLights; +}; + +} // namespace Ogre + +#endif \ No newline at end of file diff --git a/OgreMain/src/OgreGpuProgramParams.cpp b/OgreMain/src/OgreGpuProgramParams.cpp index 982d06caff6..116becbbbaa 100644 --- a/OgreMain/src/OgreGpuProgramParams.cpp +++ b/OgreMain/src/OgreGpuProgramParams.cpp @@ -185,6 +185,8 @@ namespace Ogre AutoConstantDefinition(ACT_LIGHT_CUSTOM, "light_custom", 4, ET_REAL, ACDT_INT), AutoConstantDefinition(ACT_POINT_PARAMS, "point_params", 4, ET_REAL, ACDT_NONE), AutoConstantDefinition(ACT_MATERIAL_LOD_INDEX, "material_lod_index", 1, ET_INT, ACDT_NONE), + AutoConstantDefinition(ACT_FROXEL_TILE_PARAMS, "froxel_tile_params", 4, ET_REAL, ACDT_NONE), + AutoConstantDefinition(ACT_FROXEL_DEPTH_PARAMS, "froxel_depth_params", 4, ET_REAL, ACDT_NONE), // NOTE: new auto constants must be added before this line, as the following are merely aliases // to allow legacy world_ names in scripts @@ -661,7 +663,7 @@ namespace Ogre , mActivePassIterationIndex(std::numeric_limits::max()) , mUseLinearColours(false) { - static_assert((sizeof(AutoConstantDictionary) / sizeof(AutoConstantDefinition) - 5) == ACT_MATERIAL_LOD_INDEX, + static_assert((sizeof(AutoConstantDictionary) / sizeof(AutoConstantDefinition) - 5) == ACT_FROXEL_DEPTH_PARAMS, "AutoConstantDictionary out of sync"); } GpuProgramParameters::~GpuProgramParameters() {} @@ -1017,6 +1019,8 @@ namespace Ogre case ACT_PASS_NUMBER: case ACT_TEXTURE_MATRIX: case ACT_LOD_CAMERA_POSITION: + case ACT_FROXEL_TILE_PARAMS: + case ACT_FROXEL_DEPTH_PARAMS: return (uint16)GPV_GLOBAL; @@ -2091,7 +2095,12 @@ namespace Ogre source->getSpotlightViewProjMatrix(l),ac.elementCount); } break; - + case ACT_FROXEL_TILE_PARAMS: + _writeRawConstant(ac.physicalIndex, source->getFroxelTileParams(), ac.elementCount); + break; + case ACT_FROXEL_DEPTH_PARAMS: + _writeRawConstant(ac.physicalIndex, source->getFroxelDepthParams(), ac.elementCount); + break; default: break; }; From c5a1e615882e273a4cc1668b1032595260a473a8 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Sun, 2 Aug 2026 21:14:01 +0200 Subject: [PATCH 3/5] RTSS: add SRS_CLUSTERED_LIGHT_CULLING --- .../include/OgreShaderSubRenderState.h | 2 + .../src/OgreShaderClusteredLightCulling.cpp | 132 ++++++++++++++++++ .../src/OgreShaderClusteredLightCulling.h | 71 ++++++++++ .../src/OgreShaderGenerator.cpp | 4 + .../src/OgreShaderPrecompiledHeaders.h | 1 + Media/RTShaderLib/RTSLib_Froxels.glsl | 96 +++++++++++++ 6 files changed, 306 insertions(+) create mode 100644 Components/RTShaderSystem/src/OgreShaderClusteredLightCulling.cpp create mode 100644 Components/RTShaderSystem/src/OgreShaderClusteredLightCulling.h create mode 100644 Media/RTShaderLib/RTSLib_Froxels.glsl diff --git a/Components/RTShaderSystem/include/OgreShaderSubRenderState.h b/Components/RTShaderSystem/include/OgreShaderSubRenderState.h index 9d94eced5ba..a4d330a31e7 100644 --- a/Components/RTShaderSystem/include/OgreShaderSubRenderState.h +++ b/Components/RTShaderSystem/include/OgreShaderSubRenderState.h @@ -76,6 +76,8 @@ _OgreRTSSExport extern const String SRS_TRIPLANAR_TEXTURING; _OgreRTSSExport extern const String SRS_LAYERED_BLENDING; /// Include skinning calculations for Skeletal Animation in the shader to move computations to the GPU _OgreRTSSExport extern const String SRS_HARDWARE_SKINNING; +/// Enable clustered light culling and per-fragment light list generation +_OgreRTSSExport extern const String SRS_CLUSTERED_LIGHT_CULLING; /** This class is the base interface of sub part from a shader based rendering pipeline. * All sub parts implementations should derive from it and implement the needed methods. diff --git a/Components/RTShaderSystem/src/OgreShaderClusteredLightCulling.cpp b/Components/RTShaderSystem/src/OgreShaderClusteredLightCulling.cpp new file mode 100644 index 00000000000..e32c0ce6d52 --- /dev/null +++ b/Components/RTShaderSystem/src/OgreShaderClusteredLightCulling.cpp @@ -0,0 +1,132 @@ +#include "OgreLogManager.h" +#include "OgreShaderPrecompiledHeaders.h" +#ifdef RTSHADER_SYSTEM_BUILD_EXT_SHADERS + +namespace Ogre { +namespace RTShader { + +const String SRS_CLUSTERED_LIGHT_CULLING = "SGX_FroxelClustered"; + +//----------------------------------------------------------------------- +const String& ClusteredLightCulling::getType() const { return SRS_CLUSTERED_LIGHT_CULLING; } + +//----------------------------------------------------------------------- +void ClusteredLightCulling::copyFrom(const SubRenderState& rhs) +{ + const auto& rhsFroxel = static_cast(rhs); + mDebugVisualisation = rhsFroxel.mDebugVisualisation; +} + +//----------------------------------------------------------------------- +bool ClusteredLightCulling::setParameter(const String& name, const String& value) +{ + if (name == "debug") + return StringConverter::parse(value, mDebugVisualisation); + + return false; +} + +//----------------------------------------------------------------------- +bool ClusteredLightCulling::preAddToRenderState(const RenderState* renderState, Pass* srcPass, Pass* dstPass) +{ + if (!srcPass->getLightingEnabled()) + return false; + + // must match the light count used by the lighting SRS, so the + // ACT_LIGHT_POSITION_VIEW_SPACE_ARRAY parameter is shared instead of duplicated + mLightCount = renderState->getLightCount(); + + if (srcPass->getIteratePerLight()) + { + mLightCount = srcPass->getLightCountPerIteration(); + } + + if(srcPass->getMaxSimultaneousLights() == 0) + { + mLightCount = 0; + } + + return mLightCount > 0; +} + +bool ClusteredLightCulling::createCpuSubPrograms(ProgramSet* programSet) +{ + Program* vsProgram = programSet->getCpuProgram(GPT_VERTEX_PROGRAM); + Program* psProgram = programSet->getCpuProgram(GPT_FRAGMENT_PROGRAM); + + psProgram->addDependency("RTSLib_Froxels"); + + if(mDebugVisualisation) + psProgram->addPreprocessorDefines("DEBUG_FROXELS"); + + Function* vsMain = vsProgram->getEntryPointFunction(); + Function* psMain = psProgram->getEntryPointFunction(); + + // make sure the shared parameter block exists before we reference it + auto aps = ShaderGenerator::getSingleton().getActiveSceneManager()->_getAutoParamDataSource(); + const_cast(aps)->updateFroxelData(); + + auto froxelData = GpuProgramManager::getSingleton().getSharedParameters("OgreFroxels"); + psProgram->addSharedParameters(froxelData); + + auto froxelGrid = ParameterFactory::createSharedParam(froxelData, "froxelGrid"); + auto froxelRecords = ParameterFactory::createSharedParam(froxelData, "froxelRecords"); + + auto tileParams = psProgram->resolveParameter(GpuProgramParameters::ACT_FROXEL_TILE_PARAMS); + auto depthParams = psProgram->resolveParameter(GpuProgramParameters::ACT_FROXEL_DEPTH_PARAMS); + auto lightPositions = + psProgram->resolveParameter(GpuProgramParameters::ACT_LIGHT_POSITION_VIEW_SPACE_ARRAY, mLightCount); + + // fragment position in projective space (provided by SRS_TRANSFORM) + auto vsOutPos = vsMain->getOutputParameter(Parameter::SPC_POSITION_PROJECTIVE_SPACE); + auto fragCoord = psMain->resolveInputParameter(vsOutPos); + + auto lightList = psMain->resolveLocalStructParameter("FroxelLights", "lights"); + + // runs before the lighting stage (FFP_PS_COLOUR_BEGIN + 1) + auto stage = psMain->getStage(FFP_PS_COLOUR_BEGIN); + + std::vector args = {In(fragCoord).xyz(), In(tileParams), In(depthParams), In(lightPositions), + At(0), In(froxelGrid), Out(lightList)}; + if(mDebugVisualisation) + args.push_back(InOut(psProgram->resolveParameter(GpuProgramParameters::ACT_DERIVED_SCENE_COLOUR)).xyz()); + stage.callFunction("getFroxelLights", args); + return true; +} + +//----------------------------------------------------------------------- +const String& ClusteredLightCullingFactory::getType() const { return SRS_CLUSTERED_LIGHT_CULLING; } + +//----------------------------------------------------------------------- +SubRenderState* ClusteredLightCullingFactory::createInstance(const ScriptProperty& prop, Pass* pass, + SGScriptTranslator* translator) +{ + if (prop.name != "light_clustering" || prop.values.empty()) + return NULL; + + if (prop.values[0] != "froxel") + return NULL; + + auto ret = createOrRetrieveInstance(translator); + + for (auto it = prop.values.begin() + 1; it != prop.values.end(); ++it) + { + if (!ret->setParameter(*it, "true")) + translator->emitError(*it); + } + + return ret; +} + +//----------------------------------------------------------------------- +void ClusteredLightCullingFactory::writeInstance(MaterialSerializer* ser, SubRenderState* subRenderState, + Pass* srcPass, Pass* dstPass) +{ + ser->writeAttribute(4, "light_clustering"); + ser->writeValue("froxel"); +} + +} +} + +#endif \ No newline at end of file diff --git a/Components/RTShaderSystem/src/OgreShaderClusteredLightCulling.h b/Components/RTShaderSystem/src/OgreShaderClusteredLightCulling.h new file mode 100644 index 00000000000..a06e52ac66f --- /dev/null +++ b/Components/RTShaderSystem/src/OgreShaderClusteredLightCulling.h @@ -0,0 +1,71 @@ +#ifndef _ShaderExFroxelClustered_ +#define _ShaderExFroxelClustered_ + +#include "OgreShaderPrerequisites.h" +#ifdef RTSHADER_SYSTEM_BUILD_EXT_SHADERS +#include "OgreShaderSubRenderState.h" +#include "OgreShaderFFPRenderState.h" +#include "OgreShaderParameter.h" + +namespace Ogre { +namespace RTShader { + +/** \addtogroup Optional +* @{ +*/ +/** \addtogroup RTShader +* @{ +*/ + +/** Clustered (froxel) light culling. + +Culls the global light list against the froxel grid and publishes the resulting +per-fragment light list as the local parameter @c lights of type @c FroxelLights. +Lighting sub render states +*/ +class ClusteredLightCulling : public SubRenderState +{ +public: + const String& getType() const override; + /// must run before any lighting SRS so the light list local exists + int getExecutionOrder() const override { return FFP_LIGHTING - 1; } + + void copyFrom(const SubRenderState& rhs) override; + bool setParameter(const String& name, const String& value) override; + bool preAddToRenderState(const RenderState* renderState, Pass* srcPass, Pass* dstPass) override; + +protected: + void updateGpuProgramsParams(Renderable*, const Pass*, const AutoParamDataSource* source, + const LightList*) override + { + const_cast(source)->updateFroxelData(); + } + + bool createCpuSubPrograms(ProgramSet* programSet) override; + + int mLightCount = 0; + bool mDebugVisualisation = false; +}; + +/// A factory that enables creation of ClusteredLightCulling instances. +class ClusteredLightCullingFactory : public SubRenderStateFactory +{ +public: + const String& getType() const override; + SubRenderState* createInstance(const ScriptProperty& prop, Pass* pass, + SGScriptTranslator* translator) override; + void writeInstance(MaterialSerializer* ser, SubRenderState* subRenderState, Pass* srcPass, + Pass* dstPass) override; + +protected: + SubRenderState* createInstanceImpl() override { return OGRE_NEW ClusteredLightCulling; } +}; + +/** @} */ +/** @} */ + +} +} + +#endif +#endif \ No newline at end of file diff --git a/Components/RTShaderSystem/src/OgreShaderGenerator.cpp b/Components/RTShaderSystem/src/OgreShaderGenerator.cpp index 006c6a7b86f..e031b5b347c 100644 --- a/Components/RTShaderSystem/src/OgreShaderGenerator.cpp +++ b/Components/RTShaderSystem/src/OgreShaderGenerator.cpp @@ -280,6 +280,10 @@ void ShaderGenerator::createBuiltinSRSFactories() curFactory = OGRE_NEW HardwareSkinningFactory; addSubRenderStateFactory(curFactory); mBuiltinSRSFactories.push_back(curFactory); + + curFactory = OGRE_NEW ClusteredLightCullingFactory; + addSubRenderStateFactory(curFactory); + mBuiltinSRSFactories.push_back(curFactory); } curFactory = OGRE_NEW TriplanarTexturingFactory; diff --git a/Components/RTShaderSystem/src/OgreShaderPrecompiledHeaders.h b/Components/RTShaderSystem/src/OgreShaderPrecompiledHeaders.h index e4f331f77e3..3ec5def05de 100644 --- a/Components/RTShaderSystem/src/OgreShaderPrecompiledHeaders.h +++ b/Components/RTShaderSystem/src/OgreShaderPrecompiledHeaders.h @@ -81,6 +81,7 @@ THE SOFTWARE. #include "OgreShaderExWBOIT.h" #include "OgreShaderCookTorranceLighting.h" #include "OgreShaderImageBasedLighting.h" +#include "OgreShaderClusteredLightCulling.h" #include "OgreShaderProgramWriter.h" #include "OgreShaderProgramWriterManager.h" diff --git a/Media/RTShaderLib/RTSLib_Froxels.glsl b/Media/RTShaderLib/RTSLib_Froxels.glsl new file mode 100644 index 00000000000..c41708fa0ca --- /dev/null +++ b/Media/RTShaderLib/RTSLib_Froxels.glsl @@ -0,0 +1,96 @@ +// This file is part of the OGRE project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at https://www.ogre3d.org/licensing. +// SPDX-License-Identifier: MIT + +#define USE_FROXELS + +#define FROXEL_GRID_VEC4_COUNT 1024 // 4096 froxels / 4 per uvec4 +#define FROXEL_RECORD_VEC4_COUNT 1024 // 16384 light indices / 16 per uvec4 + +// macros for lighting SubRenderStates to get the current froxel lights and light index +#define CURRENT_LIGHT_COUNT lights.count +#define GET_LIGHT_INDEX(n) int(getLightIndex(lights, n, froxelRecords)) + +struct FroxelLights +{ + int offset; + int count; + int dirLights; +}; + +// fragCoord = gl_FragCoord.xyz: .xy in pixels, .z = screen depth +// froxel_params = (countX, countY, fixY, tileSizePx) +// froxel_z_params = (scaleZ, biasZ, linZ, sliceCount) +uvec3 getFroxelCoord(in vec3 fragCoord, in vec4 froxel_params, in vec4 froxel_z_params) +{ + if(froxel_params.z > 0.0) // needs flipping + fragCoord.y = froxel_params.z - fragCoord.y; + + uint x = min(uint(fragCoord.x / froxel_params.w), uint(froxel_params.x) - 1u); + uint y = min(uint(fragCoord.y / froxel_params.w), uint(froxel_params.y) - 1u); + + // recipViewZ = zLightFar / viewZ, coefficients fitted from the real projection matrix + float recipViewZ = froxel_z_params.x * fragCoord.z + froxel_z_params.y; + float sliceZ = log2(max(recipViewZ, 1e-8)) * froxel_z_params.z; + uint z = uint(clamp(sliceZ + froxel_z_params.w, 0.0, froxel_z_params.w - 1.0)); + + return uvec3(x, y, z); +} + +uint getLightIndex(uint record, in uvec4 froxelRecords[FROXEL_RECORD_VEC4_COUNT]) +{ + uint word = froxelRecords[record >> 4][(record >> 2) & 3u]; // which uint + return (word >> ((record & 3u) * 8u)) & 0xFFu; // which byte +} + +// wrapper considering dir lights +int getLightIndex(in FroxelLights lights, int i, in uvec4 froxelRecords[FROXEL_RECORD_VEC4_COUNT]) +{ + return i < lights.dirLights ? i : int(getLightIndex(uint(lights.offset + i), froxelRecords)); +} + +// jet-like ramp (blue -> cyan -> green -> yellow -> red), t in [0, 1] +vec3 debugRamp(float t) +{ + t = saturate(t); + return saturate(vec3(1.5 - abs(4.0 * t - 3.0), + 1.5 - abs(4.0 * t - 2.0), + 1.5 - abs(4.0 * t - 1.0))); +} +void debugFroxelOccupancy(in FroxelLights lights, inout vec3 color) +{ +#ifdef LIGHT_COUNT + float count = float(lights.count - lights.dirLights); + if (count < 1.0) + return; + + float t = saturate(count / float(LIGHT_COUNT - lights.dirLights)); + color = mix(color, debugRamp(t), 0.8); +#endif +} + +void getFroxelLights(in vec3 fragCoord, in vec4 froxel_params, in vec4 froxel_z_params, + in vec4 light0Pos, + in uvec4 froxelGrid[FROXEL_GRID_VEC4_COUNT], + out FroxelLights lights +#ifdef DEBUG_FROXELS + , inout vec3 color +#endif + ) +{ + lights.dirLights = int(light0Pos.w == 0.0); + + uvec3 f = getFroxelCoord(fragCoord, froxel_params, froxel_z_params); + uint fidx = (f.z * uint(froxel_params.y) + f.y) * uint(froxel_params.x) + f.x; + uint grid = froxelGrid[fidx >> 2][fidx & 3u]; + lights.offset = int(grid >> 8) - lights.dirLights; + lights.count = int(grid & 0xFFu) + lights.dirLights; + +#ifdef DEBUG_FROXELS + // depth + //color = mix(color, debugRamp((float(f.z) + 0.5f) / max(froxel_z_params.w - 1.0, 1.0)), 0.8); + //color = mix(color, debugRamp(fract(float(f.x + f.y) / 8.0)), 0.8); + debugFroxelOccupancy(lights, color); +#endif +} \ No newline at end of file From 4f31d66ef06763bb0d82083d40ed07c1d6188984 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Sun, 2 Aug 2026 21:14:07 +0200 Subject: [PATCH 4/5] RTSS: allow injecting clustered lights into light SRS --- .../src/OgreShaderCookTorranceLighting.cpp | 7 +++++++ .../src/OgreShaderExPerPixelLighting.cpp | 2 +- .../RTShaderSystem/src/OgreShaderFFPLighting.cpp | 11 +++++++++-- .../RTShaderSystem/src/OgreShaderFFPLighting.h | 2 +- Media/RTShaderLib/SGXLib_CookTorrance.glsl | 12 +++++++++++- Media/RTShaderLib/SGXLib_PerPixelLighting.glsl | 13 ++++++++++++- 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/Components/RTShaderSystem/src/OgreShaderCookTorranceLighting.cpp b/Components/RTShaderSystem/src/OgreShaderCookTorranceLighting.cpp index 0025e037e57..1f6488a06f8 100644 --- a/Components/RTShaderSystem/src/OgreShaderCookTorranceLighting.cpp +++ b/Components/RTShaderSystem/src/OgreShaderCookTorranceLighting.cpp @@ -144,6 +144,13 @@ bool CookTorranceLighting::createCpuSubPrograms(ProgramSet* programSet) In(lightDiffuse), In(pointParams), In(lightDirView), In(spotParams), In(pixelParams), InOut(outDiffuse).xyz()}; + if(auto lights = psMain->getLocalParameter("lights")) + { + auto froxelData = GpuProgramManager::getSingleton().getSharedParameters("OgreFroxels"); + auto froxelRecords = ParameterFactory::createSharedParam(froxelData, "froxelRecords"); + params.insert(params.begin(), {In(lights), In(froxelRecords)}); + } + if(mLtcLUT1SamplerIndex > -1) { auto ltcLUT1 = psProgram->resolveParameter(GCT_SAMPLER2D, "ltcLUT1Sampler", mLtcLUT1SamplerIndex); diff --git a/Components/RTShaderSystem/src/OgreShaderExPerPixelLighting.cpp b/Components/RTShaderSystem/src/OgreShaderExPerPixelLighting.cpp index 19c27596e19..2ddb8dfce98 100644 --- a/Components/RTShaderSystem/src/OgreShaderExPerPixelLighting.cpp +++ b/Components/RTShaderSystem/src/OgreShaderExPerPixelLighting.cpp @@ -239,7 +239,7 @@ bool PerPixelLighting::addFunctionInvocations(ProgramSet* programSet) mShadowFactor = psMain->getLocalParameter("lShadowFactor"); - addIlluminationInvocation(stage); + addIlluminationInvocation(stage, psMain->getLocalParameter("lights")); // Assign back temporary variables stage.assign(mOutDiffuse, mInDiffuse); diff --git a/Components/RTShaderSystem/src/OgreShaderFFPLighting.cpp b/Components/RTShaderSystem/src/OgreShaderFFPLighting.cpp index 6bf49857475..f412b08428b 100644 --- a/Components/RTShaderSystem/src/OgreShaderFFPLighting.cpp +++ b/Components/RTShaderSystem/src/OgreShaderFFPLighting.cpp @@ -190,7 +190,7 @@ bool FFPLighting::addFunctionInvocations(ProgramSet* programSet) addGlobalIlluminationInvocation(stage); // Add per light functions. - addIlluminationInvocation(stage); + addIlluminationInvocation(stage, nullptr); auto psProgram = programSet->getCpuProgram(GPT_FRAGMENT_PROGRAM); auto psMain = psProgram->getMain(); @@ -258,7 +258,7 @@ void FFPLighting::addGlobalIlluminationInvocation(const FunctionStageRef& stage) } //----------------------------------------------------------------------- -void FFPLighting::addIlluminationInvocation(const FunctionStageRef& stage) +void FFPLighting::addIlluminationInvocation(const FunctionStageRef& stage, const ParameterPtr& lights) { if(mLightCount == 0) return; @@ -282,6 +282,13 @@ void FFPLighting::addIlluminationInvocation(const FunctionStageRef& stage) args.insert(args.end(), {In(mSpecularColours), In(mSurfaceShininess), InOut(mOutSpecular).xyz()}); + if(lights) + { + auto froxelData = GpuProgramManager::getSingleton().getSharedParameters("OgreFroxels"); + auto froxelRecords = ParameterFactory::createSharedParam(froxelData, "froxelRecords"); + args.insert(args.end(), {In(lights), In(froxelRecords)}); + } + stage.callFunction("FFP_Lights", args); } diff --git a/Components/RTShaderSystem/src/OgreShaderFFPLighting.h b/Components/RTShaderSystem/src/OgreShaderFFPLighting.h index fdddd2df69d..d494caace12 100644 --- a/Components/RTShaderSystem/src/OgreShaderFFPLighting.h +++ b/Components/RTShaderSystem/src/OgreShaderFFPLighting.h @@ -145,7 +145,7 @@ class FFPLighting : public SubRenderState /** Internal method that adds illumination component functions invocations. */ - void addIlluminationInvocation(const FunctionStageRef& stage); + void addIlluminationInvocation(const FunctionStageRef& stage, const ParameterPtr& lights); // Track per vertex colour type. diff --git a/Media/RTShaderLib/SGXLib_CookTorrance.glsl b/Media/RTShaderLib/SGXLib_CookTorrance.glsl index c8fee3f3309..b9ba2d81226 100644 --- a/Media/RTShaderLib/SGXLib_CookTorrance.glsl +++ b/Media/RTShaderLib/SGXLib_CookTorrance.glsl @@ -188,6 +188,11 @@ void PBR_MakeParams(in vec3 baseColor, in vec3 ormParam, in PixelParams pixel) pixel.energyCompensation = vec3_splat(0.0); // will be set later } +#ifndef USE_FROXELS +#define CURRENT_LIGHT_COUNT LIGHT_COUNT +#define GET_LIGHT_INDEX(n) n +#endif + #if LIGHT_COUNT > 0 void PBR_Lights( #ifdef SHADOWLIGHT_COUNT @@ -196,6 +201,10 @@ void PBR_Lights( #ifdef HAVE_AREA_LIGHTS in sampler2D ltcLUT1, in sampler2D ltcLUT2, +#endif +#ifdef USE_FROXELS + in FroxelLights lights, + in uvec4 froxelRecords[FROXEL_RECORD_VEC4_COUNT], #endif in vec3 vNormal, in vec3 viewPos, @@ -212,8 +221,9 @@ void PBR_Lights( // See "Multiple-Scattering Microfacet BSDFs with the Smith Model" pixel.energyCompensation = 1.0 + pixel.f0 * (1.0 / pixel.dfg.y - 1.0); - for(int i = 0; i < LIGHT_COUNT; i++) + for(int n = 0; n < CURRENT_LIGHT_COUNT; n++) { + int i = GET_LIGHT_INDEX(n); #ifdef HAVE_AREA_LIGHTS if(spotParams[i].w == 2.0) { diff --git a/Media/RTShaderLib/SGXLib_PerPixelLighting.glsl b/Media/RTShaderLib/SGXLib_PerPixelLighting.glsl index 87c61ce76b8..e635f011e76 100644 --- a/Media/RTShaderLib/SGXLib_PerPixelLighting.glsl +++ b/Media/RTShaderLib/SGXLib_PerPixelLighting.glsl @@ -127,6 +127,11 @@ void evaluateLight( #endif } +#ifndef USE_FROXELS +#define CURRENT_LIGHT_COUNT LIGHT_COUNT +#define GET_LIGHT_INDEX(n) n +#endif + #if LIGHT_COUNT > 0 void FFP_Lights( #ifdef SHADOWLIGHT_COUNT @@ -151,11 +156,17 @@ void FFP_Lights( , in f32vec4 vSpecularColour[LIGHT_COUNT], in float fSpecularPower, inout vec3 vOutSpecular +#endif +#ifdef USE_FROXELS + , in FroxelLights lights, + in uvec4 froxelRecords[FROXEL_RECORD_VEC4_COUNT] #endif ) { - for (int i = 0; i < LIGHT_COUNT; ++i) + for (int n = 0; n < CURRENT_LIGHT_COUNT; ++n) { + int i = GET_LIGHT_INDEX(n); + // resolve per-light inputs: vertex colour tracking and shadows vec3 dcol = vDiffuseColour[i].rgb; #ifdef TVC_DIFFUSE From 00e34a6f5ee1b6e13bd304b26775c2a8161a1e20 Mon Sep 17 00:00:00 2001 From: Pavel Rojtberg Date: Sun, 2 Aug 2026 00:41:03 +0200 Subject: [PATCH 5/5] Samples: MultiLight - use for clustered light culling --- .../GLSL/SegmentedPerPixelLighting.glsl | 167 ---- .../HLSL_Cg/SegmentedPerPixelLighting.cg | 179 ---- .../include/RTShaderSRSSegmentedLights.h | 249 ------ .../include/SegmentedDynamicLightManager.h | 167 ---- .../src/RTShaderSRSSegmentedLights.cpp | 786 ------------------ .../src/SegmentedDynamicLightManager.cpp | 433 ---------- .../src/ShaderSystemMultiLight.cpp | 9 - .../include/ShaderSystemMultiLight.h | 154 +--- 8 files changed, 39 insertions(+), 2105 deletions(-) delete mode 100644 Samples/Media/materials/programs/GLSL/SegmentedPerPixelLighting.glsl delete mode 100644 Samples/Media/materials/programs/HLSL_Cg/SegmentedPerPixelLighting.cg delete mode 100644 Samples/ShaderSystemMultiLight/include/RTShaderSRSSegmentedLights.h delete mode 100644 Samples/ShaderSystemMultiLight/include/SegmentedDynamicLightManager.h delete mode 100644 Samples/ShaderSystemMultiLight/src/RTShaderSRSSegmentedLights.cpp delete mode 100644 Samples/ShaderSystemMultiLight/src/SegmentedDynamicLightManager.cpp delete mode 100644 Samples/ShaderSystemMultiLight/src/ShaderSystemMultiLight.cpp rename Samples/{ShaderSystemMultiLight => Simple}/include/ShaderSystemMultiLight.h (58%) diff --git a/Samples/Media/materials/programs/GLSL/SegmentedPerPixelLighting.glsl b/Samples/Media/materials/programs/GLSL/SegmentedPerPixelLighting.glsl deleted file mode 100644 index a81d7462237..00000000000 --- a/Samples/Media/materials/programs/GLSL/SegmentedPerPixelLighting.glsl +++ /dev/null @@ -1,167 +0,0 @@ -//----------------------------------------------------------------------------- -// Program Name: SL_Lighting -// Program Desc: Per pixel lighting functions. -// Program Type: Vertex/Pixel shader -// Language: GLSL -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -void SL_TransformNormal(in mat4 m, - in vec3 v, - out vec3 vOut) -{ - vOut = mat3(m) * v; -} - -//----------------------------------------------------------------------------- -void SL_TransformPosition(in mat4 mWorldView, - in vec4 vPos, - out vec3 vOut) -{ - vOut = (mWorldView * vPos).xyz; -} - -//----------------------------------------------------------------------------- -void SL_Light_Directional_Diffuse( - in vec3 vNormal, - in vec3 vNegLightDirView, - in vec3 vDiffuseColour, - in vec3 vBaseColour, - out vec3 vOut) -{ - vec3 vNormalView = normalize(vNormal); - float nDotL = dot(vNormalView, vNegLightDirView); - - vOut = vBaseColour + vDiffuseColour * min(max(nDotL, 0.0), 1.0); -} - -//----------------------------------------------------------------------------- -void SL_Light_Directional_DiffuseSpecular( - in vec3 vNormal, - in vec3 vViewPos, - in vec3 vNegLightDirView, - in vec3 vDiffuseColour, - in vec3 vSpecularColour, - in float fSpecularPower, - in vec3 vBaseDiffuseColour, - in vec3 vBaseSpecularColour, - out vec3 vOutDiffuse, - out vec3 vOutSpecular) -{ - vOutDiffuse = vBaseDiffuseColour; - vOutSpecular = vBaseSpecularColour; - - vec3 vNormalView = normalize(vNormal); - float nDotL = dot(vNormalView, vNegLightDirView); - vec3 vView = -normalize(vViewPos); - vec3 vHalfWay = normalize(vView + vNegLightDirView); - float nDotH = dot(vNormalView, vHalfWay); - - nDotL = max(nDotL, 0); - vOutDiffuse += vDiffuseColour * nDotL; - vOutSpecular += vSpecularColour * pow(clamp(nDotH, 0.0, 1.0), fSpecularPower); -} - -//----------------------------------------------------------------------------- -void SL_Light_Ambient_Diffuse_Inner( - in vec3 vNormal, - in vec3 vLightView, - in float fLightDist, - in vec3 vNegLightDirView, - in vec3 vSpotParams, - in vec3 vDiffuseColour, - inout vec3 vColorOut) -{ - float fLightDistInv = 1.0 / fLightDist; - float nDotL = dot(vNormal, vLightView) * fLightDistInv; - - float fAtten = (1.0 - (fLightDist * vSpotParams.x)); - fAtten = fAtten * fAtten; - - float rho = dot(vNegLightDirView, vLightView) * fLightDistInv; - float fSpotT = min(max((rho - vSpotParams.y) * vSpotParams.z, 0.0), 1.0); - - nDotL = (nDotL < 0.0 ? 0.0 : 1.0) * (0.7 + (0.3 * nDotL)); - vColorOut += vDiffuseColour * 2.0* nDotL * fAtten * fSpotT; -} - -//----------------------------------------------------------------------------- -void SL_Light_Ambient_Diffuse( - in vec3 vNormal, - in vec3 vViewPos, - in vec3 vLightPosView, - in vec3 vNegLightDirView, - in vec3 vSpotParams, - in vec3 vDiffuseColour, - inout vec3 vColorOut) -{ - vec3 vLightView = vLightPosView - vViewPos; - float fLightDist = length(vLightView); - if (fLightDist * vSpotParams.x < 1) - { - SL_Light_Ambient_Diffuse_Inner(vNormal, vLightView, fLightDist, - vNegLightDirView, vSpotParams, vDiffuseColour, vColorOut); - } -} - -//----------------------------------------------------------------------------- -void SL_Light_Segment_Texture_Ambient_Diffuse( - in vec3 vNormal, - in vec3 vViewPos, - in sampler2D dataTexture, - in vec2 lightIndexLimit, - in vec4 lightBounds, - in float invWidth, - in float invHeight, - inout vec3 vColorOut) -{ - float widthOffset = invWidth * 0.5; - float heightOffset = invHeight * 0.5; - - vec2 indexes = (vViewPos.xz - lightBounds.xy) * lightBounds.zw; - indexes = min(max(indexes, 0.0), 8.0); - int index = int(indexes.x) + int(indexes.y) * 9; - widthOffset += invWidth * 3.0 * float(index); - - vec4 indexBounds = textureLod(dataTexture, vec2(widthOffset,heightOffset), 0.0); - int toIndex = int(min(lightIndexLimit.y, indexBounds.x)); - for(int i = int(lightIndexLimit.x); i <= toIndex; ++i) - { - float heightCoord = heightOffset + invHeight * float(i); - vec4 dat1 = textureLod(dataTexture, vec2(widthOffset,heightCoord), 0.0); - - vec3 vLightView = dat1.xyz - vViewPos; - float fLightDist = length(vLightView); - if (fLightDist * dat1.w < 1.0) - { - vec4 dat2 = textureLod(dataTexture, vec2(widthOffset + invWidth,heightCoord),0.0); - vec4 dat3 = textureLod(dataTexture, vec2(widthOffset + invWidth * 2.0,heightCoord),0.0); - SL_Light_Ambient_Diffuse_Inner(vNormal, vLightView, fLightDist, dat2.xyz, vec3(dat1.w, dat2.w, dat3.w), dat3.xyz, vColorOut); - } - } -} - -//----------------------------------------------------------------------------- -void SL_Light_Segment_Debug( - in vec3 vNormal, - in vec3 vViewPos, - in sampler2D dataTexture, - in vec2 lightIndexLimit, - in vec4 lightBounds, - in float invWidth, - in float invHeight, - inout vec3 vColorOut) -{ - float widthOffset = invWidth * 0.5; - float heightOffset = invHeight * 0.5; - - vec2 indexes = (vViewPos.xz - lightBounds.xy) * lightBounds.zw; - indexes = min(max(indexes, 0.0), 8.0); - int index = int(indexes.x + (indexes.y * 9.0)); - vec4 indexBounds = textureLod(dataTexture, vec2(widthOffset,heightOffset),0.0); - - vec2 debugColors = vColorOut.xy * 0.5 + ((mod(floor(indexes.xy),2.0) == vec2(0)) ? 0.1 : 0.2); - vColorOut.xy = debugColors; - int toIndex = int(min(lightIndexLimit.y, indexBounds.x)); - vColorOut.z = (float(toIndex) - lightIndexLimit.x) / 32.0; -} diff --git a/Samples/Media/materials/programs/HLSL_Cg/SegmentedPerPixelLighting.cg b/Samples/Media/materials/programs/HLSL_Cg/SegmentedPerPixelLighting.cg deleted file mode 100644 index dd509fe4996..00000000000 --- a/Samples/Media/materials/programs/HLSL_Cg/SegmentedPerPixelLighting.cg +++ /dev/null @@ -1,179 +0,0 @@ - - -//----------------------------------------------------------------------------- -// Program Name: SL_Lighting -// Program Desc: Per pixel lighting functions. -// Program Type: Vertex/Pixel shader -// Language: CG -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -void SL_TransformNormal(in float4x4 m, - in float3 v, - out float3 vOut) -{ - vOut = mul((float3x3)m, v); -} - -//----------------------------------------------------------------------------- -void SL_TransformPosition(in float4x4 mWorldView, - in float4 vPos, - out float3 vOut) -{ - vOut = mul(mWorldView, vPos).xyz; -} - -//----------------------------------------------------------------------------- -void SL_Light_Directional_Diffuse( - in float3 vNormal, - in float3 vNegLightDirView, - in float3 vDiffuseColour, - in float3 vBaseColour, - out float3 vOut) -{ - float3 vNormalView = normalize(vNormal); - float nDotL = dot(vNormalView, vNegLightDirView); - - vOut = vBaseColour + vDiffuseColour * saturate(nDotL); -} - -//----------------------------------------------------------------------------- -void SL_Light_Directional_DiffuseSpecular( - in float3 vNormal, - in float3 vViewPos, - in float3 vNegLightDirView, - in float3 vDiffuseColour, - in float3 vSpecularColour, - in float fSpecularPower, - in float3 vBaseDiffuseColour, - in float3 vBaseSpecularColour, - out float3 vOutDiffuse, - out float3 vOutSpecular) -{ - vOutDiffuse = vBaseDiffuseColour; - vOutSpecular = vBaseSpecularColour; - - float3 vNormalView = normalize(vNormal); - float nDotL = dot(vNormalView, vNegLightDirView); - float3 vView = -normalize(vViewPos); - float3 vHalfWay = normalize(vView + vNegLightDirView); - float nDotH = dot(vNormalView, vHalfWay); - - nDotL = max(nDotL, 0); - vOutDiffuse += vDiffuseColour * nDotL; - vOutSpecular += vSpecularColour * pow(saturate(nDotH), fSpecularPower); -} - - -//the amount of light taken for ambient light (does not realy on direction) -//const float spotAmbientPart = 1; - - -//----------------------------------------------------------------------------- -void SL_Light_Ambient_Diffuse_Inner( - in float3 vNormal, - in float3 vLightView, - in float fLightDist, - in float3 vNegLightDirView, - in float3 vSpotParams, - in float3 vDiffuseColour, - inout float3 vColorOut) -{ - float fLightDistInv = 1 / fLightDist; - float nDotL = dot(vNormal, vLightView) * fLightDistInv; - - float fAtten = (1 - (fLightDist * vSpotParams.x)); - fAtten = fAtten * fAtten; - - float rho = dot(vNegLightDirView, vLightView) * fLightDistInv; - float fSpotT = saturate((rho - vSpotParams.y) * vSpotParams.z); - - nDotL = step(0,nDotL) * (0.7 + (0.3 * nDotL)); - vColorOut += vDiffuseColour * 2* nDotL * fAtten * fSpotT; - - //if ((fLightDist < 150) && (rho < 0.85)) - // vColorOut.x = 0.5; -} - - -//----------------------------------------------------------------------------- -void SL_Light_Ambient_Diffuse( - in float3 vNormal, - in float3 vViewPos, - in float3 vLightPosView, - in float3 vNegLightDirView, - in float3 vSpotParams, - in float3 vDiffuseColour, - inout float3 vColorOut) -{ - float3 vLightView = vLightPosView - vViewPos; - float fLightDist = length(vLightView); - if (fLightDist * vSpotParams.x < 1) - { - SL_Light_Ambient_Diffuse_Inner(vNormal, vLightView, fLightDist, - vNegLightDirView, vSpotParams, vDiffuseColour, vColorOut); - } -} - -//----------------------------------------------------------------------------- - -void SL_Light_Segment_Texture_Ambient_Diffuse( - in float3 vNormal, - in float3 vViewPos, - in sampler2D dataTexture, - in float2 lightIndexLimit, - in float4 lightBounds, - in float invWidth, - in float invHeight, - inout float3 vColorOut) -{ - float widthOffset = invWidth * 0.5; - float heightOffset = invHeight * 0.5; - - float2 indexes = (vViewPos.xz - lightBounds.xy) * lightBounds.zw; - indexes = clamp(indexes,0,8); - int index = (int)indexes.x + (int)(indexes.y) * 9; - widthOffset += invWidth * 3 * index; - - float4 indexBounds = tex2Dlod(dataTexture, float4(widthOffset,heightOffset,0,0)); - int toIndex = min(lightIndexLimit.y, indexBounds.x); - for(int i = lightIndexLimit.x; i <= toIndex; ++i) - { - float heightCoord = heightOffset + invHeight * i; - float4 dat1 = tex2Dlod(dataTexture, float4(widthOffset,heightCoord,0,0)); - - float3 vLightView = dat1.xyz - vViewPos; - float fLightDist = length(vLightView); - if (fLightDist * dat1.w < 1) - { - float4 dat2 = tex2Dlod(dataTexture, float4(widthOffset + invWidth,heightCoord,0,0)); - float4 dat3 = tex2Dlod(dataTexture, float4(widthOffset + invWidth * 2,heightCoord,0,0)); - SL_Light_Ambient_Diffuse_Inner(vNormal, vLightView, fLightDist, dat2.xyz, float3(dat1.w, dat2.w, dat3.w), dat3.xyz, vColorOut); - } - } -} - - -void SL_Light_Segment_Debug( - in float3 vNormal, - in float3 vViewPos, - in sampler2D dataTexture, - in float2 lightIndexLimit, - in float4 lightBounds, - in float invWidth, - in float invHeight, - inout float3 vColorOut) -{ - float widthOffset = invWidth * 0.5; - float heightOffset = invHeight * 0.5; - - float2 indexes = (vViewPos.xz - lightBounds.xy) * lightBounds.zw; - indexes = clamp(indexes,0,8); - int index = (int)indexes.x + (int)(indexes.y) * 9; - float4 indexBounds = tex2Dlod(dataTexture, float4(widthOffset,heightOffset,0,0)); - - float2 debugColors = vColorOut.xy * 0.5 + ((fmod(floor(indexes.xy),2) == 0) ? 0.1 : 0.2); - vColorOut.xy = debugColors; - int toIndex = min(lightIndexLimit.y, indexBounds.x); - vColorOut.z = (toIndex - lightIndexLimit.x) / 32; -} diff --git a/Samples/ShaderSystemMultiLight/include/RTShaderSRSSegmentedLights.h b/Samples/ShaderSystemMultiLight/include/RTShaderSRSSegmentedLights.h deleted file mode 100644 index 3e55424208b..00000000000 --- a/Samples/ShaderSystemMultiLight/include/RTShaderSRSSegmentedLights.h +++ /dev/null @@ -1,249 +0,0 @@ -/* ------------------------------------------------------------------------------ -This source file is part of OGRE -(Object-oriented Graphics Rendering Engine) -For the latest info, see http://www.ogre3d.org - -Copyright (c) 2000-2014 Torus Knot Software Ltd -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. ------------------------------------------------------------------------------ -*/ -#ifndef _RTShaderSRSSegmentedLights_ -#define _RTShaderSRSSegmentedLights_ - -#include "OgreShaderPrerequisites.h" -#include "OgreShaderSubRenderState.h" -#include "OgreVector.h" -#include "OgreLight.h" -#include "OgreCommon.h" - -/** Segmented lighting sub render state -* The following is sub render state handles lighting in the scene. -* This sub render state is heavily based on PerPixelLighting -*/ -class RTShaderSRSSegmentedLights : public Ogre::RTShader::SubRenderState -{ - - // Interface. -public: - /** Class default constructor */ - RTShaderSRSSegmentedLights(); - - /** - @see SubRenderState::getType. - */ - const Ogre::String& getType() const override; - - /** - @see SubRenderState::getType. - */ - int getExecutionOrder() const override; - - /** - @see SubRenderState::updateGpuProgramsParams. - */ - void updateGpuProgramsParams(Ogre::Renderable* rend, const Ogre::Pass* pass, const Ogre::AutoParamDataSource* source, const Ogre::LightList* pLightList) override; - - /** - @see SubRenderState::copyFrom. - */ - void copyFrom(const Ogre::RTShader::SubRenderState& rhs) override; - - - /** - @see SubRenderState::preAddToRenderState. - */ - bool preAddToRenderState(const Ogre::RTShader::RenderState* renderState, Ogre::Pass* srcPass, Ogre::Pass* dstPass) override; - - - - static Ogre::String Type; - - // Protected types: -protected: - - // Per light parameters. - struct LightParams - { - Ogre::Light::LightTypes mType; // Light type. - Ogre::RTShader::UniformParameterPtr mPosition; // Light position. - Ogre::RTShader::UniformParameterPtr mDirection; // Light direction. - Ogre::RTShader::UniformParameterPtr mSpotParams; // Spot light parameters. - Ogre::RTShader::UniformParameterPtr mDiffuseColour; // Diffuse colour. - Ogre::RTShader::UniformParameterPtr mSpecularColour; // Specular colour. - - }; - - typedef std::vector LightParamsList; - typedef LightParamsList::iterator LightParamsIterator; - typedef LightParamsList::const_iterator LightParamsConstIterator; - - // Protected methods -protected: - - /** - Set the track per vertex colour type. Ambient, Diffuse, Specular and Emissive lighting components source - can be the vertex colour component. To establish such a link one should provide the matching flags to this - sub render state. - */ - void setTrackVertexColourType(Ogre::TrackVertexColourType type) { mTrackVertexColourType = type; } - - /** - Return the current track per vertex type. - */ - Ogre::TrackVertexColourType getTrackVertexColourType() const { return mTrackVertexColourType; } - - - /** - Set the light count per light type that this sub render state will generate. - @see ShaderGenerator::setLightCount. - */ - void setLightCount(int lightCount); - - /** - Set the specular component state. If set to true this sub render state will compute a specular - lighting component in addition to the diffuse component. - @param enable Pass true to enable specular component computation. - */ - void setSpecularEnable(bool enable) { mSpecularEnable = enable; } - - /** - Get the specular component state. - */ - bool getSpecularEnable() const { return mSpecularEnable; } - - - /** - @see SubRenderState::resolveParameters. - */ - bool resolveParameters(Ogre::RTShader::ProgramSet* programSet) override; - - /** Resolve global lighting parameters */ - bool resolveGlobalParameters(Ogre::RTShader::ProgramSet* programSet); - - /** Resolve per light parameters */ - bool resolvePerLightParameters(Ogre::RTShader::ProgramSet* programSet); - - /** - @see SubRenderState::resolveDependencies. - */ - bool resolveDependencies(Ogre::RTShader::ProgramSet* programSet) override; - - /** - @see SubRenderState::addFunctionInvocations. - */ - bool addFunctionInvocations(Ogre::RTShader::ProgramSet* programSet) override; - - - /** - Internal method that adds related vertex shader functions invocations. - */ - bool addVSInvocation(Ogre::RTShader::Function* vsMain, const int groupOrder); - - - /** - Internal method that adds global illumination component functions invocations. - */ - bool addPSGlobalIlluminationInvocationBegin(Ogre::RTShader::Function* psMain, const int groupOrder); - bool addPSGlobalIlluminationInvocationEnd(Ogre::RTShader::Function* psMain, const int groupOrder); - - /** - Internal method that adds per light illumination component functions invocations. - */ - bool addPSIlluminationInvocation(LightParams* curLightParams, Ogre::RTShader::Function* psMain, const int groupOrder); - - /** - Internal method that adds light illumination component calculated from the segmented texture. - */ - bool addPSSegmentedTextureLightInvocation(Ogre::RTShader::Function* psMain, const int groupOrder); - - /** - Internal method that adds the final colour assignments. - */ - bool addPSFinalAssignmentInvocation(Ogre::RTShader::Function* psMain, const int groupOrder); - - - // Attributes. -protected: - Ogre::TrackVertexColourType mTrackVertexColourType; // Track per vertex colour type. - bool mSpecularEnable; // Specular component enabled/disabled. - LightParamsList mLightParamsList; // Light list. - Ogre::RTShader::UniformParameterPtr mWorldMatrix; // World view matrix parameter. - Ogre::RTShader::UniformParameterPtr mWorldITMatrix; // World view matrix inverse transpose parameter. - Ogre::RTShader::ParameterPtr mVSInPosition; // Vertex shader input position parameter. - Ogre::RTShader::ParameterPtr mVSOutWorldPos; // Vertex shader output view position (position in camera space) parameter. - Ogre::RTShader::ParameterPtr mPSInWorldPos; // Pixel shader input view position (position in camera space) parameter. - Ogre::RTShader::ParameterPtr mVSInNormal; // Vertex shader input normal. - Ogre::RTShader::ParameterPtr mVSOutNormal; // Vertex shader output normal. - Ogre::RTShader::ParameterPtr mPSInNormal; // Pixel shader input normal. - Ogre::RTShader::ParameterPtr mPSLocalNormal; - Ogre::RTShader::ParameterPtr mPSTempDiffuseColour; // Pixel shader temporary diffuse calculation parameter. - Ogre::RTShader::ParameterPtr mPSTempSpecularColour; // Pixel shader temporary specular calculation parameter. - Ogre::RTShader::ParameterPtr mPSDiffuse; // Pixel shader input/local diffuse parameter. - Ogre::RTShader::ParameterPtr mPSSpecular; // Pixel shader input/local specular parameter. - Ogre::RTShader::ParameterPtr mPSOutDiffuse; // Pixel shader output diffuse parameter. - Ogre::RTShader::ParameterPtr mPSOutSpecular; // Pixel shader output specular parameter. - Ogre::RTShader::UniformParameterPtr mDerivedSceneColour; // Derived scene colour parameter. - Ogre::RTShader::UniformParameterPtr mLightAmbientColour; // Ambient light colour parameter. - Ogre::RTShader::UniformParameterPtr mDerivedAmbientLightColour; // Derived ambient light colour parameter. - Ogre::RTShader::UniformParameterPtr mSurfaceAmbientColour; // Surface ambient colour parameter. - Ogre::RTShader::UniformParameterPtr mSurfaceDiffuseColour; // Surface diffuse colour parameter. - Ogre::RTShader::UniformParameterPtr mSurfaceSpecularColour; // Surface specular colour parameter. - Ogre::RTShader::UniformParameterPtr mSurfaceEmissiveColour; // Surface emissive colour parameter. - Ogre::RTShader::UniformParameterPtr mSurfaceShininess; // Surface shininess parameter. - - //Segmented texture - bool mUseSegmentedLightTexture; - bool mIsDebugMode; - unsigned short mLightSamplerIndex; - Ogre::RTShader::UniformParameterPtr mPSLightTextureIndexLimit; - Ogre::RTShader::UniformParameterPtr mPSLightTextureLightBounds; - Ogre::RTShader::UniformParameterPtr mPSSegmentedLightTexture; - //Ogre::RTShader::UniformParameterPtr mPSLightAreaBounds; - - static Ogre::Light msBlankLight; // Shared blank light. - -}; - - -/** -A factory that enables creation of PerPixelLighting instances. -@remarks Sub class of SubRenderStateFactory -*/ -class RTShaderSRSSegmentedLightsFactory : public Ogre::RTShader::SubRenderStateFactory -{ -public: - - /** - @see SubRenderStateFactory::getType. - */ - const Ogre::String& getType() const override; - -protected: - - /** - @see SubRenderStateFactory::createInstanceImpl. - */ - Ogre::RTShader::SubRenderState* createInstanceImpl() override; - - -}; - -#endif - diff --git a/Samples/ShaderSystemMultiLight/include/SegmentedDynamicLightManager.h b/Samples/ShaderSystemMultiLight/include/SegmentedDynamicLightManager.h deleted file mode 100644 index 08b47834bb0..00000000000 --- a/Samples/ShaderSystemMultiLight/include/SegmentedDynamicLightManager.h +++ /dev/null @@ -1,167 +0,0 @@ -/* ------------------------------------------------------------------------------ -This source file is part of OGRE -(Object-oriented Graphics Rendering Engine) -For the latest info, see http://www.ogre3d.org - -Copyright (c) 2000-2014 Torus Knot Software Ltd -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. ------------------------------------------------------------------------------ -*/ -#ifndef _SegmentedLightManager_ -#define _SegmentedLightManager_ - -#include "OgreShaderPrerequisites.h" -#include "OgreSingleton.h" -#include "OgreLight.h" -#include "OgreTexture.h" -#include "OgreLight.h" -#include "OgreSceneManager.h" - -#define SDL_SEGMENT_DIVISIONS 9 -#define SDL_SEGMENT_GRID_SIZE (SDL_SEGMENT_DIVISIONS * SDL_SEGMENT_DIVISIONS) -#define SDL_LIGHT_DATA_SIZE 3 -#define SDL_TEXTURE_ROWS 32 -#define SDL_TEXTURE_DATA_ROWS 1 -#define SDL_LIGHT_PER_BLOCK (SDL_TEXTURE_ROWS - SDL_TEXTURE_DATA_ROWS) - - -using namespace Ogre; - -class SegmentedDynamicLightManager : public Singleton, - public SceneManager::Listener -{ - -public: - SegmentedDynamicLightManager(); - ~SegmentedDynamicLightManager(); - - bool setDebugMode(bool i_IsDebugMode); - //Set the system to active mode - void setSceneManager(SceneManager* i_Manager); - //Tells if the system is active - bool isActive() const { return mManager != NULL; } - //Get the name of the texture used to store the light information - const String& getSDLTextureName(); - - //Get the range of lights in the supplied texture data that need to be calculated for a given renderable - bool getLightListRange(const Renderable* i_Rend, Vector4& o_GridBounds, unsigned int& o_IndexMin, unsigned int& o_IndexMax); - - //Get the width of the texture containing the light information - unsigned int getTextureWidth() const { return mTextureWidth; } - //Get the height of the texture containing the light information - unsigned int getTextureHeight() const { return mTextureHeight; } - //Get the amount of cells the texture is divided into on either axis - unsigned int getGridDivision() const { return SDL_SEGMENT_DIVISIONS; } - //Get whether to display the lights in debug mode - bool isDebugMode() const { return mIsDebugMode; } - - void postFindVisibleObjects(SceneManager* source, - SceneManager::IlluminationRenderStage irs, Viewport* v) override; - - /// @copydoc Singleton::getSingleton() - static SegmentedDynamicLightManager& getSingleton(void); - /// @copydoc Singleton::getSingleton() - static SegmentedDynamicLightManager* getSingletonPtr(void); -private: - - class LightData - { - public: - //Constructor for LightData - LightData(); - //Sets the values of the boundaries of the light - void setBounds(const AxisAlignedBox& i_Bounds); - //Add an index to the possible range of indexes - void addIndexToRange(unsigned int i_LightIndex); - - unsigned int getIndexMin() const { return mIndexMin; } - unsigned int getIndexMax() const { return mIndexMax; } - - Real getMinX() const { return mMinX; } - Real getMaxX() const { return mMaxX; } - Real getMinZ() const { return mMinZ; } - Real getMaxZ() const { return mMaxZ; } - - private: - unsigned int mIndexMin; - unsigned int mIndexMax; - - Real mMinX; - Real mMaxX; - Real mMinZ; - Real mMaxZ; - }; - - typedef std::map MapLightData; - -private: - //Update the systems internal light lists - void updateLightList(const Camera* i_pCamera, const LightList& i_LightList); - //Initialize the texture to be used to store the light information - bool initTexture(); - //Arrange the lights in the different lists - void arrangeLightsInSegmentedLists(const Camera* i_pCamera, const LightList& i_LightList); - //Repopulate the m_ActiveLights list which keeps track of all lights being rendered in the frame - void regenerateActiveLightList(const LightList& i_LightList); - //Calculate the bounds of a single light - void calculateLightBounds(const Light* i_Light, LightData &o_LightData); - //Calculate the area which bounds area in which the lights exist - void recalculateGridSize(); - //Distribute the lights in the active light list (mActiveLights) in the grid parameter (mSegmentedLightGrid) - void distributeLightsInGrid(); - //Get the index in the grid of a given world position - unsigned int calcGridColumn(Real i_Position, Real i_BoundStart, Real i_BoundEnd); - //Returns a grid index for a given x and y index positions - unsigned int calcGridIndex(unsigned int i_X, unsigned int i_Y); - - //Load the lights information from the internal lists to the texture - void updateTextureFromSegmentedLists(const Camera* i_pCamera); - -private: - //Tells whether to run the lights in debug mode - bool mIsDebugMode; - //Pointer to a scene manager on which the lights will work - SceneManager* mManager; - - //List of active lights in the frame - MapLightData mActiveLights; - - //A Grid structures to contain the lights as they are represented in the light texture - typedef std::vector VecLights; - typedef std::vector SegmentedVecLight; - SegmentedVecLight mSegmentedLightGrid; - - //A pointer to a texture which containing information from which a shader renders the lights - TexturePtr mLightTexture; - //The height of the width information texture - unsigned int mTextureWidth; - //The height of the light information texture - unsigned int mTextureHeight; - - - //Light grid bounds - Real mGridMinX; - Real mGridMinZ; - Real mGridMaxX; - Real mGridMaxZ; -}; - -#endif - diff --git a/Samples/ShaderSystemMultiLight/src/RTShaderSRSSegmentedLights.cpp b/Samples/ShaderSystemMultiLight/src/RTShaderSRSSegmentedLights.cpp deleted file mode 100644 index 6191bb65295..00000000000 --- a/Samples/ShaderSystemMultiLight/src/RTShaderSRSSegmentedLights.cpp +++ /dev/null @@ -1,786 +0,0 @@ -#include "RTShaderSRSSegmentedLights.h" -#include "OgreShaderFFPRenderState.h" -#include "OgreShaderProgram.h" -#include "OgreShaderParameter.h" -#include "OgreShaderProgramSet.h" -#include "OgreGpuProgram.h" -#include "OgrePass.h" -#include "OgreShaderGenerator.h" -#include "OgreSceneManager.h" -#include "OgreViewport.h" -#include "OgreMaterialSerializer.h" -#include "SegmentedDynamicLightManager.h" - -#define SL_LIB_PERPIXELLIGHTING "SegmentedPerPixelLighting" -#define SL_FUNC_TRANSFORMNORMAL "SL_TransformNormal" -#define SL_FUNC_TRANSFORMPOSITION "SL_TransformPosition" -#define SL_FUNC_LIGHT_DIRECTIONAL_DIFFUSE "SL_Light_Directional_Diffuse" -#define SL_FUNC_LIGHT_DIRECTIONAL_DIFFUSESPECULAR "SL_Light_Directional_DiffuseSpecular" -#define SL_FUNC_LIGHT_AMBIENT_DIFFUSE "SL_Light_Ambient_Diffuse" -#define SL_FUNC_LIGHT_SEGMENT_TEXTURE_AMBIENT_DIFFUSE "SL_Light_Segment_Texture_Ambient_Diffuse" -#define SL_FUNC_LIGHT_SEGMENT_DEBUG "SL_Light_Segment_Debug" - -using namespace Ogre; -using namespace Ogre::RTShader; - -String RTShaderSRSSegmentedLights::Type = "Segmented_PerPixelLighting"; -Light RTShaderSRSSegmentedLights::msBlankLight; - - -//----------------------------------------------------------------------- -RTShaderSRSSegmentedLights::RTShaderSRSSegmentedLights() -{ - mTrackVertexColourType = TVC_NONE; - mSpecularEnable = false; - mUseSegmentedLightTexture = false; - mLightSamplerIndex = 0; - - msBlankLight.setDiffuseColour(ColourValue::Black); - msBlankLight.setSpecularColour(ColourValue::Black); - msBlankLight.setAttenuation(0,1,0,0); -} - -//----------------------------------------------------------------------- -const String& RTShaderSRSSegmentedLights::getType() const -{ - return Type; -} - - -//----------------------------------------------------------------------- -int RTShaderSRSSegmentedLights::getExecutionOrder() const -{ - return FFP_LIGHTING; -} - -//----------------------------------------------------------------------- -void RTShaderSRSSegmentedLights::updateGpuProgramsParams(Renderable* rend, const Pass* pass, const AutoParamDataSource* source, - const LightList* pLightList) -{ - if ((mLightParamsList.empty()) && (!mUseSegmentedLightTexture)) - return; - - Light::LightTypes curLightType = Light::LT_DIRECTIONAL; - unsigned int curSearchLightIndex = 0; - - //update spot strength - float spotIntensity = 1; - - // Update per light parameters. - for (auto & curParams : mLightParamsList) - { - if (curLightType != curParams.mType) - { - curLightType = curParams.mType; - curSearchLightIndex = 0; - } - - Light* srcLight = NULL; - Vector4 vParameter; - ColourValue colour; - - // Search a matching light from the current sorted lights of the given renderable. - for (unsigned int j = curSearchLightIndex; j < pLightList->size(); ++j) - { - if (pLightList->at(j)->getType() == curLightType) - { - srcLight = pLightList->at(j); - curSearchLightIndex = j + 1; - break; - } - } - - // No matching light found -> use a blank dummy light for parameter update. - if (srcLight == NULL) - { - srcLight = &msBlankLight; - } - - - switch (curParams.mType) - { - case Light::LT_DIRECTIONAL: - - // Update light direction. - vParameter = srcLight->getAs4DVector(true); - curParams.mDirection->setGpuParameter(vParameter.ptr(),3,1); - break; - - case Light::LT_POINT: - - // Update light position. - vParameter = srcLight->getAs4DVector(true); - curParams.mPosition->setGpuParameter(vParameter.ptr(),3,1); - - // Update light attenuation parameters. - curParams.mSpotParams->setGpuParameter(Ogre::Vector3(1 / srcLight->getAttenuationRange(),0,0)); - break; - case Light::LT_RECTLIGHT: - case Light::LT_SPOTLIGHT: - { - Ogre::Vector3 vec3; - - // Update light position. - vParameter = srcLight->getAs4DVector(true); - curParams.mPosition->setGpuParameter(vParameter.ptr(),3,1); - - - // Update light direction. - vec3 = source->getInverseTransposeWorldMatrix().linear() * srcLight->getDerivedDirection(); - vec3.normalise(); - - vParameter.x = -vec3.x; - vParameter.y = -vec3.y; - vParameter.z = -vec3.z; - vParameter.w = 0.0; - curParams.mDirection->setGpuParameter(vParameter.ptr(),3,1); - - // Update spotlight parameters. - Real phi = Math::Cos(srcLight->getSpotlightOuterAngle().valueRadians() * 0.5f); - Real theta = Math::Cos(srcLight->getSpotlightInnerAngle().valueRadians() * 0.5f); - - vec3.x = 1 / srcLight->getAttenuationRange(); - vec3.y = phi; - vec3.z = 1 / (theta - phi); - - curParams.mSpotParams->setGpuParameter(vec3); - } - break; - } - - float lightIntensity = 1; - if (curParams.mType == Light::LT_SPOTLIGHT) - { - lightIntensity = spotIntensity; - } - - // Update diffuse colour. - colour = srcLight->getDiffuseColour() * lightIntensity; - if ((mTrackVertexColourType & TVC_DIFFUSE) == 0) - { - colour = colour * pass->getDiffuse(); - } - curParams.mDiffuseColour->setGpuParameter(colour.ptr(),3,1); - - // Update specular colour if need to. - if ((mSpecularEnable) && (curParams.mType == Light::LT_DIRECTIONAL)) - { - // Update diffuse colour. - colour = srcLight->getSpecularColour() * lightIntensity; - if ((mTrackVertexColourType & TVC_SPECULAR) == 0) - { - colour = colour * pass->getSpecular(); - } - curParams.mSpecularColour->setGpuParameter(colour.ptr(),3,1); - } - } - - if (mUseSegmentedLightTexture) - { - unsigned int indexStart = 0, indexEnd = 0; - Ogre::Vector4 lightBounds; - SegmentedDynamicLightManager::getSingleton().getLightListRange(rend, lightBounds, indexStart, indexEnd); - mPSLightTextureIndexLimit->setGpuParameter(Ogre::Vector2((Ogre::Real)indexStart, (Ogre::Real)indexEnd)); - mPSLightTextureLightBounds->setGpuParameter(lightBounds); - - Ogre::TextureUnitState* pLightTexture = pass->getTextureUnitState(mLightSamplerIndex); - const Ogre::String& textureName = SegmentedDynamicLightManager::getSingleton().getSDLTextureName(); - if (textureName != pLightTexture->getTextureName()) - { - pLightTexture->setTextureName(textureName, Ogre::TEX_TYPE_2D); - } - } -} - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::resolveParameters(ProgramSet* programSet) -{ - if (false == resolveGlobalParameters(programSet)) - return false; - - if (false == resolvePerLightParameters(programSet)) - return false; - - return true; -} - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::resolveGlobalParameters(ProgramSet* programSet) -{ - Program* vsProgram = programSet->getCpuProgram(GPT_VERTEX_PROGRAM); - Program* psProgram = programSet->getCpuProgram(GPT_FRAGMENT_PROGRAM); - Function* vsMain = vsProgram->getEntryPointFunction(); - Function* psMain = psProgram->getEntryPointFunction(); - - - // Resolve world IT matrix. - mWorldITMatrix = vsProgram->resolveParameter(GpuProgramParameters::ACT_INVERSE_TRANSPOSE_WORLD_MATRIX); - - // Get surface ambient colour if need to. - if ((mTrackVertexColourType & TVC_AMBIENT) == 0) - { - mDerivedAmbientLightColour = psProgram->resolveParameter(GpuProgramParameters::ACT_DERIVED_AMBIENT_LIGHT_COLOUR); - } - else - { - mLightAmbientColour = psProgram->resolveParameter(GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR); - mSurfaceAmbientColour = psProgram->resolveParameter(GpuProgramParameters::ACT_SURFACE_AMBIENT_COLOUR); - } - - // Get surface diffuse colour if need to. - if ((mTrackVertexColourType & TVC_DIFFUSE) == 0) - { - mSurfaceDiffuseColour = psProgram->resolveParameter(GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); - } - - // Get surface specular colour if need to. - if ((mTrackVertexColourType & TVC_SPECULAR) == 0) - { - mSurfaceSpecularColour = psProgram->resolveParameter(GpuProgramParameters::ACT_SURFACE_SPECULAR_COLOUR); - } - - // Get surface emissive colour if need to. - if ((mTrackVertexColourType & TVC_EMISSIVE) == 0) - { - mSurfaceEmissiveColour = psProgram->resolveParameter(GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); - } - - // Get derived scene colour. - mDerivedSceneColour = psProgram->resolveParameter(GpuProgramParameters::ACT_DERIVED_SCENE_COLOUR); - // Get surface shininess. - mSurfaceShininess = psProgram->resolveParameter(GpuProgramParameters::ACT_SURFACE_SHININESS); - - - //Check if another SRS already defined a normal in world space to be used - mPSLocalNormal = psMain->getLocalParameter(Parameter::SPC_NORMAL_WORLD_SPACE); - if (mPSLocalNormal.get() == NULL) - { - //create parameters to fetch the normal from the vertex shader - - // Resolve input vertex shader normal. - mVSInNormal = vsMain->resolveInputParameter(Parameter::SPC_NORMAL_OBJECT_SPACE); - // Resolve output vertex shader normal. - mVSOutNormal = vsMain->resolveOutputParameter(Parameter::SPC_NORMAL_WORLD_SPACE); - // Resolve input pixel shader normal. - mPSInNormal = psMain->resolveInputParameter(mVSOutNormal); - mPSLocalNormal = psMain->resolveLocalParameter(Parameter::SPC_NORMAL_WORLD_SPACE); - } - - mPSDiffuse = psMain->getInputParameter(Parameter::SPC_COLOR_DIFFUSE); - if (mPSDiffuse.get() == NULL) - { - mPSDiffuse = psMain->getLocalParameter(Parameter::SPC_COLOR_DIFFUSE); - if (mPSDiffuse.get() == NULL) - return false; - } - - mPSOutDiffuse = psMain->resolveOutputParameter(Parameter::SPS_COLOR, 0, Parameter::SPC_COLOR_DIFFUSE, GCT_FLOAT4); - mPSTempDiffuseColour = psMain->resolveLocalParameter(GCT_FLOAT4, "lPerPixelDiffuse"); - mVSOutWorldPos = vsMain->resolveOutputParameter(Parameter::SPC_POSITION_WORLD_SPACE); - mPSInWorldPos = psMain->resolveInputParameter(mVSOutWorldPos); - mWorldMatrix = vsProgram->resolveParameter(GpuProgramParameters::ACT_WORLD_MATRIX); - mVSInPosition = vsMain->resolveInputParameter(Parameter::SPC_POSITION_OBJECT_SPACE); - - - if (mSpecularEnable) - { - mPSSpecular = psMain->getInputParameter(Parameter::SPC_COLOR_SPECULAR); - if (mPSSpecular.get() == NULL) - { - mPSSpecular = psMain->getLocalParameter(Parameter::SPC_COLOR_SPECULAR); - if (mPSSpecular.get() == NULL) - return false; - } - - mPSTempSpecularColour = psMain->resolveLocalParameter(GCT_FLOAT4, "lPerPixelSpecular"); - mVSInPosition = vsMain->resolveInputParameter(Parameter::SPC_POSITION_OBJECT_SPACE); - mWorldMatrix = vsProgram->resolveParameter(GpuProgramParameters::ACT_WORLD_MATRIX); - } - - - if (mUseSegmentedLightTexture) - { - mPSLightTextureIndexLimit = psProgram->resolveParameter(GCT_FLOAT2, -1, (uint16)GPV_PER_OBJECT, "LightTextureIndexLimits"); - mPSLightTextureLightBounds = psProgram->resolveParameter(GCT_FLOAT4, -1, (uint16)GPV_PER_OBJECT, "LightTextureBounds"); - mPSSegmentedLightTexture = psProgram->resolveParameter(Ogre::GCT_SAMPLER2D, mLightSamplerIndex, (Ogre::uint16)Ogre::GPV_GLOBAL, "segmentedLightTexture"); - } - - return true; -} - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::resolvePerLightParameters(ProgramSet* programSet) -{ - Program* psProgram = programSet->getCpuProgram(GPT_FRAGMENT_PROGRAM); - - // Resolve per light parameters. - for (auto & i : mLightParamsList) - { - switch (i.mType) - { - case Light::LT_RECTLIGHT: - case Light::LT_DIRECTIONAL: - i.mDirection = psProgram->resolveParameter(GCT_FLOAT3, -1, (uint16)GPV_LIGHTS, "light_direction_space"); - break; - - case Light::LT_POINT: - case Light::LT_SPOTLIGHT: - i.mPosition = psProgram->resolveParameter(GCT_FLOAT3, -1, (uint16)GPV_LIGHTS, "light_position_space"); - i.mDirection = psProgram->resolveParameter(GCT_FLOAT3, -1, (uint16)GPV_LIGHTS, "light_direction_space"); - i.mSpotParams = psProgram->resolveParameter(GCT_FLOAT3, -1, (uint16)GPV_LIGHTS, "spotlight_params"); - break; - } - - // Resolve diffuse colour. - if ((mTrackVertexColourType & TVC_DIFFUSE) == 0) - { - i.mDiffuseColour = psProgram->resolveParameter(GCT_FLOAT3, -1, (uint16)GPV_LIGHTS | (uint16)GPV_GLOBAL, "derived_light_diffuse"); - } - else - { - i.mDiffuseColour = psProgram->resolveParameter(GCT_FLOAT3, -1, (uint16)GPV_LIGHTS, "light_diffuse"); - } - - if ((mSpecularEnable) && (i.mType == Light::LT_DIRECTIONAL)) - { - // Resolve specular colour. - if ((mTrackVertexColourType & TVC_SPECULAR) == 0) - { - i.mSpecularColour = psProgram->resolveParameter(GCT_FLOAT3, -1, (uint16)GPV_LIGHTS | (uint16)GPV_GLOBAL, "derived_light_specular"); - } - else - { - i.mSpecularColour = psProgram->resolveParameter(GCT_FLOAT3, -1, (uint16)GPV_LIGHTS, "light_specular"); - } - } - - } - - return true; -} - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::resolveDependencies(ProgramSet* programSet) -{ - Program* vsProgram = programSet->getCpuProgram(GPT_VERTEX_PROGRAM); - Program* psProgram = programSet->getCpuProgram(GPT_FRAGMENT_PROGRAM); - - vsProgram->addDependency(SL_LIB_PERPIXELLIGHTING); - - psProgram->addDependency(SL_LIB_PERPIXELLIGHTING); - - return true; -} - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::addFunctionInvocations(ProgramSet* programSet) -{ - Program* vsProgram = programSet->getCpuProgram(GPT_VERTEX_PROGRAM); - Function* vsMain = vsProgram->getEntryPointFunction(); - Program* psProgram = programSet->getCpuProgram(GPT_FRAGMENT_PROGRAM); - Function* psMain = psProgram->getEntryPointFunction(); - - // Add the global illumination functions. - if (false == addVSInvocation(vsMain, FFP_VS_LIGHTING)) - return false; - - // Add the global illumination functions. - if (false == addPSGlobalIlluminationInvocationBegin(psMain, FFP_PS_COLOUR_BEGIN + 1)) - return false; - - - // Add per light functions. - for (auto & i : mLightParamsList) - { - if (false == addPSIlluminationInvocation(&i, psMain, FFP_PS_COLOUR_BEGIN + 1)) - return false; - } - - if (mUseSegmentedLightTexture) - { - addPSSegmentedTextureLightInvocation(psMain, FFP_PS_COLOUR_BEGIN + 1); - } - - - // Add the global illumination functions. - if (false == addPSGlobalIlluminationInvocationEnd(psMain, FFP_PS_COLOUR_BEGIN + 1)) - return false; - - - // Assign back temporary variables to the ps diffuse and specular components. - if (false == addPSFinalAssignmentInvocation(psMain, FFP_PS_COLOUR_BEGIN + 1)) - return false; - - - return true; -} - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::addVSInvocation(Function* vsMain, const int groupOrder) -{ - FunctionInvocation* curFuncInvocation = NULL; - - if (mVSInNormal.get() != NULL) - { - // Transform normal in world space. - curFuncInvocation = OGRE_NEW FunctionInvocation(SL_FUNC_TRANSFORMNORMAL, groupOrder); - curFuncInvocation->pushOperand(mWorldITMatrix, Operand::OPS_IN); - curFuncInvocation->pushOperand(mVSInNormal, Operand::OPS_IN); - curFuncInvocation->pushOperand(mVSOutNormal, Operand::OPS_OUT); - vsMain->addAtomInstance(curFuncInvocation); - } - - // Transform world space position if need to. - if (mVSOutWorldPos.get() != NULL) - { - curFuncInvocation = OGRE_NEW FunctionInvocation(SL_FUNC_TRANSFORMPOSITION, groupOrder); - curFuncInvocation->pushOperand(mWorldMatrix, Operand::OPS_IN); - curFuncInvocation->pushOperand(mVSInPosition, Operand::OPS_IN); - curFuncInvocation->pushOperand(mVSOutWorldPos, Operand::OPS_OUT); - vsMain->addAtomInstance(curFuncInvocation); - } - - - return true; -} - - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::addPSGlobalIlluminationInvocationBegin(Function* psMain, const int groupOrder) -{ - FunctionAtom* curFuncInvocation = NULL; - - if (mPSInNormal.get()) - { - curFuncInvocation = OGRE_NEW AssignmentAtom(FFP_PS_PRE_PROCESS + 1); - curFuncInvocation->pushOperand(mPSInNormal, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSLocalNormal, Operand::OPS_OUT); - psMain->addAtomInstance(curFuncInvocation); - } - - //alpha channel is controlled by the diffuse value - if (mTrackVertexColourType & TVC_DIFFUSE) - { - curFuncInvocation = OGRE_NEW AssignmentAtom(groupOrder); - curFuncInvocation->pushOperand(mPSDiffuse, Operand::OPS_IN, Operand::OPM_W); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_W); - psMain->addAtomInstance(curFuncInvocation); - } - else - { - curFuncInvocation = OGRE_NEW AssignmentAtom(groupOrder); - curFuncInvocation->pushOperand(mDerivedSceneColour, Operand::OPS_IN, Operand::OPM_W); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_W); - psMain->addAtomInstance(curFuncInvocation); - } - - ParameterPtr pZeroParam = ParameterFactory::createConstParam(Ogre::Vector3::ZERO); - - curFuncInvocation = OGRE_NEW AssignmentAtom(groupOrder); - curFuncInvocation->pushOperand(pZeroParam, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - - if (mSpecularEnable) - { - curFuncInvocation = OGRE_NEW AssignmentAtom(groupOrder); - curFuncInvocation->pushOperand(pZeroParam, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSTempSpecularColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - - return true; -} - - - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::addPSGlobalIlluminationInvocationEnd(Function* psMain, const int groupOrder) -{ - FunctionAtom* curFuncInvocation = NULL; - - // Merge diffuse colour with vertex colour if need to. - if (mTrackVertexColourType & TVC_DIFFUSE) - { - curFuncInvocation = OGRE_NEW BinaryOpAtom('*', groupOrder); - curFuncInvocation->pushOperand(mPSDiffuse, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - - // Merge specular colour with vertex colour if need to. - if ((mSpecularEnable == true) && (mTrackVertexColourType & TVC_SPECULAR)) - { - curFuncInvocation = OGRE_NEW BinaryOpAtom('*', groupOrder); - curFuncInvocation->pushOperand(mPSDiffuse, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempSpecularColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempSpecularColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - - - if ((mTrackVertexColourType & TVC_AMBIENT) == 0 && - (mTrackVertexColourType & TVC_EMISSIVE) == 0) - { - curFuncInvocation = OGRE_NEW BinaryOpAtom('+', groupOrder); - curFuncInvocation->pushOperand(mDerivedSceneColour, Operand::OPS_IN, (Operand::OPM_XYZ)); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_IN, (Operand::OPM_XYZ)); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - else - { - if (mTrackVertexColourType & TVC_AMBIENT) - { - curFuncInvocation = OGRE_NEW BinaryOpAtom('*', groupOrder); - curFuncInvocation->pushOperand(mPSDiffuse, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mLightAmbientColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mLightAmbientColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - - curFuncInvocation = OGRE_NEW BinaryOpAtom('+', groupOrder); - curFuncInvocation->pushOperand(mLightAmbientColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - else - { - curFuncInvocation = OGRE_NEW BinaryOpAtom('+', groupOrder); - curFuncInvocation->pushOperand(mDerivedAmbientLightColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - - if (mTrackVertexColourType & TVC_EMISSIVE) - { - curFuncInvocation = OGRE_NEW BinaryOpAtom('+', groupOrder); - curFuncInvocation->pushOperand(mPSDiffuse, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - else - { - curFuncInvocation = OGRE_NEW BinaryOpAtom('+', groupOrder); - curFuncInvocation->pushOperand(mSurfaceEmissiveColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - } - - if (mSpecularEnable) - { - curFuncInvocation = OGRE_NEW BinaryOpAtom('+', groupOrder); - curFuncInvocation->pushOperand(mPSSpecular, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSTempSpecularColour, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSTempSpecularColour, Operand::OPS_OUT); - psMain->addAtomInstance(curFuncInvocation); - } - - return true; -} - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::addPSIlluminationInvocation(LightParams* curLightParams, Function* psMain, const int groupOrder) -{ - FunctionInvocation* curFuncInvocation = NULL; - - - switch (curLightParams->mType) - { - case Light::LT_RECTLIGHT: - case Light::LT_DIRECTIONAL: - if (mSpecularEnable) - { - curFuncInvocation = OGRE_NEW FunctionInvocation(SL_FUNC_LIGHT_DIRECTIONAL_DIFFUSESPECULAR, groupOrder); - curFuncInvocation->pushOperand(mPSLocalNormal, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSInWorldPos, Operand::OPS_IN); - curFuncInvocation->pushOperand(curLightParams->mDirection, Operand::OPS_IN); - curFuncInvocation->pushOperand(curLightParams->mDiffuseColour, Operand::OPS_IN); - curFuncInvocation->pushOperand(curLightParams->mSpecularColour, Operand::OPS_IN); - curFuncInvocation->pushOperand(mSurfaceShininess, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempSpecularColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempSpecularColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - - else - { - curFuncInvocation = OGRE_NEW FunctionInvocation(SL_FUNC_LIGHT_DIRECTIONAL_DIFFUSE, groupOrder); - curFuncInvocation->pushOperand(mPSLocalNormal, Operand::OPS_IN); - curFuncInvocation->pushOperand(curLightParams->mDirection, Operand::OPS_IN); - curFuncInvocation->pushOperand(curLightParams->mDiffuseColour, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_IN, Operand::OPM_XYZ); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_OUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - break; - - case Light::LT_POINT: - case Light::LT_SPOTLIGHT: - { - curFuncInvocation = OGRE_NEW FunctionInvocation(SL_FUNC_LIGHT_AMBIENT_DIFFUSE, groupOrder); - curFuncInvocation->pushOperand(mPSLocalNormal, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSInWorldPos, Operand::OPS_IN); - curFuncInvocation->pushOperand(curLightParams->mPosition, Operand::OPS_IN); - curFuncInvocation->pushOperand(curLightParams->mDirection, Operand::OPS_IN); - curFuncInvocation->pushOperand(curLightParams->mSpotParams, Operand::OPS_IN); - curFuncInvocation->pushOperand(curLightParams->mDiffuseColour, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_INOUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - } - break; - } - - return true; -} - -bool RTShaderSRSSegmentedLights::addPSSegmentedTextureLightInvocation(Function* psMain, const int groupOrder) -{ - float invWidth = 1.0f / (float)SegmentedDynamicLightManager::getSingleton().getTextureWidth(); - float invHeight = 1.0f / (float)SegmentedDynamicLightManager::getSingleton().getTextureHeight(); - ParameterPtr paramInvWidth = ParameterFactory::createConstParam(invWidth); - ParameterPtr paramInvHeight = ParameterFactory::createConstParam(invHeight); - - FunctionInvocation* curFuncInvocation = NULL; - curFuncInvocation = OGRE_NEW FunctionInvocation(SL_FUNC_LIGHT_SEGMENT_TEXTURE_AMBIENT_DIFFUSE, groupOrder); - curFuncInvocation->pushOperand(mPSLocalNormal, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSInWorldPos, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSSegmentedLightTexture, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSLightTextureIndexLimit, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSLightTextureLightBounds, Operand::OPS_IN); - curFuncInvocation->pushOperand(paramInvWidth, Operand::OPS_IN); - curFuncInvocation->pushOperand(paramInvHeight, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_INOUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curFuncInvocation); - - if (SegmentedDynamicLightManager::getSingleton().isDebugMode()) - { - ParameterPtr psOutColor = psMain->resolveOutputParameter(Parameter::SPS_COLOR, -1, Parameter::SPC_COLOR_DIFFUSE, GCT_FLOAT4); - - FunctionInvocation* curDebugFuncInvocation = NULL; - curDebugFuncInvocation = OGRE_NEW FunctionInvocation(SL_FUNC_LIGHT_SEGMENT_DEBUG, FFP_PS_COLOUR_END + 1); - curDebugFuncInvocation->pushOperand(mPSLocalNormal, Operand::OPS_IN); - curDebugFuncInvocation->pushOperand(mPSInWorldPos, Operand::OPS_IN); - curDebugFuncInvocation->pushOperand(mPSSegmentedLightTexture, Operand::OPS_IN); - curDebugFuncInvocation->pushOperand(mPSLightTextureIndexLimit, Operand::OPS_IN); - curDebugFuncInvocation->pushOperand(mPSLightTextureLightBounds, Operand::OPS_IN); - curDebugFuncInvocation->pushOperand(paramInvWidth, Operand::OPS_IN); - curDebugFuncInvocation->pushOperand(paramInvHeight, Operand::OPS_IN); - - curDebugFuncInvocation->pushOperand(psOutColor, Operand::OPS_INOUT, Operand::OPM_XYZ); - psMain->addAtomInstance(curDebugFuncInvocation); - } - - return true; -} - - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::addPSFinalAssignmentInvocation( Function* psMain, const int groupOrder) -{ - FunctionAtom* curFuncInvocation; - - curFuncInvocation = OGRE_NEW AssignmentAtom(FFP_PS_COLOUR_BEGIN + 1); - curFuncInvocation->pushOperand(mPSTempDiffuseColour, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSDiffuse, Operand::OPS_OUT); - psMain->addAtomInstance(curFuncInvocation); - - curFuncInvocation = OGRE_NEW AssignmentAtom(FFP_PS_COLOUR_BEGIN + 1); - curFuncInvocation->pushOperand(mPSDiffuse, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSOutDiffuse, Operand::OPS_OUT); - psMain->addAtomInstance(curFuncInvocation); - - if (mSpecularEnable) - { - curFuncInvocation = OGRE_NEW AssignmentAtom(FFP_PS_COLOUR_BEGIN + 1); - curFuncInvocation->pushOperand(mPSTempSpecularColour, Operand::OPS_IN); - curFuncInvocation->pushOperand(mPSSpecular, Operand::OPS_OUT); - psMain->addAtomInstance(curFuncInvocation); - } - - return true; -} - - -//----------------------------------------------------------------------- -void RTShaderSRSSegmentedLights::copyFrom(const SubRenderState& rhs) -{ - const RTShaderSRSSegmentedLights& rhsLighting = static_cast(rhs); - - mUseSegmentedLightTexture = rhsLighting.mUseSegmentedLightTexture; - mLightParamsList = rhsLighting.mLightParamsList; -} - -//----------------------------------------------------------------------- -bool RTShaderSRSSegmentedLights::preAddToRenderState(const RenderState* renderState, Pass* srcPass, Pass* dstPass) -{ - if (srcPass->getLightingEnabled() == false) - return false; - - mUseSegmentedLightTexture = SegmentedDynamicLightManager::getSingleton().isActive(); - setTrackVertexColourType(srcPass->getVertexColourTracking()); - - if (srcPass->getShininess() > 0.0 && - srcPass->getSpecular() != ColourValue::Black) - { - setSpecularEnable(true); - } - else - { - setSpecularEnable(false); - } - - setLightCount(renderState->getLightCount()); - - if (mUseSegmentedLightTexture) - { - const_cast(renderState)->setLightCountAutoUpdate(false); - - Ogre::TextureUnitState* pLightTexture = dstPass->createTextureUnitState(); - pLightTexture->setTextureName(SegmentedDynamicLightManager::getSingleton().getSDLTextureName(), Ogre::TEX_TYPE_2D); - pLightTexture->setTextureFiltering(Ogre::TFO_NONE); - mLightSamplerIndex = dstPass->getNumTextureUnitStates() - 1; - } - - - return true; -} - -//----------------------------------------------------------------------- -void RTShaderSRSSegmentedLights::setLightCount(int lightCount) -{ - mLightParamsList.clear(); - //Set always to have one single directional lights - LightParams curParams; - curParams.mType = Light::LT_DIRECTIONAL; - mLightParamsList.push_back(curParams); - - curParams.mType = Light::LT_POINT; - for (int i=0; i < lightCount; ++i) - { - if ((!mUseSegmentedLightTexture) || (curParams.mType == Light::LT_DIRECTIONAL)) - { - mLightParamsList.push_back(curParams); - } - } -} - -//----------------------------------------------------------------------- -const String& RTShaderSRSSegmentedLightsFactory::getType() const -{ - return RTShaderSRSSegmentedLights::Type; -} - -//----------------------------------------------------------------------- -SubRenderState* RTShaderSRSSegmentedLightsFactory::createInstanceImpl() -{ - return OGRE_NEW RTShaderSRSSegmentedLights; -} - diff --git a/Samples/ShaderSystemMultiLight/src/SegmentedDynamicLightManager.cpp b/Samples/ShaderSystemMultiLight/src/SegmentedDynamicLightManager.cpp deleted file mode 100644 index 3aeb81e15f6..00000000000 --- a/Samples/ShaderSystemMultiLight/src/SegmentedDynamicLightManager.cpp +++ /dev/null @@ -1,433 +0,0 @@ -#include "SegmentedDynamicLightManager.h" -#include "OgreTextureManager.h" -#include "OgreCamera.h" -#include "OgreSceneManager.h" -#include "OgreHardwarePixelBuffer.h" -#include "OgreRenderable.h" -#include "OgreBitwise.h" -#include "OgrePixelFormat.h" -#include "OgreRoot.h" -#include "OgreViewport.h" - -#define SDL_LIGHT_DATA_SIZE 3 // 12 floats divided by 4 slots (rgba) - -namespace Ogre -{ - template<> SegmentedDynamicLightManager* Singleton::msSingleton = 0; -} - -SegmentedDynamicLightManager* SegmentedDynamicLightManager::getSingletonPtr(void) -{ - return msSingleton; -} -SegmentedDynamicLightManager& SegmentedDynamicLightManager::getSingleton(void) -{ - assert( msSingleton ); return ( *msSingleton ); -} - -using namespace Ogre; - -const String c_SDLTextureName = "Simigon/SDLTexture"; - -SegmentedDynamicLightManager::SegmentedDynamicLightManager() : - mIsDebugMode(false), - mManager(NULL), - mSegmentedLightGrid(SDL_SEGMENT_GRID_SIZE), - mLightTexture(), - mTextureWidth(0), - mTextureHeight(SDL_TEXTURE_ROWS) -{ - //calculate needed texture width - mTextureWidth = SDL_LIGHT_DATA_SIZE * SDL_SEGMENT_GRID_SIZE; - //round up to the nearest power of 2 - unsigned int pow2Val = 1; - for( ; mTextureWidth > pow2Val; pow2Val = pow2Val << 1); - mTextureWidth = pow2Val; -} - -//------------------------------------------------------------------------------ -SegmentedDynamicLightManager::~SegmentedDynamicLightManager() -{ - setSceneManager(NULL); - if (mLightTexture.get()) - { - TextureManager::getSingleton().remove(mLightTexture->getHandle()); - } -} - -//------------------------------------------------------------------------------ -bool SegmentedDynamicLightManager::setDebugMode(bool i_IsDebugMode) -{ - bool requireInvalidate = false; - if (mIsDebugMode != i_IsDebugMode) - { - mIsDebugMode = i_IsDebugMode; - requireInvalidate = true; - } - return requireInvalidate; -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::postFindVisibleObjects(SceneManager* source, - SceneManager::IlluminationRenderStage irs, Viewport* v) -{ - if (irs == SceneManager::IRS_NONE) - { - updateLightList(v->getCamera(), source->_getLightsAffectingFrustum()); - } -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::setSceneManager(SceneManager* i_Manager) -{ - if (mManager != i_Manager) - { - if (mManager) mManager->removeListener(this); - mManager = i_Manager; - if (mManager) - { - mManager->addListener(this); - initTexture(); - } - } -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::updateLightList(const Camera* i_pCamera, const LightList& i_LightList) -{ - if (isActive()) - { - arrangeLightsInSegmentedLists(i_pCamera, i_LightList); - updateTextureFromSegmentedLists(i_pCamera); - } -} - -//------------------------------------------------------------------------------ -bool SegmentedDynamicLightManager::initTexture() -{ - if (mLightTexture.get() == NULL) - { - const String& sdlTextureName = getSDLTextureName(); - // create the render texture - mLightTexture = TextureManager::getSingleton().createManual(sdlTextureName, - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,TEX_TYPE_2D, - mTextureWidth,mTextureHeight,0,PF_FLOAT16_RGBA,TU_STATIC_WRITE_ONLY); - } - return mLightTexture.get() != NULL; -} - -//------------------------------------------------------------------------------ -const String& SegmentedDynamicLightManager::getSDLTextureName() -{ - return c_SDLTextureName; -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::arrangeLightsInSegmentedLists(const Camera* i_pCamera, const LightList& i_LightList) -{ - //Clear the previous buffers - for(int i = 0; i < SDL_SEGMENT_GRID_SIZE; ++i) - { - mSegmentedLightGrid[i].clear(); - } - mActiveLights.clear(); - - regenerateActiveLightList(i_LightList); - recalculateGridSize(); - distributeLightsInGrid(); -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::regenerateActiveLightList(const LightList& i_LightList) -{ - //add the buffers to the segmented lists - LightList::const_iterator itLight = i_LightList.begin(), - itLightEnd = i_LightList.end(); - for(;itLight != itLightEnd ; ++itLight) - { - const Light* pLight = (*itLight); - Light::LightTypes type = pLight->getType(); - if (((type == Light::LT_SPOTLIGHT) || (type == Light::LT_POINT)) && - (pLight->getAttenuationRange() > 0)) - { - - MapLightData::iterator it = mActiveLights.emplace(pLight,LightData()).first; - LightData& lightData = it->second; - - calculateLightBounds(pLight, lightData); - } - } -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::calculateLightBounds(const Light* i_Light, LightData& o_LightData) -{ - Real lightRange = i_Light->getAttenuationRange(); - const Vector3& lightPosition = i_Light->getDerivedPosition(true); - - AxisAlignedBox boundBox(lightPosition - lightRange, lightPosition + lightRange); - - if (i_Light->getType() == Light::LT_SPOTLIGHT) - { - static const Radian c_RadianPI(Math::PI); - static const Radian c_RadianZero(0); - - Radian halfOuterAngle = i_Light->getSpotlightOuterAngle() * 0.5; - Real boxOffset = Math::Sin(halfOuterAngle) * lightRange; - const Vector3& lightDirection = i_Light->getDerivedDirection(); - - Radian dirUpAngle(fabs(Math::ASin(lightDirection.y).valueRadians())); - Radian dirUpMaxAngle = std::max(dirUpAngle - halfOuterAngle,c_RadianZero); - Radian dirUpMinAngle = std::min(dirUpAngle + halfOuterAngle, c_RadianPI); - Real dirDistanceMax = Math::Cos(dirUpMaxAngle) * lightRange; - Real dirDistanceMin = Math::Cos(dirUpMinAngle) * lightRange; - - Vector3 flatDirection(lightDirection.x, 0, lightDirection.z); - Real flatDirLen = flatDirection.length(); - if (flatDirLen != 0) flatDirection /= flatDirLen; - else flatDirection = Vector3(1,0,0); - - Vector3 flatDirectionPerp(flatDirection.z, 0, -flatDirection.x); - flatDirectionPerp *= boxOffset; - - Vector3 flatPositionMax = lightPosition + dirDistanceMax * flatDirection; - Vector3 flatPositionMin = lightPosition + dirDistanceMin * flatDirection; - - AxisAlignedBox spotBox; - spotBox.merge(flatPositionMax + flatDirectionPerp); - spotBox.merge(flatPositionMax - flatDirectionPerp); - spotBox.merge(flatPositionMin + flatDirectionPerp); - spotBox.merge(flatPositionMin - flatDirectionPerp); - spotBox.merge(lightPosition); - - boundBox.getMaximum().makeFloor(spotBox.getMaximum()); - boundBox.getMinimum().makeCeil(spotBox.getMinimum()); - } - - o_LightData.setBounds(boundBox); -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::recalculateGridSize() -{ - mGridMinX = std::numeric_limits::max(); - mGridMinZ = std::numeric_limits::max(); - mGridMaxX = -std::numeric_limits::max(); - mGridMaxZ = -std::numeric_limits::max(); - - MapLightData::const_iterator it = mActiveLights.begin(), - itEnd = mActiveLights.end(); - for(;it != itEnd ; ++it) - { - const LightData& lightData = it->second; - mGridMinX = std::min(mGridMinX,lightData.getMinX()); - mGridMaxX = std::max(mGridMaxX,lightData.getMaxX()); - mGridMinZ = std::min(mGridMinZ,lightData.getMinZ()); - mGridMaxZ = std::max(mGridMaxZ,lightData.getMaxZ()); - } -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::distributeLightsInGrid() -{ - MapLightData::iterator it = mActiveLights.begin(), - itEnd = mActiveLights.end(); - for(;it != itEnd ; ++it) - { - LightData& lightData = it->second; - unsigned int indexXStart = calcGridColumn(lightData.getMinX(), mGridMinX, mGridMaxX); - unsigned int indexXEnd = calcGridColumn(lightData.getMaxX(), mGridMinX, mGridMaxX); - unsigned int indexZStart = calcGridColumn(lightData.getMinZ(), mGridMinZ, mGridMaxZ); - unsigned int indexZEnd = calcGridColumn(lightData.getMaxZ(), mGridMinZ, mGridMaxZ); - for(unsigned int i = indexXStart ; i <= indexXEnd ; ++i) - { - for(unsigned int j = indexZStart ; j <= indexZEnd ; ++j) - { - VecLights& block = mSegmentedLightGrid[calcGridIndex(i,j)]; - unsigned int lightIndex = (unsigned int)block.size(); - if (lightIndex < SDL_LIGHT_PER_BLOCK) - { - block.push_back(it->first); - lightData.addIndexToRange(lightIndex); - } - } - } - } -} - -//------------------------------------------------------------------------------ -unsigned int SegmentedDynamicLightManager::calcGridColumn(Real i_Position, - Real i_BoundStart, Real i_BoundEnd) -{ - int index = (unsigned int) - ((Math::inverseLerp(i_BoundStart, i_BoundEnd, i_Position)) * SDL_SEGMENT_DIVISIONS); - return (unsigned int)Math::Clamp(index, 0 ,SDL_SEGMENT_DIVISIONS - 1); -} - -unsigned int SegmentedDynamicLightManager::calcGridIndex(unsigned int i_X, unsigned int i_Y) -{ - return i_X + i_Y * SDL_SEGMENT_DIVISIONS; -} - - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::updateTextureFromSegmentedLists(const Camera* i_pCamera) -{ - float spotIntensity = 1; - - HardwarePixelBufferSharedPtr pBuf = mLightTexture->getBuffer(); - void* pStartPos = pBuf->lock(HardwareBuffer::HBL_DISCARD); - uint16* pData = (uint16*)pStartPos; - - size_t remainBufWidth = mTextureWidth; - for(size_t j = 0; j < SDL_SEGMENT_GRID_SIZE; ++j) - { - //assign first row with number of indexes in the block - float maxRow = (float)(mSegmentedLightGrid[j].size() - 1 + SDL_TEXTURE_DATA_ROWS); - PixelUtil::packColour(maxRow,0.0f,0.0f,0.0f,PF_FLOAT16_RGBA, pData); - pData += 4 * SDL_LIGHT_DATA_SIZE; - remainBufWidth -= SDL_LIGHT_DATA_SIZE; - } - - //advance the remaining space of the row - pData += 4 * remainBufWidth; - - for(size_t i = 0 ; i < SDL_LIGHT_PER_BLOCK ; ++i) - { - remainBufWidth = mTextureWidth; - for(size_t j = 0; j < SDL_SEGMENT_GRID_SIZE; ++j) - { - if (i < mSegmentedLightGrid[j].size()) - { - const Light* pLight = mSegmentedLightGrid[j][i]; - - const Vector3& position = pLight->getDerivedPosition(true); - Vector3 direction = -pLight->getDerivedDirection(); - direction.normalise(); - - // Update spotlight parameters. - Vector3 spotParam; - float inverseRange = 1.0f / (float)pLight->getAttenuationRange(); - float spotAngle = -1; - float spotInvAngleRange = std::numeric_limits::max(); - if (pLight->getType() == Light::LT_SPOTLIGHT) - { - Real phi = Math::Cos(pLight->getSpotlightOuterAngle().valueRadians() * 0.5f); - Real theta = Math::Cos(pLight->getSpotlightInnerAngle().valueRadians() * 0.5f); - spotAngle = (float)phi; - spotInvAngleRange = 1.0f / (float)(theta - phi); - } - - PixelUtil::packColour( - (float)position.x, - (float)position.y, - (float)position.z, - inverseRange, - PF_FLOAT16_RGBA, pData); - pData += 4; - - PixelUtil::packColour( - (float)direction.x, - (float)direction.y, - (float)direction.z, - spotAngle, - PF_FLOAT16_RGBA, pData); - pData += 4; - - PixelUtil::packColour( - pLight->getDiffuseColour().r * spotIntensity, - pLight->getDiffuseColour().g * spotIntensity, - pLight->getDiffuseColour().b * spotIntensity, - spotInvAngleRange, - PF_FLOAT16_RGBA, pData); - pData += 4; - - } - else - { - //assign position zero with zero width - PixelUtil::packColour(0.0f,0.0f,0.0f,std::numeric_limits::max(), - PF_FLOAT16_RGBA, pData); - pData += 4; - for(int d = 0 ; d < (SDL_LIGHT_DATA_SIZE - 1) ; ++d) - { - PixelUtil::packColour(0.0f,0.0f,0.0f,0.0f,PF_FLOAT16_RGBA, pData); - pData += 4; - } - } - remainBufWidth -= 3; - } - - //advance the remaining space of the row - pData += 4 * remainBufWidth; - } - - //Check for memory overrun - if (pBuf->getSizeInBytes() != (size_t)((const char*)(void*)pData - (const char*)pStartPos)) - { - throw "memory overrun"; - } - - pBuf->unlock(); -} - -//------------------------------------------------------------------------------ -bool SegmentedDynamicLightManager::getLightListRange(const Renderable* i_Rend, - Vector4& o_GridBounds, unsigned int& o_IndexMin, unsigned int& o_IndexMax) -{ - o_IndexMin = 100000; - o_IndexMax = 0; - - const LightList& lights = i_Rend->getLights(); - LightList::const_iterator it = lights.begin(), itEnd = lights.end(); - for(; it != itEnd ; ++it) - { - MapLightData::const_iterator itActive = mActiveLights.find(*it); - if (itActive != mActiveLights.end()) - { - o_IndexMin = (unsigned int)std::min(o_IndexMin, itActive->second.getIndexMin()); - o_IndexMax = (unsigned int)std::max(o_IndexMax, itActive->second.getIndexMax()); - } - } - - o_GridBounds.x = mGridMinX; - o_GridBounds.y = mGridMinZ; - o_GridBounds.z = SDL_SEGMENT_DIVISIONS / (mGridMaxX - mGridMinX); - o_GridBounds.w = SDL_SEGMENT_DIVISIONS / (mGridMaxZ - mGridMinZ); - o_IndexMin += SDL_TEXTURE_DATA_ROWS; - o_IndexMax += SDL_TEXTURE_DATA_ROWS; - return o_IndexMin <= o_IndexMax; -} - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////// SegmentedDynamicLightManager::LightData -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -//------------------------------------------------------------------------------ -SegmentedDynamicLightManager::LightData::LightData() -{ - mIndexMin = 100000; - mIndexMax = 0; - mMinX = std::numeric_limits::max(); - mMaxX = -std::numeric_limits::max(); - mMinZ = std::numeric_limits::max(); - mMaxZ = -std::numeric_limits::max(); -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::LightData::setBounds(const AxisAlignedBox& i_Bounds) -{ - mMinX = i_Bounds.getMinimum().x; - mMaxX = i_Bounds.getMaximum().x; - mMinZ = i_Bounds.getMinimum().z; - mMaxZ = i_Bounds.getMaximum().z; -} - -//------------------------------------------------------------------------------ -void SegmentedDynamicLightManager::LightData::addIndexToRange(unsigned int i_LightIndex) -{ - mIndexMin = (unsigned int)std::min(mIndexMin, i_LightIndex); - mIndexMax = (unsigned int)std::max(mIndexMax, i_LightIndex); -} diff --git a/Samples/ShaderSystemMultiLight/src/ShaderSystemMultiLight.cpp b/Samples/ShaderSystemMultiLight/src/ShaderSystemMultiLight.cpp deleted file mode 100644 index 813ab7d9b96..00000000000 --- a/Samples/ShaderSystemMultiLight/src/ShaderSystemMultiLight.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "SamplePlugin.h" -#include "ShaderSystemMultiLight.h" - -using namespace Ogre; -using namespace OgreBites; - -const String Sample_ShaderSystemMultiLight::DEBUG_MODE_CHECKBOX = "DebugMode"; -const String Sample_ShaderSystemMultiLight::NUM_OF_LIGHTS_SLIDER = "NumOfLights"; -const String Sample_ShaderSystemMultiLight::TWIRL_LIGHTS_CHECKBOX = "TwirlLights"; diff --git a/Samples/ShaderSystemMultiLight/include/ShaderSystemMultiLight.h b/Samples/Simple/include/ShaderSystemMultiLight.h similarity index 58% rename from Samples/ShaderSystemMultiLight/include/ShaderSystemMultiLight.h rename to Samples/Simple/include/ShaderSystemMultiLight.h index 5b9fde8090f..793260dbf7f 100644 --- a/Samples/ShaderSystemMultiLight/include/ShaderSystemMultiLight.h +++ b/Samples/Simple/include/ShaderSystemMultiLight.h @@ -2,38 +2,9 @@ #define __ShaderSystemMultiLight_H__ #include "SdkSample.h" -#include "SegmentedDynamicLightManager.h" -#include "RTShaderSRSSegmentedLights.h" #include "OgreControllerManager.h" #include "OgreBillboard.h" -/* -Part of the original guidelines under which the RTSS was created was to emulate the fixed pipeline mechanism as close as possible. -Due to this fact and how it was interpreted using multiple lights in RTSS with the default implementation is problematic. Every light -requires it's own line in the shader. Every time an object receives a different amount of lights the shader for is invalidated and lights -recompiled. Amount of is also limited by the amount of const registers a shader supports. - -The following example shows a different approach to rendering lights in RTSS. A few points on this system - - Only one directional light is supported. - - Point lights and spot lights are handled through the same code. - - Light attenuation is only controlled by range. all other parameters are ignored (to produce more efficient shader programs) - - point light specular effect is not calculated (to produce more faster shader programs). If any one wants to add it feel free. - - Large amount of lights can be supported. Limited currently by the size of the texture used to send the light information to the - shader (currently set to a 9x9 grid. each grid cell can contain 32 lights). - - No need to recompile the shader when the number of lights on an object changes - - Sample requires shader model 3 or higher to run in order - - The world is divided into a grid of 9x9 cells (can be easily increased). Each cell receives it's own list of lights appropriate - only for it. This can be increased depending on your situation. - - The information of the lights in the grid is transferred onto a texture. Which is sent to the shader. - - The list of lights is iterated over in the shader through a dynamic loop. - - -Note: -This code was somewhat inspired by Kojack's "Tons of street lights" (http://www.ogre3d.org/forums/viewtopic.php?t=48412) idea. One of -the more innovative ideas I've seen of late. - -*/ - using namespace Ogre; using namespace OgreBites; @@ -44,72 +15,29 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample static const uint8 cPriorityLights = 55; static const uint32 cInitialLightCount = 3; - static const String DEBUG_MODE_CHECKBOX; - static const String NUM_OF_LIGHTS_SLIDER; - static const String TWIRL_LIGHTS_CHECKBOX; + static constexpr const char* DEBUG_MODE_CHECKBOX = "DebugModeCheckbox"; + static constexpr const char* NUM_OF_LIGHTS_SLIDER = "NumOfLightsSlider"; + static constexpr const char* CLUSTERED_CULLING_CHECKBOX = "ClusteredCullingCheckbox"; public: Sample_ShaderSystemMultiLight() : - mTwirlLights(false), - mSRSSegLightFactory(NULL), mPathNameGen("RTPath") { mInfo["Title"] = "ShaderSystem - Multi Light"; - mInfo["Description"] = "Shows a possible way to support a large varying amount of spot lights in the RTSS using a relatively simple system." - "Note in debug mode green and red lines show the light grid. Blue shows the amount of lights processed per grid position."; + mInfo["Description"] = "Shows a possible way to support a large varying amount of spot lights"; mInfo["Thumbnail"] = "thumb_shadersystemmultilight.png"; mInfo["Category"] = "Lighting"; } - ~Sample_ShaderSystemMultiLight() - { - - } - - void _shutdown() override - { - delete SegmentedDynamicLightManager::getSingletonPtr(); - - RTShader::RenderState* pMainRenderState = - RTShader::ShaderGenerator::getSingleton().createOrRetrieveRenderState(MSN_SHADERGEN).first; - pMainRenderState->resetToBuiltinSubRenderStates(); - - if (mSRSSegLightFactory) - { - RTShader::ShaderGenerator::getSingleton().removeAllShaderBasedTechniques(); - RTShader::ShaderGenerator::getSingleton().removeSubRenderStateFactory(mSRSSegLightFactory); - delete mSRSSegLightFactory; - mSRSSegLightFactory = NULL; - } - - while (mLights.size()) - { - removeSpotLight(); - } - - SdkSample::_shutdown(); - } - bool frameRenderingQueued(const FrameEvent& evt) override { // Move the lights along their paths for(size_t i = 0 ; i < mLights.size() ; ++i) { mLights[i].animState->addTime(evt.timeSinceLastFrame); - if (mTwirlLights) - { - mLights[i].dirnode->setDirection( - Quaternion(Degree(ControllerManager::getSingleton().getElapsedTime() * 150 + 360 * i / (float)mLights.size()), Vector3::UNIT_Y) * - Vector3(0,-1,-1).normalisedCopy(), Node::TS_WORLD); - } - else - { - mLights[i].dirnode->setDirection(Vector3::NEGATIVE_UNIT_Y, Node::TS_WORLD); - } } - - + return SdkSample::frameRenderingQueued(evt); // don't forget the parent class updates! } @@ -117,9 +45,11 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample void setupContent() override { - mTrayMgr->createThickSlider(TL_BOTTOM, NUM_OF_LIGHTS_SLIDER, "Num of lights", 240, 80, 0, 64, 65)->setValue(cInitialLightCount, false); - mTrayMgr->createCheckBox(TL_BOTTOM, TWIRL_LIGHTS_CHECKBOX, "Twirl Lights", 240)->setChecked(false, false); - mTrayMgr->createCheckBox(TL_BOTTOM, DEBUG_MODE_CHECKBOX, "Show Grid", 240)->setChecked(false, false); + mTrayMgr->createThickSlider(TL_BOTTOM, NUM_OF_LIGHTS_SLIDER, "Num of lights", 240, 80, 0, 128, 129)->setValue(cInitialLightCount, false); + mTrayMgr->createCheckBox(TL_BOTTOM, CLUSTERED_CULLING_CHECKBOX, "Clustered Light Culling", 240)->setChecked(true, false); + mTrayMgr->createCheckBox(TL_BOTTOM, DEBUG_MODE_CHECKBOX, "Show Occupancy", 240)->setChecked(false, false); + + mCamera->setNearClipDistance(30); // Set our camera to orbit around the origin at a suitable distance mCameraMan->setStyle(CS_ORBIT); @@ -153,25 +83,17 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample void setupShaderGenerator() { - new SegmentedDynamicLightManager; - - SegmentedDynamicLightManager::getSingleton().setSceneManager(mSceneMgr); - - RTShader::ShaderGenerator* mGen = RTShader::ShaderGenerator::getSingletonPtr(); - - RTShader::RenderState* pMainRenderState = - mGen->createOrRetrieveRenderState(MSN_SHADERGEN).first; + RTShader::RenderState* pMainRenderState = mShaderGenerator->getRenderState(MSN_SHADERGEN); pMainRenderState->resetToBuiltinSubRenderStates(); - // If we are using segmented lighting, no auto light update required. (prevent constant invalidation) - pMainRenderState->setLightCountAutoUpdate(false); - mSRSSegLightFactory = new RTShaderSRSSegmentedLightsFactory; - mGen->addSubRenderStateFactory(mSRSSegLightFactory); - pMainRenderState->addTemplateSubRenderState( - mGen->createSubRenderState()); + if(mClusteredLightCullingEnabled) + { + mClusteredLightCullingSRS = mShaderGenerator->createSubRenderState(RTShader::SRS_CLUSTERED_LIGHT_CULLING); + pMainRenderState->addTemplateSubRenderState(mClusteredLightCullingSRS); + } - mGen->invalidateScheme(Ogre::MSN_SHADERGEN); + mShaderGenerator->invalidateScheme(Ogre::MSN_SHADERGEN); // Make this viewport work with shader generator scheme. mViewport->setMaterialScheme(MSN_SHADERGEN); @@ -202,7 +124,7 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample LightState state; // Create a light node - state.node = mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(50, 30, 0)); + state.node = mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(50, 10, 0)); String animName = mPathNameGen.generate(); // Create a 14 second animation with spline interpolation @@ -218,7 +140,7 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample Vector3 firstFramePos; for(int i = 0 ; i <= animPoints ; ++i) { - Vector3 framePos(rand01() * 900 - 500, 10 + rand01() * 100, rand01() * 900 - 500); + Vector3 framePos(rand01() * 900 - 500, rand01() * 20, rand01() * 900 - 500); if (i == 0) { firstFramePos = framePos; @@ -245,7 +167,7 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample state.light = mSceneMgr->createLight(); state.light->setCastShadows(false); state.light->setType(mLights.size() % 10 ? Light::LT_SPOTLIGHT : Light::LT_POINT); - state.light->setAttenuation(200,0,0,0); + state.light->setAttenuation(50,1,0,0); state.light->setDiffuseColour(lightColor); state.dirnode = state.node->createChildSceneNode(); state.dirnode->setDirection(Vector3::NEGATIVE_UNIT_Y, Node::TS_WORLD); @@ -254,6 +176,7 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample // Attach a flare with the same colour to the light node state.bbs = mSceneMgr->createBillboardSet(1); Billboard* bb = state.bbs->createBillboard(Vector3::ZERO, lightColor); + bb->setDimensions(25, 25); bb->setColour(lightColor); state.bbs->setMaterialName("Examples/Flare"); state.bbs->setRenderQueueGroup(cPriorityLights); @@ -264,16 +187,7 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample float rand01() { - return (abs(rand()) % 1000) / 1000.0f; - } - - void setDebugModeState(bool state) - { - bool needInvalidate = SegmentedDynamicLightManager::getSingleton().setDebugMode(state); - if (needInvalidate) - { - RTShader::ShaderGenerator::getSingleton().invalidateScheme(MSN_SHADERGEN); - } + return Math::UnitRandom(); } //-------------------------------------------------------------------------- @@ -320,11 +234,22 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample if (cbName == DEBUG_MODE_CHECKBOX) { - setDebugModeState(box->isChecked()); + if(!mClusteredLightCullingEnabled) + { + box->setChecked(false, false); + return; + } + mClusteredLightCullingSRS->setParameter("debug", box->isChecked() ? "true" : "false"); + RTShader::ShaderGenerator::getSingleton().invalidateScheme(MSN_SHADERGEN); } - if (cbName == TWIRL_LIGHTS_CHECKBOX) + if (cbName == CLUSTERED_CULLING_CHECKBOX) { - mTwirlLights = box->isChecked(); + mClusteredLightCullingEnabled = box->isChecked(); + setupShaderGenerator(); + if(!mClusteredLightCullingEnabled) + { + mClusteredLightCullingSRS = NULL; + } } } private: @@ -340,11 +265,10 @@ class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample BillboardSet* bbs; }; - typedef std::vector VecLights; - VecLights mLights; - bool mTwirlLights; + std::vector mLights; + bool mClusteredLightCullingEnabled = true; - RTShaderSRSSegmentedLightsFactory* mSRSSegLightFactory; + RTShader::SubRenderState* mClusteredLightCullingSRS = NULL; NameGenerator mPathNameGen; };