shadow caching

SPECIAL NOTE: highly suggest https://github.com/GarageGames/Torque3D/pull/1441 or a variation thereof to prevent debug spew and false-postives for occlusion results.

With significant research, development and prototyping assistance from both @andr3wmac (shaders and partial hook work), and @LuisAntonRebollo (additional culling)

System operates as follows:
1) materials are given an additional castDynamicShadows boolean entry. (Default at time of writing is true by request. Personal usage at time of writing defaults to false. value is default-initialized in materialDefinition.cpp. script/gui exposed)
2) lights are given a staticRefreshFreq and dynamicRefreshFreq (in milliseconds). script/gui exposed
3) materials are (effectively) sorted into dynamic and static shadowmap render lists based on flag. (see shadowMapPass.cpp)
4) initial shadowmaps are generated for each light and 'list'.
5) as each refreshFreq times out, the relevant shadowmap for a given light is refreshed.

Special notes:
dynamicRefreshFreq for all lights is set to a (script exposed) 8MS refresh timer.
StaticRefreshFreq for the lions share of lights defaults to 250 MS (1/4 of a second)
scattersky's embedded light, which is intended to operate in a mobile manner, defaults to 8
to reiterate, these are all customizable per-light via script/inspector gui in the case of alternate project needs.
This commit is contained in:
Azaezel 2015-10-13 18:12:19 -05:00
parent 2044b2691e
commit 2753f562e8
54 changed files with 1477 additions and 464 deletions

View file

@ -60,6 +60,8 @@ LightBase::LightBase()
mColor( ColorF::WHITE ),
mBrightness( 1.0f ),
mCastShadows( false ),
mStaticRefreshFreq( 250 ),
mDynamicRefreshFreq( 8 ),
mPriority( 1.0f ),
mAnimationData( NULL ),
mFlareData( NULL ),
@ -90,6 +92,8 @@ void LightBase::initPersistFields()
addField( "color", TypeColorF, Offset( mColor, LightBase ), "Changes the base color hue of the light." );
addField( "brightness", TypeF32, Offset( mBrightness, LightBase ), "Adjusts the lights power, 0 being off completely." );
addField( "castShadows", TypeBool, Offset( mCastShadows, LightBase ), "Enables/disabled shadow casts by this light." );
addField( "staticRefreshFreq", TypeS32, Offset( mStaticRefreshFreq, LightBase ), "static shadow refresh rate (milliseconds)" );
addField( "dynamicRefreshFreq", TypeS32, Offset( mDynamicRefreshFreq, LightBase ), "dynamic shadow refresh rate (milliseconds)" );
addField( "priority", TypeF32, Offset( mPriority, LightBase ), "Used for sorting of lights by the light manager. "
"Priority determines if a light has a stronger effect than, those with a lower value" );
@ -277,6 +281,8 @@ U32 LightBase::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
stream->write( mBrightness );
stream->writeFlag( mCastShadows );
stream->write(mStaticRefreshFreq);
stream->write(mDynamicRefreshFreq);
stream->write( mPriority );
@ -322,6 +328,8 @@ void LightBase::unpackUpdate( NetConnection *conn, BitStream *stream )
stream->read( &mColor );
stream->read( &mBrightness );
mCastShadows = stream->readFlag();
stream->read(&mStaticRefreshFreq);
stream->read(&mDynamicRefreshFreq);
stream->read( &mPriority );

View file

@ -56,7 +56,8 @@ protected:
F32 mBrightness;
bool mCastShadows;
S32 mStaticRefreshFreq;
S32 mDynamicRefreshFreq;
F32 mPriority;
LightInfo *mLight;

View file

@ -36,6 +36,8 @@ LightDescription::LightDescription()
brightness( 1.0f ),
range( 5.0f ),
castShadows( false ),
mStaticRefreshFreq( 250 ),
mDynamicRefreshFreq( 8 ),
animationData( NULL ),
animationDataId( 0 ),
animationPeriod( 1.0f ),
@ -94,6 +96,8 @@ void LightDescription::initPersistFields()
addField( "brightness", TypeF32, Offset( brightness, LightDescription ), "Adjusts the lights power, 0 being off completely." );
addField( "range", TypeF32, Offset( range, LightDescription ), "Controls the size (radius) of the light" );
addField( "castShadows", TypeBool, Offset( castShadows, LightDescription ), "Enables/disabled shadow casts by this light." );
addField( "staticRefreshFreq", TypeS32, Offset( mStaticRefreshFreq, LightDescription ), "static shadow refresh rate (milliseconds)" );
addField( "dynamicRefreshFreq", TypeS32, Offset( mDynamicRefreshFreq, LightDescription ), "dynamic shadow refresh rate (milliseconds)" );
endGroup( "Light" );
@ -153,6 +157,8 @@ void LightDescription::packData( BitStream *stream )
stream->write( brightness );
stream->write( range );
stream->writeFlag( castShadows );
stream->write(mStaticRefreshFreq);
stream->write(mDynamicRefreshFreq);
stream->write( animationPeriod );
stream->write( animationPhase );
@ -181,6 +187,8 @@ void LightDescription::unpackData( BitStream *stream )
stream->read( &brightness );
stream->read( &range );
castShadows = stream->readFlag();
stream->read(&mStaticRefreshFreq);
stream->read(&mDynamicRefreshFreq);
stream->read( &animationPeriod );
stream->read( &animationPhase );
@ -200,6 +208,8 @@ void LightDescription::submitLight( LightState *state, const MatrixF &xfm, Light
li->setRange( range );
li->setColor( color );
li->setCastShadows( castShadows );
li->setStaticRefreshFreq(mStaticRefreshFreq);
li->setDynamicRefreshFreq(mDynamicRefreshFreq);
li->setTransform( xfm );
if ( animationData )

View file

@ -101,6 +101,8 @@ public:
F32 brightness;
F32 range;
bool castShadows;
S32 mStaticRefreshFreq;
S32 mDynamicRefreshFreq;
LightAnimData *animationData;
S32 animationDataId;

View file

@ -116,6 +116,8 @@ void PointLight::_conformLights()
mLight->setBrightness( mBrightness );
mLight->setCastShadows( mCastShadows );
mLight->setStaticRefreshFreq(mStaticRefreshFreq);
mLight->setDynamicRefreshFreq(mDynamicRefreshFreq);
mLight->setPriority( mPriority );
// Update the bounds and scale to fit our light.

View file

@ -122,6 +122,8 @@ void SpotLight::_conformLights()
mLight->setColor( mColor );
mLight->setBrightness( mBrightness );
mLight->setCastShadows( mCastShadows );
mLight->setStaticRefreshFreq(mStaticRefreshFreq);
mLight->setDynamicRefreshFreq(mDynamicRefreshFreq);
mLight->setPriority( mPriority );
mOuterConeAngle = getMax( 0.01f, mOuterConeAngle );

View file

@ -151,6 +151,8 @@ ScatterSky::ScatterSky()
mBrightness = 1.0f;
mCastShadows = true;
mStaticRefreshFreq = 8;
mDynamicRefreshFreq = 8;
mDirty = true;
mLight = LightManager::createLightInfo();
@ -271,6 +273,8 @@ void ScatterSky::_conformLights()
mLight->setAmbient( mAmbientColor );
mLight->setColor( mSunColor );
mLight->setCastShadows( mCastShadows );
mLight->setStaticRefreshFreq(mStaticRefreshFreq);
mLight->setDynamicRefreshFreq(mDynamicRefreshFreq);
FogData fog = getSceneManager()->getFogData();
fog.color = mFogColor;
@ -381,6 +385,9 @@ void ScatterSky::initPersistFields()
addField( "castShadows", TypeBool, Offset( mCastShadows, ScatterSky ),
"Enables/disables shadows cast by objects due to ScatterSky light." );
addField("staticRefreshFreq", TypeS32, Offset(mStaticRefreshFreq, ScatterSky), "static shadow refresh rate (milliseconds)");
addField("dynamicRefreshFreq", TypeS32, Offset(mDynamicRefreshFreq, ScatterSky), "dynamic shadow refresh rate (milliseconds)");
addField( "brightness", TypeF32, Offset( mBrightness, ScatterSky ),
"The brightness of the ScatterSky's light object." );
@ -487,6 +494,8 @@ U32 ScatterSky::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
stream->write( mBrightness );
stream->writeFlag( mCastShadows );
stream->write(mStaticRefreshFreq);
stream->write(mDynamicRefreshFreq);
stream->write( mFlareScale );
@ -588,6 +597,8 @@ void ScatterSky::unpackUpdate(NetConnection *con, BitStream *stream)
stream->read( &mBrightness );
mCastShadows = stream->readFlag();
stream->read(&mStaticRefreshFreq);
stream->read(&mDynamicRefreshFreq);
stream->read( &mFlareScale );

View file

@ -207,6 +207,8 @@ protected:
LightInfo *mLight;
bool mCastShadows;
S32 mStaticRefreshFreq;
S32 mDynamicRefreshFreq;
bool mDirty;
LightFlareData *mFlareData;

View file

@ -66,6 +66,8 @@ Sun::Sun()
mSunAzimuth = 0.0f;
mSunElevation = 35.0f;
mCastShadows = true;
mStaticRefreshFreq = 250;
mDynamicRefreshFreq = 8;
mAnimateSun = false;
mTotalTime = 0.0f;
@ -163,7 +165,10 @@ void Sun::initPersistFields()
"Adjust the Sun's global contrast/intensity");
addField( "castShadows", TypeBool, Offset( mCastShadows, Sun ),
"Enables/disables shadows cast by objects due to Sun light");
"Enables/disables shadows cast by objects due to Sun light");
addField("staticRefreshFreq", TypeS32, Offset(mStaticRefreshFreq, Sun), "static shadow refresh rate (milliseconds)");
addField("dynamicRefreshFreq", TypeS32, Offset(mDynamicRefreshFreq, Sun), "dynamic shadow refresh rate (milliseconds)");
endGroup( "Lighting" );
@ -220,7 +225,9 @@ U32 Sun::packUpdate(NetConnection *conn, U32 mask, BitStream *stream )
stream->write( mLightColor );
stream->write( mLightAmbient );
stream->write( mBrightness );
stream->writeFlag( mCastShadows );
stream->writeFlag( mCastShadows );
stream->write(mStaticRefreshFreq);
stream->write(mDynamicRefreshFreq);
stream->write( mFlareScale );
if ( stream->writeFlag( mFlareData ) )
@ -254,6 +261,8 @@ void Sun::unpackUpdate( NetConnection *conn, BitStream *stream )
stream->read( &mLightAmbient );
stream->read( &mBrightness );
mCastShadows = stream->readFlag();
stream->read(&mStaticRefreshFreq);
stream->read(&mDynamicRefreshFreq);
stream->read( &mFlareScale );
if ( stream->readFlag() )
@ -426,6 +435,8 @@ void Sun::_conformLights()
// directional color are the same.
bool castShadows = mLightColor != mLightAmbient && mCastShadows;
mLight->setCastShadows( castShadows );
mLight->setStaticRefreshFreq(mStaticRefreshFreq);
mLight->setDynamicRefreshFreq(mDynamicRefreshFreq);
}
void Sun::_initCorona()

