Skip to content

Commit

Permalink
Implemented normal offset bias (#100)
Browse files Browse the repository at this point in the history
This feature produces MUCH better shadow results using front faces.
It has overall fewer artifacts

New features:
- Added normal_offset_bias / ShadowTextureDefinition::normalOffsetBias
to globally control this normal offset bias per shadow map / cascade
- Added constant_bias_scale /
ShadowTextureDefinition::constantBiasScale. Constant bias is still
per-material, but it can be globally amplified/decrease per shadow map /
cascade
- Constant bias is again automatically amplified with deeper shadow
maps, as otherwise acne becomes visible (most obvious when using PSSM on
the higher cascades). With the previous commit it was constant across
all ranges and cascades

Default mShadowMappingUseBackFaces to false, but it will be removed in
the next commit. This feature is much more accuratem, we don't have the
resources to maintain two codepaths, and the switching between the two
requires aligning many variables (shader changes so constant bias is
constant across regardless of depth range, set normal offset bias to 0,
set mShadowMappingUseBackFaces to true)
  • Loading branch information
darksylinc committed Jun 11, 2020
1 parent f3c0d82 commit f9bd68a
Show file tree
Hide file tree
Showing 22 changed files with 257 additions and 47 deletions.
2 changes: 2 additions & 0 deletions Components/Hlms/Pbs/include/OgreHlmsPbs.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ namespace Ogre

ConstBufferPool::BufferPool const *mLastBoundPool;

float mConstantBiasScale;

bool mHasSeparateSamplers;
DescriptorSetTexture const *mLastDescTexture;
DescriptorSetSampler const *mLastDescSampler;
Expand Down
11 changes: 7 additions & 4 deletions Components/Hlms/Pbs/src/OgreHlmsPbs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ namespace Ogre
mDecalsDiffuseMergedEmissive( false ),
mDecalsSamplerblock( 0 ),
mLastBoundPool( 0 ),
mConstantBiasScale( 1.0f ),
mHasSeparateSamplers( 0 ),
mLastDescTexture( 0 ),
mLastDescSampler( 0 ),
Expand Down Expand Up @@ -1491,6 +1492,7 @@ namespace Ogre
retVal.setProperties = mSetProperties;

CamerasInProgress cameras = sceneManager->getCamerasInProgress();
mConstantBiasScale = cameras.renderingCamera->_getConstantBiasScale();
Matrix4 viewMatrix = cameras.renderingCamera->getVrViewMatrix( 0 );

Matrix4 projectionMatrix = cameras.renderingCamera->getProjectionMatrixWithRSDepth();
Expand Down Expand Up @@ -1578,7 +1580,8 @@ namespace Ogre

//mat4 view + mat4 shadowRcv[numShadowMapLights].texViewProj +
// vec2 shadowRcv[numShadowMapLights].shadowDepthRange +
// vec2 padding +
// float normalOffsetBias +
// float padding +
// vec4 shadowRcv[numShadowMapLights].invShadowMapSize +
//mat3 invViewMatCubemap (upgraded to three vec4)
mapSize += ( 16 + (16 + 2 + 2 + 4) * numShadowMapLights + 4 * 3 ) * 4;
Expand Down Expand Up @@ -1899,7 +1902,7 @@ namespace Ogre
*passBufferPtr++ = fNear;
}
*passBufferPtr++ = 1.0f / depthRange;
*passBufferPtr++ = 0.0f;
*passBufferPtr++ = shadowNode->getNormalOffsetBias( (size_t)shadowMapTexIdx );
*passBufferPtr++ = 0.0f; //Padding


Expand Down Expand Up @@ -3210,8 +3213,8 @@ namespace Ogre
currentMappedTexBuffer = mStartMappedTexBuffer + currentConstOffset;
}

