From David Callu, "Here a fix for pssm.

Problem 1 :
With GLSL, multi pass to apply each shadow map is not required.


Problem 2 :
GLSL code use "shadow2DProj" build-in function to look up in shadow texture.
Projection is orthogonal so "shadow2D" build-in function is sufficient.

Problem 3:
Bad calcul in
osgShadow::ParallelSplitShadowMap::calculateLightViewProjectionFormFrustum(..)
provide some visual error in specific configuration.


to reproduce pssm_bug.jpg, you need to add a light direction in osgshadow.cpp example (done in joint osgshadow.cpp file)
then "osgshadow --noUpdate --pssm --maxFarDist 500 --minNearSplit 500 --mapcount 6 --debug-color  model_test.3ds"


As you can see in pssm_bug.jpg and pssm_fix.jpg, performance is really better when Problem 1 is fixed.
"
This commit is contained in:
Robert Osfield 2010-11-23 11:11:58 +00:00
parent 04b6a0888b
commit 5b3691e602

View File

@ -1,33 +1,28 @@
/* OpenSceneGraph example, osgshadow.
*
* 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 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.
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
/* ##################################################################################################### */
/* ParallelSplitShadowMap written by Adrian Egli (3dhelp (at) gmail.com) */
/* ParallelSplitShadowMap written by Adrian Egli (3dhelp (at) gmail.com) */
/* ##################################################################################################### */
/* */
/* the pssm main idea is based on: */
/* */
/* Parallel-Split Shadow Maps for Large-scale Virtual Environments */
/* Fan Zhang Hanqiu Sun Leilei Xu Lee Kit Lun */
/* The Chinese University of Hong Kong */
/* */
/* */
/* the pssm main idea is based on: */
/* */
/* Parallel-Split Shadow Maps for Large-scale Virtual Environments */
/* Fan Zhang Hanqiu Sun Leilei Xu Lee Kit Lun */
/* The Chinese University of Hong Kong */
/* */
/* Refer to our latest project webpage for "Parallel-Split Shadow Maps on Programmable GPUs" in GPU Gems */
/* */
/* */
/* ##################################################################################################### */
#include <osgShadow/ParallelSplitShadowMap>
@ -108,7 +103,7 @@ std::string ParallelSplitShadowMap::FragmentShaderGenerator::generateGLSL_Fragme
}
for (unsigned int i=0;i<nbrSplits;i++) {
if (!filtered) {
sstr << " float shadow" << i <<" = shadow2DProj( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"]).r;" << std::endl;
sstr << " float shadow" << i <<" = shadow2D( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"].xyz).r;" << std::endl;
sstr << " shadow" << i <<" = step(0.25,shadow" << i <<");" << std::endl; // reduce shadow artefacts
} else {
@ -121,11 +116,11 @@ std::string ParallelSplitShadowMap::FragmentShaderGenerator::generateGLSL_Fragme
//
// / 6
sstr << " float shadowOrg" << i <<" = shadow2DProj( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"]+vec4(0.0,0.0,fZOffSet,0.0) ).r;" << std::endl;
sstr << " float shadow0" << i <<" = shadow2DProj( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"]+vec4(-fTexelSize,-fTexelSize,fZOffSet,0.0) ).r;" << std::endl;
sstr << " float shadow1" << i <<" = shadow2DProj( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"]+vec4( fTexelSize,-fTexelSize,fZOffSet,0.0) ).r;" << std::endl;
sstr << " float shadow2" << i <<" = shadow2DProj( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"]+vec4( fTexelSize, fTexelSize,fZOffSet,0.0) ).r;" << std::endl;
sstr << " float shadow3" << i <<" = shadow2DProj( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"]+vec4(-fTexelSize, fTexelSize,fZOffSet,0.0) ).r;" << std::endl;
sstr << " float shadowOrg" << i <<" = shadow2D( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"].xyz+vec3(0.0,0.0,fZOffSet) ).r;" << std::endl;
sstr << " float shadow0" << i <<" = shadow2D( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"].xyz+vec3(-fTexelSize,-fTexelSize,fZOffSet) ).r;" << std::endl;
sstr << " float shadow1" << i <<" = shadow2D( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"].xyz+vec3( fTexelSize,-fTexelSize,fZOffSet) ).r;" << std::endl;
sstr << " float shadow2" << i <<" = shadow2D( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"].xyz+vec3( fTexelSize, fTexelSize,fZOffSet) ).r;" << std::endl;
sstr << " float shadow3" << i <<" = shadow2D( shadowTexture" << i <<",gl_TexCoord[" << (i+textureOffset) <<"].xyz+vec3(-fTexelSize, fTexelSize,fZOffSet) ).r;" << std::endl;
sstr << " float shadow" << i <<" = ( 2.0*shadowOrg" << i
<<" + shadow0" << i
@ -285,7 +280,7 @@ void ParallelSplitShadowMap::init(){
if (!_shadowedScene) return;
osg::StateSet* sharedStateSet = new osg::StateSet;
sharedStateSet->setDataVariance(osg::Object::DYNAMIC);
unsigned int iCamerasMax=_number_of_splits;
for (unsigned int iCameras=0;iCameras<iCamerasMax;iCameras++)
@ -564,8 +559,12 @@ void ParallelSplitShadowMap::cull(osgUtil::CullVisitor& cv){
unsigned int traversalMask = cv.getTraversalMask();
osgUtil::RenderStage* orig_rs = cv.getRenderStage();
#ifdef SHADOW_TEXTURE_GLSL
PSSMShadowSplitTextureMap::iterator it=_PSSMShadowSplitTextureMap.begin();
#else
// do traversal of shadow receiving scene which does need to be decorated by the shadow map
for (PSSMShadowSplitTextureMap::iterator it=_PSSMShadowSplitTextureMap.begin();it!=_PSSMShadowSplitTextureMap.end();it++)
#endif
{
PSSMShadowSplitTexture pssmShadowSplitTexture = it->second;
cv.pushStateSet(pssmShadowSplitTexture._stateset.get());
@ -733,125 +732,116 @@ void ParallelSplitShadowMap::cleanSceneGraph(){
//
//
//unit box representing frustum in clip space
const osg::Vec3f const_pointFarTR(1.0f, 1.0f, 1.0f);
const osg::Vec3f const_pointFarBR(1.0f, -1.0f, 1.0f);
const osg::Vec3f const_pointFarTL(-1.0f, 1.0f, 1.0f);
const osg::Vec3f const_pointFarBL(-1.0f, -1.0f, 1.0f);
const osg::Vec3f const_pointNearTR(1.0f, 1.0f, -1.0f);
const osg::Vec3f const_pointNearBR(1.0f, -1.0f, -1.0f);
const osg::Vec3f const_pointNearTL(-1.0f, 1.0f, -1.0f);
const osg::Vec3f const_pointNearBL(-1.0f, -1.0f, -1.0f);
const osg::Vec3d const_pointFarTR( 1.0, 1.0, 1.0);
const osg::Vec3d const_pointFarBR( 1.0, -1.0, 1.0);
const osg::Vec3d const_pointFarTL( -1.0, 1.0, 1.0);
const osg::Vec3d const_pointFarBL( -1.0, -1.0, 1.0);
const osg::Vec3d const_pointNearTR( 1.0, 1.0, -1.0);
const osg::Vec3d const_pointNearBR( 1.0, -1.0, -1.0);
const osg::Vec3d const_pointNearTL( -1.0, 1.0, -1.0);
const osg::Vec3d const_pointNearBL( -1.0, -1.0, -1.0);
//////////////////////////////////////////////////////////////////////////
void ParallelSplitShadowMap::calculateFrustumCorners(
PSSMShadowSplitTexture &pssmShadowSplitTexture,
osg::Vec3d *frustumCorners
) {
// get user cameras
double fovy,aspectRatio,camNear,camFar;
pssmShadowSplitTexture._cameraProj.getPerspective(fovy,aspectRatio,camNear,camFar);
void ParallelSplitShadowMap::calculateFrustumCorners(PSSMShadowSplitTexture &pssmShadowSplitTexture, osg::Vec3d *frustumCorners)
{
// get user cameras
double fovy,aspectRatio,camNear,camFar;
pssmShadowSplitTexture._cameraProj.getPerspective(fovy,aspectRatio,camNear,camFar);
// force to max far distance to show shadow, for some scene it can be solve performance problems.
if ( _isSetMaxFarDistance ) {
if (_setMaxFarDistance < camFar) camFar = _setMaxFarDistance;
// force to max far distance to show shadow, for some scene it can be solve performance problems.
if ((_isSetMaxFarDistance) && (_setMaxFarDistance < camFar))
camFar = _setMaxFarDistance;
// build camera matrix with some offsets (the user view camera)
osg::Matrixd viewMat;
osg::Vec3d camEye,camCenter,camUp;
pssmShadowSplitTexture._cameraView.getLookAt(camEye,camCenter,camUp);
osg::Vec3d viewDir = camCenter - camEye;
//viewDir.normalize(); //we can assume that viewDir is still normalized in the viewMatrix
camEye = camEye - viewDir * _move_vcam_behind_rcam_factor;
camFar += _move_vcam_behind_rcam_factor * viewDir.length();
viewMat.makeLookAt(camEye,camCenter,camUp);
//////////////////////////////////////////////////////////////////////////
/// CALCULATE SPLIT
double maxFar = camFar;
// double minNear = camNear;
double camNearFar_Dist = maxFar - camNear;
if ( _SplitCalcMode == SPLIT_LINEAR )
{
camFar = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID+1))/((double)(_number_of_splits));
camNear = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID))/((double)(_number_of_splits));
}
else
{
// Exponential split scheme:
//
// Ci = (n - f)*(i/numsplits)^(bias+1) + n;
//
static double fSplitSchemeBias[2]={0.25f,0.66f};
fSplitSchemeBias[1]=Clamp(fSplitSchemeBias[1],0.0,3.0);
double* pSplitDistances =new double[_number_of_splits+1];
for(int i=0;i<(int)_number_of_splits;i++)
{
double fIDM=(double)(i)/(double)(_number_of_splits);
pSplitDistances[i]=camNearFar_Dist*(pow(fIDM,fSplitSchemeBias[1]+1))+camNear;
}
// make sure border values are right
pSplitDistances[0]=camNear;
pSplitDistances[_number_of_splits]=camFar;
camNear = pSplitDistances[pssmShadowSplitTexture._splitID];
camFar = pSplitDistances[pssmShadowSplitTexture._splitID+1];
delete[] pSplitDistances;
}
// build camera matrix with some offsets (the user view camera)
osg::Matrixf viewMat;
osg::Vec3d camEye,camCenter,camUp;
pssmShadowSplitTexture._cameraView.getLookAt(camEye,camCenter,camUp);
osg::Vec3d viewDir = camCenter - camEye;
//viewDir.normalize(); //we can assume that viewDir is still normalized in the viewMatrix
camEye = camEye - viewDir * _move_vcam_behind_rcam_factor;
camFar += _move_vcam_behind_rcam_factor * viewDir.length();
viewMat.makeLookAt(camEye,camCenter,camUp);
pssmShadowSplitTexture._split_far = camFar;
//////////////////////////////////////////////////////////////////////////
/// CALCULATE SPLIT
double maxFar = camFar;
// double minNear = camNear;
double camNearFar_Dist = maxFar - camNear;
if ( _SplitCalcMode == SPLIT_LINEAR ) {
camFar = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID+1))/((double)(_number_of_splits));
camNear = camNear + (camNearFar_Dist) * ((double)(pssmShadowSplitTexture._splitID))/((double)(_number_of_splits));
} else {
// Exponential split scheme:
//
// Ci = (n - f)*(i/numsplits)^(bias+1) + n;
//
static float fSplitSchemeBias[2]={0.25f,0.66f};
fSplitSchemeBias[1]=Clamp(fSplitSchemeBias[1],0.0f,3.0f);
float* pSplitDistances =new float[_number_of_splits+1];
for(int i=0;i<(int)_number_of_splits;i++) {
float fIDM=i/(float)_number_of_splits;
pSplitDistances[i]=camNearFar_Dist*(powf(fIDM,fSplitSchemeBias[1]+1))+camNear;
}
// make sure border values are right
pSplitDistances[0]=camNear;
pSplitDistances[_number_of_splits]=camFar;
camNear = pSplitDistances[pssmShadowSplitTexture._splitID];
camFar = pSplitDistances[pssmShadowSplitTexture._splitID+1];
delete[] pSplitDistances;
}
//////////////////////////////////////////////////////////////////////////
/// TRANSFORM frustum corners (Optimized for Orthogonal)
pssmShadowSplitTexture._split_far = camFar;
osg::Matrixd projMat;
projMat.makePerspective(fovy,aspectRatio,camNear,camFar);
osg::Matrixd projViewMat(viewMat*projMat);
osg::Matrixd invProjViewMat;
invProjViewMat.invert(projViewMat);
//transform frustum vertices to world space
frustumCorners[0] = const_pointFarBR * invProjViewMat;
frustumCorners[1] = const_pointNearBR* invProjViewMat;
frustumCorners[2] = const_pointNearTR* invProjViewMat;
frustumCorners[3] = const_pointFarTR * invProjViewMat;
frustumCorners[4] = const_pointFarTL * invProjViewMat;
frustumCorners[5] = const_pointFarBL * invProjViewMat;
frustumCorners[6] = const_pointNearBL* invProjViewMat;
frustumCorners[7] = const_pointNearTL* invProjViewMat;
//////////////////////////////////////////////////////////////////////////
/// TRANSFORM frustum corners (Optimized for Orthogonal)
osg::Matrixf projMat;
projMat.makePerspective(fovy,aspectRatio,camNear,camFar);
osg::Matrixf projViewMat(viewMat*projMat);
osg::Matrixf invProjViewMat;
invProjViewMat.invert(projViewMat);
//transform frustum vertices to world space
frustumCorners[0] = const_pointFarBR * invProjViewMat;
frustumCorners[1] = const_pointNearBR* invProjViewMat;
frustumCorners[2] = const_pointNearTR* invProjViewMat;
frustumCorners[3] = const_pointFarTR * invProjViewMat;
frustumCorners[4] = const_pointFarTL * invProjViewMat;
frustumCorners[5] = const_pointFarBL * invProjViewMat;
frustumCorners[6] = const_pointNearBL* invProjViewMat;
frustumCorners[7] = const_pointNearTL* invProjViewMat;
//std::cout << "camFar : "<<pssmShadowSplitTexture._splitID << " / " << camNear << "," << camFar << std::endl;
//std::cout << "camFar : "<<pssmShadowSplitTexture._splitID << " / " << camNear << "," << camFar << std::endl;
}
//////////////////////////////////////////////////////////////////////////
//
// compute directional light initial position;
void ParallelSplitShadowMap::calculateLightInitialPosition(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners){
void ParallelSplitShadowMap::calculateLightInitialPosition(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners)
{
pssmShadowSplitTexture._frustumSplitCenter = frustumCorners[0];
for(int i=1;i<8;i++) {
for(int i=1;i<8;i++)
{
pssmShadowSplitTexture._frustumSplitCenter +=frustumCorners[i];
}
pssmShadowSplitTexture._frustumSplitCenter /= 8.0;
//
// To avoid edge problems, scale the frustum so
// that it's at least a few pixels larger
//
for(int i=0;i<8;i++)
{
// scale by adding offset from center
frustumCorners[i]+=(frustumCorners[i]-pssmShadowSplitTexture._frustumSplitCenter)*(0.75);
}
// pssmShadowSplitTexture._frustumSplitCenter /= 8.0;
pssmShadowSplitTexture._frustumSplitCenter *= 0.125;
}
void ParallelSplitShadowMap::calculateLightNearFarFormFrustum(
@ -888,15 +878,14 @@ void ParallelSplitShadowMap::calculateLightNearFarFormFrustum(
void ParallelSplitShadowMap::calculateLightViewProjectionFormFrustum(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners) {
void ParallelSplitShadowMap::calculateLightViewProjectionFormFrustum(PSSMShadowSplitTexture &pssmShadowSplitTexture,osg::Vec3d *frustumCorners)
{
// calculate the camera's coordinate system
osg::Vec3d camEye,camCenter,camUp;
pssmShadowSplitTexture._cameraView.getLookAt(camEye,camCenter,camUp);
osg::Vec3d viewDir(camCenter-camEye);
osg::Vec3d camRight(camUp^viewDir);
osg::Vec3d camRight(viewDir^camUp);
// we force to have normalized vectors (camera's view)
camUp.normalize();
@ -916,7 +905,7 @@ void ParallelSplitShadowMap::calculateLightViewProjectionFormFrustum(PSSMShadowS
for(int i(0); i < 8; i++)
{
osg::Vec3d diffCorner(pssmShadowSplitTexture._lightCameraSource - frustumCorners[i]);
osg::Vec3d diffCorner(frustumCorners[i] - pssmShadowSplitTexture._frustumSplitCenter);
double lright(diffCorner*right);
double lTop(diffCorner*top);