View file

@ -65,6 +65,8 @@ protected:
F32 mEndElevation;
bool mCastShadows;
S32 mStaticRefreshFreq;
S32 mDynamicRefreshFreq;
LightInfo *mLight;

View file

@ -118,6 +118,9 @@ S32 ForestCell::renderBatches( SceneRenderState *state, Frustum *culler )
if ( culler && culler->isCulled( mBatches[i]->getWorldBox() ) )
continue;
if( state->getCullingState().isOccludedWithExtraPlanesCull( mBatches[i]->getWorldBox() ) )
continue;
mBatches[i]->render( state );
renderedItems += mBatches[i]->getItemCount();
}

View file

@ -197,6 +197,10 @@ void Forest::prepRenderImage( SceneRenderState *state )
if ( smDisableImposters )
continue;
// if cell are to far for largest item, then skip out.
if( TSShapeInstance::smLastPixelSize < TSShapeInstance::smSmallestVisiblePixelSize )
continue;
PROFILE_SCOPE(Forest_RenderBatches);
// Keep track of how many cells were batched.

View file

@ -191,10 +191,11 @@ void AdvancedLightBinManager::addLight( LightInfo *light )
// Find a shadow map for this light, if it has one
ShadowMapParams *lsp = light->getExtended<ShadowMapParams>();
LightShadowMap *lsm = lsp->getShadowMap();
LightShadowMap *dynamicShadowMap = lsp->getShadowMap(true);
// Get the right shadow type.
ShadowType shadowType = ShadowType_None;
if ( light->getCastShadows() &&
if ( light->getCastShadows() &&
lsm && lsm->hasShadowTex() &&
!ShadowMapPass::smDisableShadows )
shadowType = lsm->getShadowType();
@ -203,6 +204,7 @@ void AdvancedLightBinManager::addLight( LightInfo *light )
LightBinEntry lEntry;
lEntry.lightInfo = light;
lEntry.shadowMap = lsm;
lEntry.dynamicShadowMap = dynamicShadowMap;
lEntry.lightMaterial = _getLightMaterial( lightType, shadowType, lsp->hasCookieTex() );
if( lightType == LightInfo::Spot )
@ -325,10 +327,12 @@ void AdvancedLightBinManager::render( SceneRenderState *state )
setupSGData( sgData, state, curLightInfo );
curLightMat->setLightParameters( curLightInfo, state, worldToCameraXfm );
mShadowManager->setLightShadowMap( curEntry.shadowMap );
mShadowManager->setLightDynamicShadowMap( curEntry.dynamicShadowMap );
// Let the shadow know we're about to render from it.
if ( curEntry.shadowMap )
curEntry.shadowMap->preLightRender();
if ( curEntry.dynamicShadowMap ) curEntry.dynamicShadowMap->preLightRender();
// Set geometry
GFX->setVertexBuffer( curEntry.vertBuffer );
@ -351,10 +355,12 @@ void AdvancedLightBinManager::render( SceneRenderState *state )
// Tell it we're done rendering.
if ( curEntry.shadowMap )
curEntry.shadowMap->postLightRender();
if ( curEntry.dynamicShadowMap ) curEntry.dynamicShadowMap->postLightRender();
}
// Set NULL for active shadow map (so nothing gets confused)
mShadowManager->setLightShadowMap(NULL);
mShadowManager->setLightDynamicShadowMap(NULL);
GFX->setVertexBuffer( NULL );
GFX->setPrimitiveBuffer( NULL );

View file