*reinterpret_cast<float * RESTRICT_ALIAS>( currentMappedConstBuffer+1 ) = datablock->
mShadowConstantBias;
*reinterpret_cast<float * RESTRICT_ALIAS>( currentMappedConstBuffer + 1 ) =
datablock->mShadowConstantBias * mConstantBiasScale;
#if !OGRE_NO_FINE_LIGHT_MASK_GRANULARITY
*( currentMappedConstBuffer+2u ) = queuedRenderable.movableObject->getLightMask();
#endif
Expand Down
2 changes: 2 additions & 0 deletions Components/Hlms/Unlit/include/OgreHlmsUnlit.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ namespace Ogre
DescriptorSetTexture const *mLastDescTexture;
DescriptorSetSampler const *mLastDescSampler;

float mConstantBiasScale;

bool mUsingExponentialShadowMaps;
uint16 mEsmK; /// K parameter for ESM.
uint32 mTexUnitSlotStart;
Expand Down
7 changes: 5 additions & 2 deletions Components/Hlms/Unlit/src/OgreHlmsUnlit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace Ogre
mHasSeparateSamplers( 0 ),
mLastDescTexture( 0 ),
mLastDescSampler( 0 ),
mConstantBiasScale( 1.0f ),
mUsingExponentialShadowMaps( false ),
mEsmK( 600u ),
mTexUnitSlotStart( 2u ),
Expand All @@ -99,6 +100,7 @@ namespace Ogre
mLastBoundPool( 0 ),
mLastDescTexture( 0 ),
mLastDescSampler( 0 ),
mConstantBiasScale( 1.0f ),
mUsingExponentialShadowMaps( false ),
mEsmK( 600u ),
mTexUnitSlotStart( 2u ),
Expand Down Expand Up @@ -601,6 +603,7 @@ namespace Ogre
retVal.setProperties = mSetProperties;
retVal.pso.pass = passCache.passPso;

mConstantBiasScale = cameras.renderingCamera->_getConstantBiasScale();
Matrix4 viewMatrix = cameras.renderingCamera->getViewMatrix(true);

Matrix4 projectionMatrix = cameras.renderingCamera->getProjectionMatrixWithRSDepth();
Expand Down Expand Up @@ -888,8 +891,8 @@ namespace Ogre

//uint materialIdx[]
*currentMappedConstBuffer = datablock->getAssignedSlot();
*reinterpret_cast<float * RESTRICT_ALIAS>( currentMappedConstBuffer+1 ) = datablock->
mShadowConstantBias;
*reinterpret_cast<float * RESTRICT_ALIAS>( currentMappedConstBuffer + 1 ) =
datablock->mShadowConstantBias * mConstantBiasScale;
*(currentMappedConstBuffer+2) = useIdentityProjection;
currentMappedConstBuffer += 4;

Expand Down
16 changes: 16 additions & 0 deletions Docs/src/manual/compositor.md
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,22 @@ thus the recommended values are num\_stable\_splits = 1 or num\_stable\_splits =

The default is num\_stable\_splits = 0 which disables the feature

- normal\_offset\_bias <value>