@ -185,6 +185,7 @@ protected:
{
LightInfo* lightInfo;
LightShadowMap* shadowMap;
LightShadowMap* dynamicShadowMap;
LightMaterialInfo* lightMaterial;
GFXPrimitiveBuffer* primBuffer;
GFXVertexBuffer* vertBuffer;

View file

@ -357,10 +357,13 @@ void AdvancedLightManager::setLightInfo( ProcessedMaterial *pmat,
LightingShaderConstants *lsc = getLightingShaderConstants(shaderConsts);
LightShadowMap *lsm = SHADOWMGR->getCurrentShadowMap();
LightShadowMap *dynamicShadowMap = SHADOWMGR->getCurrentDynamicShadowMap();
LightInfo *light;
if ( lsm )
if (lsm)
light = lsm->getLightInfo();
else if ( dynamicShadowMap )
light = dynamicShadowMap->getLightInfo();
else
{
light = sgData.lights[0];
@ -385,10 +388,11 @@ void AdvancedLightManager::setLightInfo( ProcessedMaterial *pmat,
lsc->mLightInvRadiusSqSC,
lsc->mLightSpotDirSC,
lsc->mLightSpotAngleSC,
lsc->mLightSpotFalloffSC,
lsc->mLightSpotFalloffSC,
shaderConsts );
if ( lsm && light->getCastShadows() )
// Static
if (lsm && light->getCastShadows())
{
if ( lsc->mWorldToLightProjSC->isValid() )
shaderConsts->set( lsc->mWorldToLightProjSC,
@ -426,6 +430,46 @@ void AdvancedLightManager::setLightInfo( ProcessedMaterial *pmat,
lsc->mViewToLightProjSC->getType() );
}
}
// Dynamic
if ( dynamicShadowMap )
{
if ( lsc->mDynamicWorldToLightProjSC->isValid() )
shaderConsts->set( lsc->mDynamicWorldToLightProjSC,
dynamicShadowMap->getWorldToLightProj(),
lsc->mDynamicWorldToLightProjSC->getType() );
if ( lsc->mDynamicViewToLightProjSC->isValid() )
{
// TODO: Should probably cache these results and
// not do this mul here on every material that needs
// this transform.
shaderConsts->set( lsc->mDynamicViewToLightProjSC,
dynamicShadowMap->getWorldToLightProj() * state->getCameraTransform(),
lsc->mDynamicViewToLightProjSC->getType() );
}
shaderConsts->setSafe( lsc->mShadowMapSizeSC, 1.0f / (F32)dynamicShadowMap->getTexSize() );
// Do this last so that overrides can properly override parameters previously set
dynamicShadowMap->setShaderParameters(shaderConsts, lsc);
}
else
{
if ( lsc->mDynamicViewToLightProjSC->isValid() )
{
// TODO: Should probably cache these results and
// not do this mul here on every material that needs
// this transform.
MatrixF proj;
light->getWorldToLightProj( &proj );
shaderConsts->set( lsc->mDynamicViewToLightProjSC,
proj * state->getCameraTransform(),
lsc->mDynamicViewToLightProjSC->getType() );
}
}
}
void AdvancedLightManager::registerGlobalLight(LightInfo *light, SimObject *obj)
@ -454,22 +498,34 @@ bool AdvancedLightManager::setTextureStage( const SceneData &sgData,
ShaderConstHandles *handles )
{
LightShadowMap* lsm = SHADOWMGR->getCurrentShadowMap();
LightShadowMap* dynamicShadowMap = SHADOWMGR->getCurrentDynamicShadowMap();
// Assign Shadowmap, if it exists
LightingShaderConstants* lsc = getLightingShaderConstants(shaderConsts);
if ( !lsc )
return false;
if ( lsm && lsm->getLightInfo()->getCastShadows() )
return lsm->setTextureStage( currTexFlag, lsc );
if ( currTexFlag == Material::DynamicLight )
{
// Static
if ( lsm && lsm->getLightInfo()->getCastShadows() )
return lsm->setTextureStage( currTexFlag, lsc );
S32 reg = lsc->mShadowMapSC->getSamplerRegister();
if ( reg != -1 )
GFX->setTexture( reg, GFXTexHandle::ONE );
return true;
} else if ( currTexFlag == Material::DynamicShadowMap )
{
// Dynamic
if ( dynamicShadowMap )
return dynamicShadowMap->setTextureStage( currTexFlag, lsc );
S32 reg = lsc->mDynamicShadowMapSC->getSamplerRegister();
if ( reg != -1 )
GFX->setTexture( reg, GFXTexHandle::ONE );
return true;
}
else if ( currTexFlag == Material::DynamicLightMask )

View file

@ -50,6 +50,8 @@ LightInfo::LightInfo()
mOuterConeAngle( 90.0f ),
mType( Vector ),
mCastShadows( false ),
mStaticRefreshFreq( 250 ),
mDynamicRefreshFreq( 8 ),
mPriority( 1.0f ),
mScore( 0.0f ),
mDebugRender( false )
@ -72,6 +74,8 @@ void LightInfo::set( const LightInfo *light )
mOuterConeAngle = light->mOuterConeAngle;
mType = light->mType;
mCastShadows = light->mCastShadows;
mStaticRefreshFreq = light->mStaticRefreshFreq;
mDynamicRefreshFreq = light->mDynamicRefreshFreq;
for ( U32 i=0; i < mExtended.size(); i++ )
{

View file

@ -131,6 +131,9 @@ protected:
bool mCastShadows;
S32 mStaticRefreshFreq;
S32 mDynamicRefreshFreq;
::Vector<LightInfoEx*> mExtended;
/// The priority of this light used for
@ -190,6 +193,12 @@ public:
bool getCastShadows() const { return mCastShadows; }
void setCastShadows( bool castShadows ) { mCastShadows = castShadows; }
S32 getStaticRefreshFreq() const { return mStaticRefreshFreq; }
void setStaticRefreshFreq(S32 _staticRefreshFreq) { mStaticRefreshFreq = _staticRefreshFreq; }
S32 getDynamicRefreshFreq() const { return mDynamicRefreshFreq; }
void setDynamicRefreshFreq(S32 _dynamicRefreshFreq) { mDynamicRefreshFreq = _dynamicRefreshFreq; }
void setPriority( F32 priority ) { mPriority = priority; }
F32 getPriority() const { return mPriority; }

View file

@ -92,14 +92,17 @@ LightShadowMap::LightShadowMap( LightInfo *light )
mVizQuery( NULL ),
mWasOccluded( false ),
mLastScreenSize( 0.0f ),
mLastPriority( 0.0f )
mLastPriority( 0.0f ),
mIsDynamic( false )
{
GFXTextureManager::addEventDelegate( this, &LightShadowMap::_onTextureEvent );
mTarget = GFX->allocRenderToTextureTarget();
mVizQuery = GFX->createOcclusionQuery();
smShadowMaps.push_back( this );
smShadowMaps.push_back(this);
mStaticRefreshTimer = PlatformTimer::create();
mDynamicRefreshTimer = PlatformTimer::create();
}
LightShadowMap::~LightShadowMap()
@ -268,11 +271,20 @@ GFXTextureObject* LightShadowMap::_getDepthTarget( U32 width, U32 height )
bool LightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc )
{
if ( currTexFlag == Material::DynamicLight )
if ( currTexFlag == Material::DynamicLight && !isDynamic() )
{
S32 reg = lsc->mShadowMapSC->getSamplerRegister();
if ( reg != -1 )
GFX->setTexture( reg, mShadowMapTex);
if ( reg != -1 )
GFX->setTexture( reg, mShadowMapTex);
return true;
} else if ( currTexFlag == Material::DynamicShadowMap && isDynamic() )
{
S32 reg = lsc->mDynamicShadowMapSC->getSamplerRegister();
if ( reg != -1 )
GFX->setTexture( reg, mShadowMapTex);
return true;
}
@ -296,8 +308,18 @@ bool LightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants*
}
void LightShadowMap::render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState )
const SceneRenderState *diffuseState,
bool _dynamic)
{
// control how often shadow maps are refreshed
if (!_dynamic && (mStaticRefreshTimer->getElapsedMs() < getLightInfo()->getStaticRefreshFreq()))
return;
mStaticRefreshTimer->reset();
if (_dynamic && (mDynamicRefreshTimer->getElapsedMs() < getLightInfo()->getDynamicRefreshFreq()))
return;
mDynamicRefreshTimer->reset();
mDebugTarget.setTexture( NULL );
_render( renderPass, diffuseState );
mDebugTarget.setTexture( mShadowMapTex );
@ -456,25 +478,35 @@ LightingShaderConstants::LightingShaderConstants()
mLightInvRadiusSqSC(NULL),
mLightSpotDirSC(NULL),
mLightSpotAngleSC(NULL),
mLightSpotFalloffSC(NULL),
mLightSpotFalloffSC(NULL),
mShadowMapSC(NULL),
mDynamicShadowMapSC(NULL),
mShadowMapSizeSC(NULL),
mCookieMapSC(NULL),
mRandomDirsConst(NULL),
mShadowSoftnessConst(NULL),
mAtlasXOffsetSC(NULL),
mAtlasYOffsetSC(NULL),
mAtlasScaleSC(NULL),
mFadeStartLength(NULL),
mOverDarkFactorPSSM(NULL),
mTapRotationTexSC(NULL),
mWorldToLightProjSC(NULL),
mViewToLightProjSC(NULL),
mScaleXSC(NULL),
mScaleYSC(NULL),
mOffsetXSC(NULL),
mOffsetYSC(NULL),
mAtlasXOffsetSC(NULL),
mAtlasYOffsetSC(NULL),
mAtlasScaleSC(NULL),
mFadeStartLength(NULL),
mFarPlaneScalePSSM(NULL),
mOverDarkFactorPSSM(NULL),
mTapRotationTexSC(NULL)
mDynamicWorldToLightProjSC(NULL),
mDynamicViewToLightProjSC(NULL),
mDynamicScaleXSC(NULL),
mDynamicScaleYSC(NULL),
mDynamicOffsetXSC(NULL),
mDynamicOffsetYSC(NULL),
mDynamicFarPlaneScalePSSM(NULL)
{
}
@ -513,29 +545,35 @@ void LightingShaderConstants::init(GFXShader* shader)
mLightSpotFalloffSC = shader->getShaderConstHandle( ShaderGenVars::lightSpotFalloff );
mShadowMapSC = shader->getShaderConstHandle("$shadowMap");
mDynamicShadowMapSC = shader->getShaderConstHandle("$dynamicShadowMap");
mShadowMapSizeSC = shader->getShaderConstHandle("$shadowMapSize");
mCookieMapSC = shader->getShaderConstHandle("$cookieMap");
mShadowSoftnessConst = shader->getShaderConstHandle("$shadowSoftness");
mWorldToLightProjSC = shader->getShaderConstHandle("$worldToLightProj");
mViewToLightProjSC = shader->getShaderConstHandle("$viewToLightProj");
mScaleXSC = shader->getShaderConstHandle("$scaleX");
mScaleYSC = shader->getShaderConstHandle("$scaleY");
mOffsetXSC = shader->getShaderConstHandle("$offsetX");
mOffsetYSC = shader->getShaderConstHandle("$offsetY");
mAtlasXOffsetSC = shader->getShaderConstHandle("$atlasXOffset");
mAtlasYOffsetSC = shader->getShaderConstHandle("$atlasYOffset");
mAtlasScaleSC = shader->getShaderConstHandle("$atlasScale");
mFadeStartLength = shader->getShaderConstHandle("$fadeStartLength");
mOverDarkFactorPSSM = shader->getShaderConstHandle("$overDarkPSSM");
mTapRotationTexSC = shader->getShaderConstHandle( "$gTapRotationTex" );
mWorldToLightProjSC = shader->getShaderConstHandle("$worldToLightProj");
mViewToLightProjSC = shader->getShaderConstHandle("$viewToLightProj");
mScaleXSC = shader->getShaderConstHandle("$scaleX");
mScaleYSC = shader->getShaderConstHandle("$scaleY");
mOffsetXSC = shader->getShaderConstHandle("$offsetX");
mOffsetYSC = shader->getShaderConstHandle("$offsetY");
mFarPlaneScalePSSM = shader->getShaderConstHandle("$farPlaneScalePSSM");
mOverDarkFactorPSSM = shader->getShaderConstHandle("$overDarkPSSM");
mTapRotationTexSC = shader->getShaderConstHandle( "$gTapRotationTex" );
mDynamicWorldToLightProjSC = shader->getShaderConstHandle("$dynamicWorldToLightProj");
mDynamicViewToLightProjSC = shader->getShaderConstHandle("$dynamicViewToLightProj");
mDynamicScaleXSC = shader->getShaderConstHandle("$dynamicScaleX");
mDynamicScaleYSC = shader->getShaderConstHandle("$dynamicScaleY");
mDynamicOffsetXSC = shader->getShaderConstHandle("$dynamicOffsetX");
mDynamicOffsetYSC = shader->getShaderConstHandle("$dynamicOffsetY");
mDynamicFarPlaneScalePSSM = shader->getShaderConstHandle("$dynamicFarPlaneScalePSSM");
mInit = true;
}
@ -558,7 +596,9 @@ LightInfoExType ShadowMapParams::Type( "" );
ShadowMapParams::ShadowMapParams( LightInfo *light )
: mLight( light ),
mShadowMap( NULL )
mShadowMap( NULL ),
mDynamicShadowMap ( NULL ),
isDynamic ( true )
{
attenuationRatio.set( 0.0f, 1.0f, 1.0f );
shadowType = ShadowType_Spot;
@ -576,6 +616,7 @@ ShadowMapParams::ShadowMapParams( LightInfo *light )
ShadowMapParams::~ShadowMapParams()
{
SAFE_DELETE( mShadowMap );
SAFE_DELETE( mDynamicShadowMap );
}
void ShadowMapParams::_validate()
@ -632,39 +673,55 @@ void ShadowMapParams::_validate()
texSize = mClamp( texSize, 32, maxTexSize );
}
LightShadowMap* ShadowMapParams::getOrCreateShadowMap()
LightShadowMap* ShadowMapParams::getOrCreateShadowMap(bool _isDynamic)
{
if ( mShadowMap )
if (_isDynamic && mDynamicShadowMap)
return mDynamicShadowMap;
if (!_isDynamic && mShadowMap)
return mShadowMap;
if ( !mLight->getCastShadows() )
return NULL;
LightShadowMap* newShadowMap = NULL;
switch ( mLight->getType() )
{
case LightInfo::Spot:
mShadowMap = new SingleLightShadowMap( mLight );
newShadowMap = new SingleLightShadowMap( mLight );
break;
case LightInfo::Vector:
mShadowMap = new PSSMLightShadowMap( mLight );
newShadowMap = new PSSMLightShadowMap( mLight );
break;
case LightInfo::Point:
if ( shadowType == ShadowType_CubeMap )
mShadowMap = new CubeLightShadowMap( mLight );
newShadowMap = new CubeLightShadowMap( mLight );
else if ( shadowType == ShadowType_Paraboloid )
mShadowMap = new ParaboloidLightShadowMap( mLight );
newShadowMap = new ParaboloidLightShadowMap( mLight );
else
mShadowMap = new DualParaboloidLightShadowMap( mLight );
newShadowMap = new DualParaboloidLightShadowMap( mLight );
break;
default:
break;
}
return mShadowMap;
if ( _isDynamic )
{
newShadowMap->setDynamic( true );
mDynamicShadowMap = newShadowMap;
return mDynamicShadowMap;
}
else
{
newShadowMap->setDynamic(false);
mShadowMap = newShadowMap;
return mShadowMap;
}
}
GFXTextureObject* ShadowMapParams::getCookieTex()
@ -739,6 +796,7 @@ void ShadowMapParams::unpackUpdate( BitStream *stream )
// map so it can be reallocated on the next render.
shadowType = newType;
SAFE_DELETE( mShadowMap );
SAFE_DELETE( mDynamicShadowMap );
}
mathRead( *stream, &attenuationRatio );

View file

@ -47,6 +47,9 @@
#ifndef _GFXSHADER_H_
#include "gfx/gfxShader.h"
#endif
#ifndef _PLATFORM_PLATFORMTIMER_H_
#include "platform/platformTimer.h"
#endif
class ShadowMapManager;
class SceneManager;
@ -88,20 +91,13 @@ struct LightingShaderConstants
GFXShaderConstHandle *mLightSpotFalloffSC;
GFXShaderConstHandle* mShadowMapSC;
GFXShaderConstHandle* mDynamicShadowMapSC;
GFXShaderConstHandle* mShadowMapSizeSC;
GFXShaderConstHandle* mCookieMapSC;
GFXShaderConstHandle* mRandomDirsConst;
GFXShaderConstHandle* mShadowSoftnessConst;
GFXShaderConstHandle* mWorldToLightProjSC;
GFXShaderConstHandle* mViewToLightProjSC;
GFXShaderConstHandle* mScaleXSC;
GFXShaderConstHandle* mScaleYSC;
GFXShaderConstHandle* mOffsetXSC;
GFXShaderConstHandle* mOffsetYSC;
GFXShaderConstHandle* mAtlasXOffsetSC;
GFXShaderConstHandle* mAtlasYOffsetSC;
GFXShaderConstHandle* mAtlasScaleSC;
@ -109,11 +105,28 @@ struct LightingShaderConstants
// fadeStartLength.x = Distance in eye space to start fading shadows
// fadeStartLength.y = 1 / Length of fade
GFXShaderConstHandle* mFadeStartLength;
GFXShaderConstHandle* mFarPlaneScalePSSM;
GFXShaderConstHandle* mOverDarkFactorPSSM;
GFXShaderConstHandle* mTapRotationTexSC;
// Static Specific:
GFXShaderConstHandle* mWorldToLightProjSC;
GFXShaderConstHandle* mViewToLightProjSC;
GFXShaderConstHandle* mScaleXSC;
GFXShaderConstHandle* mScaleYSC;
GFXShaderConstHandle* mOffsetXSC;
GFXShaderConstHandle* mOffsetYSC;
GFXShaderConstHandle* mFarPlaneScalePSSM;
// Dynamic Specific:
GFXShaderConstHandle* mDynamicWorldToLightProjSC;
GFXShaderConstHandle* mDynamicViewToLightProjSC;
GFXShaderConstHandle* mDynamicScaleXSC;
GFXShaderConstHandle* mDynamicScaleYSC;
GFXShaderConstHandle* mDynamicOffsetXSC;
GFXShaderConstHandle* mDynamicOffsetYSC;
GFXShaderConstHandle* mDynamicFarPlaneScalePSSM;
LightingShaderConstants();
~LightingShaderConstants();
@ -147,7 +160,8 @@ public:
virtual ~LightShadowMap();
void render( RenderPassManager* renderPass,
const SceneRenderState *diffuseState );
const SceneRenderState *diffuseState,
bool _dynamic);
U32 getLastUpdate() const { return mLastUpdate; }
@ -237,6 +251,8 @@ protected:
/// The time this shadow was last updated.
U32 mLastUpdate;
PlatformTimer *mStaticRefreshTimer;
PlatformTimer *mDynamicRefreshTimer;
/// The time this shadow was last culled and prioritized.
U32 mLastCull;
@ -274,6 +290,13 @@ protected:
/// The callback used to get texture events.
/// @see GFXTextureManager::addEventDelegate
void _onTextureEvent( GFXTexCallbackCode code );
bool mIsDynamic;
public:
bool isDynamic() { return mIsDynamic; }
void setDynamic(bool value) { mIsDynamic = value; }
};
GFX_DeclareTextureProfile( ShadowMapProfile );
@ -296,9 +319,9 @@ public:
virtual void packUpdate( BitStream *stream ) const;
virtual void unpackUpdate( BitStream *stream );
LightShadowMap* getShadowMap() const { return mShadowMap; }
LightShadowMap* getShadowMap(bool _isDynamic = false) const { return _isDynamic ? mDynamicShadowMap : mShadowMap; }
LightShadowMap* getOrCreateShadowMap();
LightShadowMap* getOrCreateShadowMap(bool _isDynamic = false);
bool hasCookieTex() const { return cookie.isNotEmpty(); }
@ -315,6 +338,7 @@ protected:
///
LightShadowMap *mShadowMap;
LightShadowMap *mDynamicShadowMap;
LightInfo *mLight;
@ -376,6 +400,7 @@ public:
bool lastSplitTerrainOnly;
/// @}
bool isDynamic;
};
#endif // _LIGHTSHADOWMAP_H_