Normal-offset bias is per cascade / shadow map to fight shadow acne and self shadowing artifacts
Very large values can cause misalignments between the objects and their shadows (if they're touching)

Default is 0.00004

- constant\_bias\_scale <value>

Constant bias is per material (tweak HlmsDatablock::mShadowConstantBias).
This value lets you multiply it 'mShadowConstantBias * constantBiasScale' per cascade / shadow map

Large values can cause peter-panning.

Default is 1.0

- pssm\_lambda \<lambda\>

Only used by PSSM techniques. Value usually between 0 & 1. The default
Expand Down
5 changes: 5 additions & 0 deletions OgreMain/include/Compositor/OgreCompositorShadowNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ namespace Ogre
/// return a valid pointer.
const Light* getLightAssociatedWith( uint32 shadowMapIdx ) const;

/// Returns 0 if shadowMapIdx is out of bounds
size_t getLightIdxAssociatedWith( const size_t shadowMapIdx ) const;

/** Outputs the min & max depth range for the given camera. 0 & 100000 if camera not found
@remarks
Performs linear search O(N), except the overload that provides a shadowMapIdx
Expand Down Expand Up @@ -280,6 +283,8 @@ namespace Ogre
const TextureGpuVec& getContiguousShadowMapTex(void) const { return mContiguousShadowMapTex; }
uint32 getIndexToContiguousShadowMapTex( size_t shadowMapIdx ) const;

float getNormalOffsetBias( const size_t shadowMapIdx ) const;

/** Marks a shadow map as statically updated, and ties the given light to always use
that shadow map.
@remarks
Expand Down
9 changes: 9 additions & 0 deletions OgreMain/include/Compositor/OgreCompositorShadowNodeDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ namespace Ogre
size_t light; //Render Nth closest light
size_t split; //Split for that light (only for PSSM/CSM)

/// Constant bias is per material (tweak HlmsDatablock::mShadowConstantBias).
/// This value lets you multiply it 'mShadowConstantBias * constantBiasScale'
/// per cascade / shadow map
float constantBiasScale;
/// Normal offset bias is per cascade / shadow map
float normalOffsetBias;

ShadowMapTechniques shadowMapTechnique;

//PSSM params
Expand All @@ -87,6 +94,8 @@ namespace Ogre
arrayIdx( _arrayIdx ),
light( _light ),
split( _split ),
constantBiasScale( 1.0f ),
normalOffsetBias( 0.00004f ),
shadowMapTechnique( t ),
pssmLambda( 0.95f ),
splitPadding( 1.0f ),
Expand Down
5 changes: 5 additions & 0 deletions OgreMain/include/OgreCamera.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ namespace Ogre {
/// @see Camera::getPixelDisplayRatio
Real mPixelDisplayRatio;

float mConstantBiasScale;

/// Each frame it is set to all false. After rendering each RQ, it is set to true
vector<bool>::type mRenderedRqs;

Expand Down Expand Up @@ -721,6 +723,9 @@ namespace Ogre {
*/
Real getPixelDisplayRatio() const { return mPixelDisplayRatio; }

void _setConstantBiasScale( const float bias ) { mConstantBiasScale = bias; }
float _getConstantBiasScale( void ) const { return mConstantBiasScale; }

/** Called at the beginning of each frame to know which RenderQueue IDs have been rendered
@param numRqs
Max number of total possible render queues in this frame
Expand Down
2 changes: 2 additions & 0 deletions OgreMain/include/OgreScriptCompiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,8 @@ namespace Ogre
ID_SHADOW_NODE,
ID_NUM_SPLITS,
ID_NUM_STABLE_SPLITS,
ID_NORMAL_OFFSET_BIAS,
ID_CONSTANT_BIAS_SCALE,
ID_PSSM_SPLIT_PADDING,
ID_PSSM_SPLIT_BLEND,
ID_PSSM_SPLIT_FADE,
Expand Down
15 changes: 15 additions & 0 deletions OgreMain/src/Compositor/OgreCompositorShadowNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,8 @@ namespace Ogre
texCamera->setAutoAspectRatio( false );
}

texCamera->_setConstantBiasScale( itor->constantBiasScale );

if( itor->shadowMapTechnique == SHADOWMAP_PSSM )
{
assert( dynamic_cast<PSSMShadowCameraSetup*>
Expand Down Expand Up @@ -828,6 +830,14 @@ namespace Ogre
return retVal;
}
//-----------------------------------------------------------------------------------
size_t CompositorShadowNode::getLightIdxAssociatedWith( const size_t shadowMapIdx ) const
{
size_t retVal = 0;
if( shadowMapIdx < mDefinition->mShadowMapTexDefinitions.size() )
retVal = mDefinition->mShadowMapTexDefinitions[shadowMapIdx].light;
return retVal;
}
//-----------------------------------------------------------------------------------
void CompositorShadowNode::getMinMaxDepthRange( const Frustum *shadowMapCamera,
Real &outMin, Real &outMax ) const
{
Expand Down Expand Up @@ -950,6 +960,11 @@ namespace Ogre
return mShadowMapCameras[shadowMapIdx].idxToContiguousTex;
}
//-----------------------------------------------------------------------------------
float CompositorShadowNode::getNormalOffsetBias( const size_t shadowMapIdx ) const
{
return mDefinition->mShadowMapTexDefinitions[shadowMapIdx].normalOffsetBias;
}
//-----------------------------------------------------------------------------------
void CompositorShadowNode::setLightFixedToShadowMap( size_t shadowMapIdx, Light *light )
{
assert( shadowMapIdx < mShadowMapCameras.size() );
Expand Down
3 changes: 2 additions & 1 deletion OgreMain/src/OgreCamera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ namespace Ogre {
mUseRenderingDistance(true),
mLodCamera(0),
mUseMinPixelSize(false),
mPixelDisplayRatio(0)
mPixelDisplayRatio(0),
mConstantBiasScale(1.0f)
{

// Reasonable defaults to camera params
Expand Down
12 changes: 12 additions & 0 deletions OgreMain/src/OgreHlms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2627,6 +2627,12 @@ namespace Ogre
setProperty( propName.c_str(),
shadowNode->getIndexToContiguousShadowMapTex( shadowMapTexIdx ) );

propName.resize( basePropSize );
propName.a( "_light_idx" );
setProperty( propName.c_str(),
static_cast<int32>(
shadowNode->getLightIdxAssociatedWith( shadowMapTexIdx ) ) );

if( shadowTexDef->uvOffset != Vector2::ZERO ||
shadowTexDef->uvLength != Vector2::UNIT_SCALE )
{
Expand Down Expand Up @@ -2703,6 +2709,12 @@ namespace Ogre
propName.a( "_uv_length_y_fract" );
setProperty( propName.c_str(), (int32)(fractPart * 100000.0f) );
}
else if( light->getType() == Light::LT_SPOTLIGHT )
{
propName.resize( basePropSize );
propName.a( "_is_spot" );
setProperty( propName.c_str(), 1 );
}

++shadowMapTexIdx;
}
Expand Down
2 changes: 1 addition & 1 deletion OgreMain/src/OgreHlmsDatablock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ namespace Ogre
mAlphaTestCmp( CMPF_ALWAYS_PASS ),
mAlphaTestShadowCasterOnly( false ),
mAlphaTestThreshold( 0.5f ),
mShadowConstantBias( 0.01f )
mShadowConstantBias( 0.001f )
{
mMacroblockHash[0] = mMacroblockHash[1] = 0;
mMacroblock[0] = mMacroblock[1] = 0;
Expand Down
2 changes: 1 addition & 1 deletion OgreMain/src/OgreHlmsManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace Ogre
HlmsManager::HlmsManager() :
mComputeHlms( 0 ),
mRenderSystem( 0 ),
mShadowMappingUseBackFaces( true ),
mShadowMappingUseBackFaces( false ),
mDefaultHlmsType( HLMS_PBS )
#if !OGRE_NO_JSON
, mJsonListener( 0 )
Expand Down
2 changes: 2 additions & 0 deletions OgreMain/src/OgreScriptCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,8 @@ namespace Ogre
mIds["compositor_node_shadow"] = ID_SHADOW_NODE;
mIds["num_splits"] = ID_NUM_SPLITS;
mIds["num_stable_splits"] = ID_NUM_STABLE_SPLITS;
mIds["normal_offset_bias"] = ID_NORMAL_OFFSET_BIAS;
mIds["constant_bias_scale"] = ID_CONSTANT_BIAS_SCALE;
mIds["pssm_split_padding"] = ID_PSSM_SPLIT_PADDING;
mIds["pssm_split_blend"] = ID_PSSM_SPLIT_BLEND;
mIds["pssm_split_fade"] = ID_PSSM_SPLIT_FADE;
Expand Down
46 changes: 45 additions & 1 deletion OgreMain/src/OgreScriptTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7514,7 +7514,51 @@ namespace Ogre{
}
}
break;
case ID_PSSM_SPLIT_PADDING:
case ID_NORMAL_OFFSET_BIAS:
{
if( prop->values.empty() )
{
compiler->addError( ScriptCompiler::CE_NUMBEREXPECTED, prop->file,
prop->line );
}
else if( prop->values.size() != 1 )
{
compiler->addError( ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file,
prop->line );
}

AbstractNodeList::const_iterator it0 = prop->values.begin();
if( !getReal( *it0, &defaultParams.normalOffsetBias ) )
{
compiler->addError( ScriptCompiler::CE_NUMBEREXPECTED, prop->file,
prop->line );
return;
}
}
break;
case ID_CONSTANT_BIAS_SCALE:
{
if( prop->values.empty() )
{
compiler->addError( ScriptCompiler::CE_NUMBEREXPECTED, prop->file,
prop->line );
}
else if( prop->values.size() != 1 )
{
compiler->addError( ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file,
prop->line );
}

AbstractNodeList::const_iterator it0 = prop->values.begin();
if( !getReal( *it0, &defaultParams.constantBiasScale ) )
{
compiler->addError( ScriptCompiler::CE_NUMBEREXPECTED, prop->file,
prop->line );
return;
}
}
break;
case ID_PSSM_SPLIT_PADDING:
{
if(prop->values.empty())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,11 +450,6 @@ namespace Demo
}
#endif

if( nextFilter == Ogre::HlmsPbs::ExponentialShadowMaps )
pbs->getHlmsManager()->setShadowMappingUseBackFaces( false );
else
pbs->getHlmsManager()->setShadowMappingUseBackFaces( true );

pbs->setShadowSettings( nextFilter );

if( nextFilter == Ogre::HlmsPbs::ExponentialShadowMaps )
Expand Down
12 changes: 6 additions & 6 deletions Samples/Media/Hlms/Common/Any/ShadowCaster_piece_vs.any
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@
@property( !hlms_shadow_uses_depth_texture && !hlms_shadowcaster_point && !exponential_shadow_maps )
//Linear depth
@property( hlms_shadowcaster_directional || !hlms_no_reverse_depth )
outVs.depth = outVs_Position.z + shadowConstantBias * passBuf.depthRange.y * passBuf.depthRange.y;
outVs.depth = outVs_Position.z + shadowConstantBias;
@else
outVs.depth = (outVs_Position.z + shadowConstantBias * passBuf.depthRange.y) * passBuf.depthRange.y;
outVs.depth = outVs_Position.z * passBuf.depthRange.y + shadowConstantBias;
@end
@property( hlms_no_reverse_depth && (syntax == glsl || syntax == glsles) )outVs.depth = (outVs.depth * 0.5) + 0.5;@end
@end

@property( hlms_shadowcaster_point )
outVs.toCameraWS = worldPos.xyz - passBuf.cameraPosWS.xyz;
@property( !exponential_shadow_maps )
outVs.constBias = shadowConstantBias * passBuf.depthRange.y * passBuf.depthRange.y;
outVs.constBias = shadowConstantBias;
@end
@end

Expand All @@ -31,16 +31,16 @@
//however we can use a cheap approximation ("pseudo linear depth")
//see http://www.yosoygames.com.ar/wp/2014/01/linear-depth-buffer-my-ass/
@property( hlms_shadowcaster_directional || !hlms_no_reverse_depth )
outVs_Position.z = outVs_Position.z + shadowConstantBias * passBuf.depthRange.y * passBuf.depthRange.y;
outVs_Position.z = outVs_Position.z + shadowConstantBias;
@else
outVs_Position.z = (outVs_Position.z + shadowConstantBias * passBuf.depthRange.y) * passBuf.depthRange.y * outVs_Position.w;
outVs_Position.z = (outVs_Position.z * passBuf.depthRange.y + shadowConstantBias) * outVs_Position.w;
@end
@end

@property( exponential_shadow_maps && !hlms_shadowcaster_point )
//It's the same as (float4( worldPos.xyz, 1 ) * viewMatrix).z
float linearZ = -(dot( worldPos.xyz, passBuf.viewZRow.xyz ) + passBuf.viewZRow.w);
//linearZ += (shadowConstantBias * passBuf.depthRange.y);
//linearZ += shadowConstantBias;
outVs.depth = (linearZ - passBuf.depthRange.x) * passBuf.depthRange.y;
@end
@end
Expand Down
Loading

0 comments on commit f9bd68a

Please sign in to comment.