View file

@ -37,6 +37,7 @@
#include "materials/shaderData.h"
#include "ts/tsShapeInstance.h"
#include "console/consoleTypes.h"
#include "math/mathUtils.h"
AFTER_MODULE_INIT( Sim )
@ -248,6 +249,9 @@ void PSSMLightShadowMap::_render( RenderPassManager* renderPass,
TSShapeInstance::smDetailAdjust *= smDetailAdjustScale;
TSShapeInstance::smSmallestVisiblePixelSize = smSmallestVisiblePixelSize;
Vector< Vector<PlaneF> > _extraCull;
_calcPlanesCullForShadowCasters( _extraCull, fullFrustum, mLight->getDirection() );
for (U32 i = 0; i < mNumSplits; i++)
{
GFXTransformSaver saver;
@ -365,12 +369,17 @@ void PSSMLightShadowMap::_render( RenderPassManager* renderPass,
shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
PlaneSetF planeSet( _extraCull[i].address(), _extraCull[i].size() );
shadowRenderState.getCullingState().setExtraPlanesCull( planeSet );
U32 objectMask = SHADOW_TYPEMASK;
if ( i == mNumSplits-1 && params->lastSplitTerrainOnly )
objectMask = TerrainObjectType;
sceneManager->renderSceneNoLights( &shadowRenderState, objectMask );
shadowRenderState.getCullingState().clearExtraPlanesCull();
_debugRender( &shadowRenderState );
}
@ -435,18 +444,28 @@ void PSSMLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, Light
}
}
params->setSafe(lsc->mScaleXSC, sx);
params->setSafe(lsc->mScaleYSC, sy);
params->setSafe(lsc->mOffsetXSC, ox);
params->setSafe(lsc->mOffsetYSC, oy);
// These values change based on static/dynamic.
if ( mIsDynamic )
{
params->setSafe(lsc->mDynamicScaleXSC, sx);
params->setSafe(lsc->mDynamicScaleYSC, sy);
params->setSafe(lsc->mDynamicOffsetXSC, ox);
params->setSafe(lsc->mDynamicOffsetYSC, oy);
params->setSafe( lsc->mDynamicFarPlaneScalePSSM, mFarPlaneScalePSSM);
} else {
params->setSafe(lsc->mScaleXSC, sx);
params->setSafe(lsc->mScaleYSC, sy);
params->setSafe(lsc->mOffsetXSC, ox);
params->setSafe(lsc->mOffsetYSC, oy);
params->setSafe( lsc->mFarPlaneScalePSSM, mFarPlaneScalePSSM);
}
params->setSafe(lsc->mAtlasXOffsetSC, aXOff);
params->setSafe(lsc->mAtlasYOffsetSC, aYOff);
params->setSafe(lsc->mAtlasScaleSC, shadowMapAtlas);
Point4F lightParams( mLight->getRange().x, p->overDarkFactor.x, 0.0f, 0.0f );
params->setSafe( lsc->mLightParamsSC, lightParams );
params->setSafe( lsc->mFarPlaneScalePSSM, mFarPlaneScalePSSM);
Point2F fadeStartLength(p->fadeStartDist, 0.0f);
if (fadeStartLength.x == 0.0f)
@ -462,3 +481,117 @@ void PSSMLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, Light
// The softness is a factor of the texel size.
params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) );
}
void PSSMLightShadowMap::_calcPlanesCullForShadowCasters(Vector< Vector<PlaneF> > &out, const Frustum &viewFrustum, const Point3F &_ligthDir)
{
#define ENABLE_CULL_ASSERT
PROFILE_SCOPE(PSSMLightShadowMap_render_getCullFrustrum);
Point3F ligthDir = _ligthDir;
PlaneF lightFarPlane, lightNearPlane;
MatrixF lightFarPlaneMat(true);
MatrixF invLightFarPlaneMat(true);
// init data
{
ligthDir.normalize();
Point3F viewDir = viewFrustum.getTransform().getForwardVector();
viewDir.normalize();
const Point3F viewPosition = viewFrustum.getPosition();
const F32 viewDistance = viewFrustum.getBounds().len();
lightNearPlane = PlaneF(viewPosition + (viewDistance * -ligthDir), ligthDir);
const Point3F lightFarPlanePos = viewPosition + (viewDistance * ligthDir);
lightFarPlane = PlaneF(lightFarPlanePos, -ligthDir);
lightFarPlaneMat = MathUtils::createOrientFromDir(-ligthDir);
lightFarPlaneMat.setPosition(lightFarPlanePos);
lightFarPlaneMat.invertTo(&invLightFarPlaneMat);
}
Vector<Point2F> projVertices;
//project all frustum vertices into plane
// all vertices are 2d and local to far plane
projVertices.setSize(8);
for (int i = 0; i < 8; ++i) //
{
const Point3F &point = viewFrustum.getPoints()[i];
#ifdef ENABLE_CULL_ASSERT
AssertFatal( PlaneF::Front == lightNearPlane.whichSide(point), "" );
AssertFatal( PlaneF::Front == lightFarPlane.whichSide(point), "" );
#endif
Point3F localPoint(lightFarPlane.project(point));
invLightFarPlaneMat.mulP(localPoint);
projVertices[i] = Point2F(localPoint.x, localPoint.z);
}
//create hull arround projected proints
Vector<Point2F> hullVerts;
MathUtils::mBuildHull2D(projVertices, hullVerts);
Vector<PlaneF> planes;
planes.push_back(lightNearPlane);
planes.push_back(lightFarPlane);
//build planes
for (int i = 0; i < (hullVerts.size() - 1); ++i)
{
Point2F pos2D = (hullVerts[i] + hullVerts[i + 1]) / 2;
Point3F pos3D(pos2D.x, 0, pos2D.y);
Point3F pos3DA(hullVerts[i].x, 0, hullVerts[i].y);
Point3F pos3DB(hullVerts[i + 1].x, 0, hullVerts[i + 1].y);
// move hull points to 3d space
lightFarPlaneMat.mulP(pos3D);
lightFarPlaneMat.mulP(pos3DA);
lightFarPlaneMat.mulP(pos3DB);
PlaneF plane(pos3D, MathUtils::mTriangleNormal(pos3DB, pos3DA, (pos3DA - ligthDir)));
planes.push_back(plane);
}
//recalculate planes for each splits
for (int split = 0; split < mNumSplits; ++split)
{
Frustum subFrustum(viewFrustum);
subFrustum.cropNearFar(mSplitDist[split], mSplitDist[split + 1]);
subFrustum.setFarDist(getMin(subFrustum.getFarDist()*2.5f, viewFrustum.getFarDist()));
subFrustum.update();
Vector<PlaneF> subPlanes = planes;
for (int planeIdx = 0; planeIdx < subPlanes.size(); ++planeIdx)
{
PlaneF &plane = subPlanes[planeIdx];
F32 minDist = 0;
//calculate near vertex distance
for (int vertexIdx = 0; vertexIdx < 8; ++vertexIdx)
{
Point3F point = subFrustum.getPoints()[vertexIdx];
minDist = getMin(plane.distToPlane(point), minDist);
}
// move plane to near vertex
Point3F newPos = plane.getPosition() + (plane.getNormal() * minDist);
plane = PlaneF(newPos, plane.getNormal());
#ifdef ENABLE_CULL_ASSERT
for(int x = 0; x < 8; ++x)
{
AssertFatal( PlaneF::Back != plane.whichSide( subFrustum.getPoints()[x] ), "");
}
#endif
}
out.push_back(subPlanes);
}
#undef ENABLE_CULL_ASSERT
}

View file

@ -56,6 +56,7 @@ protected:
void _setNumSplits( U32 numSplits, U32 texSize );
void _calcSplitPos(const Frustum& currFrustum);
Box3F _calcClipSpaceAABB(const Frustum& f, const MatrixF& transform, F32 farDist);
void _calcPlanesCullForShadowCasters(Vector< Vector<PlaneF> > &out, const Frustum &viewFrustum, const Point3F &_ligthDir);
void _roundProjection(const MatrixF& lightMat, const MatrixF& cropMatrix, Point3F &offset, U32 splitNum);
static const S32 MAX_SPLITS = 4;

View file

@ -86,6 +86,7 @@ Signal<void(void)> ShadowMapManager::smShadowDeactivateSignal;
ShadowMapManager::ShadowMapManager()
: mShadowMapPass(NULL),
mCurrentShadowMap(NULL),
mCurrentDynamicShadowMap(NULL),
mIsActive(false)
{
}
@ -98,9 +99,15 @@ void ShadowMapManager::setLightShadowMapForLight( LightInfo *light )
{
ShadowMapParams *params = light->getExtended<ShadowMapParams>();
if ( params )
{
mCurrentShadowMap = params->getShadowMap();
mCurrentDynamicShadowMap = params->getShadowMap(true);
}
else
{
mCurrentShadowMap = NULL;
mCurrentDynamicShadowMap = NULL;
}
}
void ShadowMapManager::activate()

View file

@ -60,12 +60,14 @@ public:
/// Sets the current shadowmap (used in setLightInfo/setTextureStage calls)
void setLightShadowMap( LightShadowMap *lm ) { mCurrentShadowMap = lm; }
void setLightDynamicShadowMap( LightShadowMap *lm ) { mCurrentDynamicShadowMap = lm; }
/// Looks up the shadow map for the light then sets it.
void setLightShadowMapForLight( LightInfo *light );
/// Return the current shadow map
LightShadowMap* getCurrentShadowMap() const { return mCurrentShadowMap; }
LightShadowMap* getCurrentDynamicShadowMap() const { return mCurrentDynamicShadowMap; }
ShadowMapPass* getShadowMapPass() const { return mShadowMapPass; }
@ -88,6 +90,7 @@ protected:
ShadowMapPass *mShadowMapPass;
LightShadowMap *mCurrentShadowMap;
LightShadowMap *mCurrentDynamicShadowMap;
///
GFXTexHandle mTapRotationTex;

View file

@ -55,6 +55,11 @@ bool ShadowMapPass::smDisableShadows = false;
bool ShadowMapPass::smDisableShadowsEditor = false;
bool ShadowMapPass::smDisableShadowsPref = false;
/// milliseconds before static redraw
S32 ShadowMapPass::smStaticShadowUpdateFreq = 32;
/// milliseconds before dynamic redraw
S32 ShadowMapPass::smDynamicShadowUpdateFreq = 16;
/// We have a default 8ms render budget for shadow rendering.
U32 ShadowMapPass::smRenderBudgetMs = 8;
@ -62,18 +67,27 @@ ShadowMapPass::ShadowMapPass(LightManager* lightManager, ShadowMapManager* shado
{
mLightManager = lightManager;
mShadowManager = shadowManager;
// Setup our render pass managers
// Static
mShadowRPM = new ShadowRenderPassManager();
mShadowRPM->assignName( "ShadowRenderPassManager" );
mShadowRPM->registerObject();
Sim::getRootGroup()->addObject( mShadowRPM );
// Setup our render pass manager
mShadowRPM->addManager( new RenderMeshMgr(RenderPassManager::RIT_Mesh, 0.3f, 0.3f) );
//mShadowRPM->addManager( new RenderObjectMgr() );
mShadowRPM->addManager( new RenderTerrainMgr( 0.5f, 0.5f ) );
mShadowRPM->addManager( new RenderImposterMgr( 0.6f, 0.6f ) );
// Dynamic
mDynamicShadowRPM = new DynamicShadowRenderPassManager();
mDynamicShadowRPM->assignName( "DynamicShadowRenderPassManager" );
mDynamicShadowRPM->registerObject();
Sim::getRootGroup()->addObject( mDynamicShadowRPM );
mDynamicShadowRPM->addManager( new RenderMeshMgr(RenderPassManager::RIT_Mesh, 0.3f, 0.3f) );
mDynamicShadowRPM->addManager( new RenderTerrainMgr( 0.5f, 0.5f ) );
mDynamicShadowRPM->addManager( new RenderImposterMgr( 0.6f, 0.6f ) );
mActiveLights = 0;
mTimer = PlatformTimer::create();
@ -117,6 +131,9 @@ ShadowMapPass::~ShadowMapPass()
if ( mShadowRPM )
mShadowRPM->deleteObject();
if ( mDynamicShadowRPM )
mDynamicShadowRPM->deleteObject();
}
void ShadowMapPass::render( SceneManager *sceneManager,
@ -147,7 +164,7 @@ void ShadowMapPass::render( SceneManager *sceneManager,
// First do a loop thru the lights setting up the shadow
// info array for this pass.
Vector<LightShadowMap*> shadowMaps;
shadowMaps.reserve( mActiveLights );
shadowMaps.reserve( mActiveLights * 2 );
for ( U32 i = 0; i < mActiveLights; i++ )
{
ShadowMapParams *params = mLights[i]->getExtended<ShadowMapParams>();
@ -155,12 +172,14 @@ void ShadowMapPass::render( SceneManager *sceneManager,
// Before we do anything... skip lights without shadows.
if ( !mLights[i]->getCastShadows() || smDisableShadows )
continue;
LightShadowMap *lsm = params->getOrCreateShadowMap();
// --- Static Shadow Map ---
LightShadowMap *lsm = params->getOrCreateShadowMap();
LightShadowMap *dlsm = params->getOrCreateShadowMap(true);
// First check the visiblity query... if it wasn't
// visible skip it.
if ( lsm->wasOccluded() )
if (lsm->wasOccluded() || dlsm->wasOccluded())
continue;
// Any shadow that is visible is counted as being
@ -168,13 +187,25 @@ void ShadowMapPass::render( SceneManager *sceneManager,
++smActiveShadowMaps;
// Do a priority update for this shadow.
lsm->updatePriority( diffuseState, currTime );
lsm->updatePriority(diffuseState, currTime);
shadowMaps.push_back( lsm );
shadowMaps.push_back(lsm);
// --- Dynamic Shadow Map ---
// Any shadow that is visible is counted as being
// active regardless if we update it or not.
++smActiveShadowMaps;
// Do a priority update for this shadow.
dlsm->updatePriority(diffuseState, currTime);
shadowMaps.push_back( dlsm );
}
// Now sort the shadow info by priority.
shadowMaps.sort( LightShadowMap::cmpPriority );
// andrewmac: tempoarily disabled until I find a better solution.
//shadowMaps.sort( LightShadowMap::cmpPriority );
GFXDEBUGEVENT_SCOPE( ShadowMapPass_Render, ColorI::RED );
@ -183,22 +214,28 @@ void ShadowMapPass::render( SceneManager *sceneManager,
mTimer->getElapsedMs();
mTimer->reset();
for ( U32 i = 0; i < shadowMaps.size(); i++ )
// 2 Shadow Maps per Light. This may fail.
for ( U32 i = 0; i < shadowMaps.size(); i += 2 )
{
LightShadowMap *lsm = shadowMaps[i];
LightShadowMap *lsm = shadowMaps[i];
LightShadowMap *dlsm = shadowMaps[i + 1];
{
GFXDEBUGEVENT_SCOPE( ShadowMapPass_Render_Shadow, ColorI::RED );
mShadowManager->setLightShadowMap( lsm );
lsm->render( mShadowRPM, diffuseState );
mShadowManager->setLightShadowMap(lsm);
mShadowManager->setLightDynamicShadowMap( dlsm );
lsm->render(mShadowRPM, diffuseState, false);
dlsm->render(mDynamicShadowRPM, diffuseState, true);
++smUpdatedShadowMaps;
}
// View dependent shadows or ones that are covering the entire
// screen are updated every frame no matter the time left in
// our shadow rendering budget.
if ( lsm->isViewDependent() || lsm->getLastScreenSize() >= 1.0f )
if ( dlsm->isViewDependent() || dlsm->getLastScreenSize() >= 1.0f )
{
++smNearShadowMaps;
continue;
@ -224,6 +261,7 @@ void ShadowMapPass::render( SceneManager *sceneManager,
// The NULL here is importaint as having it around
// will cause extra work in AdvancedLightManager::setLightInfo().
mShadowManager->setLightShadowMap( NULL );
mShadowManager->setLightDynamicShadowMap( NULL );
}
void ShadowRenderPassManager::addInst( RenderInst *inst )
@ -237,7 +275,29 @@ void ShadowRenderPassManager::addInst( RenderInst *inst )
return;
const BaseMaterialDefinition *mat = meshRI->matInst->getMaterial();
if ( !mat->castsShadows() || mat->isTranslucent() )
if ( !mat->castsShadows() || mat->castsDynamicShadows() || mat->isTranslucent() )
{
// Do not add this instance, return here and avoid the default behavior
// of calling up to Parent::addInst()
return;
}
}
Parent::addInst(inst);
}
void DynamicShadowRenderPassManager::addInst( RenderInst *inst )
{
PROFILE_SCOPE(DynamicShadowRenderPassManager_addInst);
if ( inst->type == RIT_Mesh )
{
MeshRenderInst *meshRI = static_cast<MeshRenderInst*>( inst );
if ( !meshRI->matInst )
return;
const BaseMaterialDefinition *mat = meshRI->matInst->getMaterial();
if ( !mat->castsShadows() || !mat->castsDynamicShadows() || mat->isTranslucent() )
{
// Do not add this instance, return here and avoid the default behavior
// of calling up to Parent::addInst()

View file

@ -45,6 +45,7 @@ class RenderObjectMgr;
class RenderTerrainMgr;
class PlatformTimer;
class ShadowRenderPassManager;
class DynamicShadowRenderPassManager;
/// ShadowMapPass, this is plugged into the SceneManager to generate
/// ShadowMaps for the scene.
@ -83,6 +84,11 @@ public:
static bool smDisableShadowsEditor;
static bool smDisableShadowsPref;
/// milliseconds before static redraw
static S32 smStaticShadowUpdateFreq;
/// milliseconds before dynamic redraw
static S32 smDynamicShadowUpdateFreq;
private:
static U32 smActiveShadowMaps;
@ -103,6 +109,7 @@ private:
LightInfoList mLights;
U32 mActiveLights;
SimObjectPtr<ShadowRenderPassManager> mShadowRPM;
SimObjectPtr<DynamicShadowRenderPassManager> mDynamicShadowRPM;
LightManager* mLightManager;
ShadowMapManager* mShadowManager;
};
@ -117,4 +124,14 @@ public:
virtual void addInst( RenderInst *inst );
};
class DynamicShadowRenderPassManager : public RenderPassManager
{
typedef RenderPassManager Parent;
public:
DynamicShadowRenderPassManager() : Parent() {}
/// Add a RenderInstance to the list
virtual void addInst(RenderInst *inst);
};
#endif // _SHADOWMAPPASS_H_

View file

@ -37,6 +37,7 @@ public:
virtual bool isDoubleSided() const = 0;
virtual bool isLightmapped() const = 0;
virtual bool castsShadows() const = 0;
virtual bool castsDynamicShadows() const = 0;
};
#endif // _BASEMATERIALDEFINITION_H_

View file

@ -182,6 +182,7 @@ Material::Material()
mAlphaRef = 1;
mCastShadows = true;
mCastDynamicShadows = true;
mPlanarReflection = false;
@ -288,7 +289,7 @@ void Material::initPersistFields()
addField( "useAnisotropic", TypeBool, Offset(mUseAnisotropic, Material), MAX_STAGES,
"Use anisotropic filtering for the textures of this stage." );
addField("envMap", TypeImageFilename, Offset(mEnvMapFilename, Material), MAX_STAGES,
"The name of an environment map cube map to apply to this material." );
@ -390,6 +391,9 @@ void Material::initPersistFields()
addField( "castShadows", TypeBool, Offset(mCastShadows, Material),
"If set to false the lighting system will not cast shadows from this material." );
addField( "castDynamicShadows", TypeBool, Offset(mCastDynamicShadows, Material),
"If set to false the lighting system will not cast dynamic shadows from this material." );
addField("planarReflection", TypeBool, Offset(mPlanarReflection, Material), "@internal" );
addField("translucent", TypeBool, Offset(mTranslucent, Material),

View file

@ -89,6 +89,7 @@ public:
NormalizeCube,
TexTarget,
AccuMap,
DynamicShadowMap,
};
enum BlendOp
@ -219,7 +220,7 @@ public:
/// The strength scalar for the detail normal map.
F32 mDetailNormalMapStrength[MAX_STAGES];
FileName mEnvMapFilename[MAX_STAGES];
/// This color is the diffuse color of the material
@ -299,6 +300,7 @@ public:
/// A generic setting which tells the system to skip
/// generation of shadows from this material.
bool mCastShadows;
bool mCastDynamicShadows;
bool mAlphaTest;
U32 mAlphaRef;
@ -355,6 +357,7 @@ public:
virtual void setAutoGenerated(bool isAutoGenerated) { mAutoGenerated = isAutoGenerated; }
virtual bool isLightmapped() const;
virtual bool castsShadows() const { return mCastShadows; }
virtual bool castsDynamicShadows() const { return mCastDynamicShadows; }
const String &getPath() const { return mPath; }
void flush();

View file

@ -85,6 +85,14 @@ void ProcessedCustomMaterial::_setStageData()
continue;
}
if(filename.equal(String("$dynamicShadowMap"), String::NoCase))
{
rpd->mTexType[i] = Material::DynamicShadowMap;
rpd->mSamplerNames[i] = mCustomMaterial->mSamplerNames[i];
mMaxTex = i+1;
continue;
}
if(filename.equal(String("$dynamiclightmask"), String::NoCase))
{
rpd->mTexType[i] = Material::DynamicLightMask;

View file

@ -908,6 +908,14 @@ inline bool mIsNaN( const Point2F &p )
return mIsNaN_F( p.x ) || mIsNaN_F( p.y );
}
/// Return 0 if points are colinear
/// Return positive if p0p1p2 are counter-clockwise
/// Return negative if p0p1p2 are clockwise
inline F64 mCross(const Point2F &p0, const Point2F &p1, const Point2F &pt2)
{
return (p1.x - p0.x) * (pt2.y - p0.y) - (p1.y - p0.y) * (pt2.x - p0.x);
}
namespace DictHash
{

View file

@ -1845,4 +1845,55 @@ U32 extrudePolygonEdgesFromPoint( const Point3F* vertices, U32 numVertices, cons
return numPlanes;
}
//-----------------------------------------------------------------------------
void MathUtils::mBuildHull2D(const Vector<Point2F> _inPoints, Vector<Point2F> &hullPoints)
{
/// Andrew's monotone chain convex hull algorithm implementation
struct Util
{
//compare by x and then by y
static int CompareLexicographic( const Point2F *a, const Point2F *b)
{
return a->x < b->x || (a->x == b->x && a->y < b->y);
}
};
hullPoints.clear();
hullPoints.setSize( _inPoints.size()*2 );
// sort in points by x and then by y
Vector<Point2F> inSortedPoints = _inPoints;
inSortedPoints.sort( &Util::CompareLexicographic );
Point2F* lowerHullPtr = hullPoints.address();
U32 lowerHullIdx = 0;
//lower part of hull
for( int i = 0; i < inSortedPoints.size(); ++i )
{
while( lowerHullIdx >= 2 && mCross( lowerHullPtr[ lowerHullIdx - 2], lowerHullPtr[lowerHullIdx - 1], inSortedPoints[i] ) <= 0 )
--lowerHullIdx;
lowerHullPtr[lowerHullIdx++] = inSortedPoints[i];
}
--lowerHullIdx; // last point are the same as first in upperHullPtr
Point2F* upperHullPtr = hullPoints.address() + lowerHullIdx;
U32 upperHullIdx = 0;
//upper part of hull
for( int i = inSortedPoints.size()-1; i >= 0; --i )
{
while( upperHullIdx >= 2 && mCross( upperHullPtr[ upperHullIdx - 2], upperHullPtr[upperHullIdx - 1], inSortedPoints[i] ) <= 0 )
--upperHullIdx;
upperHullPtr[upperHullIdx++] = inSortedPoints[i];
}
hullPoints.setSize( lowerHullIdx + upperHullIdx );
}
} // namespace MathUtils

View file

@ -417,6 +417,9 @@ namespace MathUtils
//void findFarthestPoint( const Point3F* points, U32 numPoints, const Point3F& fromPoint, )
/// Build a convex hull from a cloud of 2D points, first and last hull point are the same.
void mBuildHull2D(const Vector<Point2F> inPoints, Vector<Point2F> &hullPoints);
} // namespace MathUtils
#endif // _MATHUTILS_H_

View file

@ -88,6 +88,8 @@ SceneCullingState::SceneCullingState( SceneManager* sceneManager, const SceneCam
SceneCullingVolume::Includer,
PlaneSetF( planes, 4 )
);
clearExtraPlanesCull();
}
//-----------------------------------------------------------------------------
@ -789,6 +791,9 @@ U32 SceneCullingState::cullObjects( SceneObject** objects, U32 numObjects, U32 c
result == SceneZoneCullingState::CullingTestPositiveByOcclusion );
}
if( !isCulled )
isCulled = isOccludedWithExtraPlanesCull( object->getWorldBox() );
if( !isCulled )
objects[ numRemainingObjects ++ ] = object;
}

View file

@ -106,6 +106,9 @@ class SceneCullingState
/// The root culling frustum, which may be different from the camera frustum
Frustum mCullingFrustum;
/// Extra planes for culling.
PlaneSetF mExtraPlanesCull;
/// Occluders that have been added to this render state. Adding an occluder does not
/// necessarily result in an occluder volume being added. To not repeatedly try to
/// process the same occluder object, all objects that are added are recorded here.
@ -301,6 +304,21 @@ class SceneCullingState
/// (or, if no zone is selected, all volumes in the outdoor zone) to the debug drawer.
void debugRenderCullingVolumes() const;
/// Set planes for extra culling
void setExtraPlanesCull( const PlaneSetF &cull) { mExtraPlanesCull = cull; }
/// Clear planes for extra culling.
void clearExtraPlanesCull() { mExtraPlanesCull = PlaneSetF(NULL, 0); }
/// Check extra planes culling
bool isOccludedWithExtraPlanesCull(const Box3F &box) const
{
if(mExtraPlanesCull.getNumPlanes())
return mExtraPlanesCull.testPotentialIntersection( box ) == GeometryOutside;
return false;
}
private:
typedef SceneZoneCullingState::CullingTestResult CullingTestResult;