844 lines
29 KiB
C++
844 lines
29 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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.
|
|
*
|
|
* ViewDependentShadow codes Copyright (C) 2008 Wojciech Lewandowski
|
|
* Thanks to to my company http://www.ai.com.pl for allowing me free this work.
|
|
*/
|
|
|
|
|
|
#include <osgShadow/LightSpacePerspectiveShadowMap>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
|
|
#define DIRECTIONAL_ONLY 0
|
|
#define DIRECTIONAL_ADAPTED 1
|
|
#define DIRECTIONAL_AND_SPOT 2
|
|
|
|
//#define LISPSM_ALGO DIRECTIONAL_ONLY
|
|
#define LISPSM_ALGO DIRECTIONAL_ADAPTED
|
|
//#define LISPSM_ALGO DIRECTIONAL_AND_SPOT
|
|
|
|
#define PRINT_COMPUTED_N_OPT 0
|
|
|
|
using namespace osgShadow;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// There are two slightly differing implemetations available on
|
|
// "Light Space Perspective Shadow Maps" page. One from 2004 and other from 2006.
|
|
// Our implementation is written in two versions based on these solutions.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Original LisPSM authors 2004 implementation excerpt. Kept here for reference.
|
|
// DIRECTIONAL AND DIRECTIONAL_ADAPTED versions are based on this code.
|
|
// DIRECTIONAL_AND_SPOT version is based on later 2006 code.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#if 0
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// This code is copyright Vienna University of Technology, 2004.
|
|
//
|
|
// Please feel FREE to COPY and USE the code to include it in your own work,
|
|
// provided you include this copyright notice.
|
|
// This program 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.
|
|
//
|
|
// Authors Code:
|
|
// Daniel Scherzer (scherzer@cg.tuwien.ac.at)
|
|
//
|
|
// Authors Paper:
|
|
// Michael Wimmer (wimmer@cg.tuwien.ac.at)
|
|
// Daniel Scherzer (scherzer@cg.tuwien.ac.at)
|
|
// Werner Purgathofer
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void calcLispSMMtx(struct VecPoint* B) {
|
|
Vector3 min, max;
|
|
Vector3 up;
|
|
Matrix4x4 lispMtx;
|
|
struct VecPoint Bcopy = VECPOINT_NULL;
|
|
double dotProd = dot(viewDir,lightDir);
|
|
double sinGamma;
|
|
|
|
sinGamma = sqrt(1.0-dotProd*dotProd);
|
|
|
|
copyMatrix(lispMtx,IDENTITY);
|
|
|
|
copyVecPoint(&Bcopy,*B);
|
|
|
|
//CHANGED
|
|
if(useBodyVec) {
|
|
Vector3 newDir;
|
|
calcNewDir(newDir,B);
|
|
calcUpVec(up,newDir,lightDir);
|
|
}
|
|
else {
|
|
calcUpVec(up,viewDir,lightDir);
|
|
}
|
|
|
|
//temporal light View
|
|
//look from position(eyePos)
|
|
//into direction(lightDir)
|
|
//with up vector(up)
|
|
look(lightView,eyePos,lightDir,up);
|
|
|
|
//transform the light volume points from world into light space
|
|
transformVecPoint(B,lightView);
|
|
|
|
//calculate the cubic hull (an AABB)
|
|
//of the light space extents of the intersection body B
|
|
//and save the two extreme points min and max
|
|
calcCubicHull(min,max,B->points,B->size);
|
|
|
|
{
|
|
//use the formulas of the paper to get n (and f)
|
|
const double factor = 1.0/sinGamma;
|
|
const double z_n = factor*nearDist; //often 1
|
|
const double d = absDouble(max[1]-min[1]); //perspective transform depth //light space y extents
|
|
const double z_f = z_n + d*sinGamma;
|
|
const double n = (z_n+sqrt(z_f*z_n))/sinGamma;
|
|
const double f = n+d;
|
|
Vector3 pos;
|
|
|
|
//new observer point n-1 behind eye position
|
|
//pos = eyePos-up*(n-nearDist)
|
|
linCombVector3(pos,eyePos,up,-(n-nearDist));
|
|
|
|
look(lightView,pos,lightDir,up);
|
|
|
|
//one possibility for a simple perspective transformation matrix
|
|
//with the two parameters n(near) and f(far) in y direction
|
|
copyMatrix(lispMtx,IDENTITY); // a = (f+n)/(f-n); b = -2*f*n/(f-n);
|
|
lispMtx[ 5] = (f+n)/(f-n); // [ 1 0 0 0]
|
|
lispMtx[13] = -2*f*n/(f-n); // [ 0 a 0 b]
|
|
lispMtx[ 7] = 1; // [ 0 0 1 0]
|
|
lispMtx[15] = 0; // [ 0 1 0 0]
|
|
|
|
//temporal arrangement for the transformation of the points to post-perspective space
|
|
mult(lightProjection,lispMtx,lightView); // ligthProjection = lispMtx*lightView
|
|
|
|
//transform the light volume points from world into the distorted light space
|
|
transformVecPoint(&Bcopy,lightProjection);
|
|
|
|
//calculate the cubic hull (an AABB)
|
|
//of the light space extents of the intersection body B
|
|
//and save the two extreme points min and max
|
|
calcCubicHull(min,max,Bcopy.points,Bcopy.size);
|
|
}
|
|
|
|
//refit to unit cube
|
|
//this operation calculates a scale translate matrix that
|
|
//maps the two extreme points min and max into (-1,-1,-1) and (1,1,1)
|
|
scaleTranslateToFit(lightProjection,min,max);
|
|
|
|
//together
|
|
mult(lightProjection,lightProjection,lispMtx); // ligthProjection = scaleTranslate*lispMtx
|
|
}
|
|
#endif
|
|
|
|
#if ( LISPSM_ALGO == DIRECTIONAL_ONLY )
|
|
|
|
|
|
LightSpacePerspectiveShadowMapAlgorithm::LightSpacePerspectiveShadowMapAlgorithm()
|
|
{
|
|
lispsm = NULL;
|
|
}
|
|
|
|
LightSpacePerspectiveShadowMapAlgorithm::~LightSpacePerspectiveShadowMapAlgorithm()
|
|
{
|
|
}
|
|
|
|
void LightSpacePerspectiveShadowMapAlgorithm::operator()
|
|
( const ViewDependentShadow::ConvexPolyhedron* hullShadowedView,
|
|
const osg::Camera* cameraMain,
|
|
osg::Camera* cameraShadow ) const
|
|
{
|
|
osg::BoundingBox bb = hullShadowedView->computeBoundingBox( cameraMain->getViewMatrix() );
|
|
double nearDist = -bb._max[2];
|
|
|
|
const osg::Matrix & eyeViewToWorld = cameraMain->getInverseViewMatrix();
|
|
|
|
osg::Matrix lightViewToWorld = cameraShadow->getInverseViewMatrix();
|
|
|
|
osg::Vec3 eyePos = osg::Vec3( 0, 0, 0 ) * eyeViewToWorld;
|
|
|
|
osg::Vec3 viewDir( osg::Matrix::transform3x3( osg::Vec3(0,0,-1), eyeViewToWorld ) );
|
|
|
|
osg::Vec3 lightDir( osg::Matrix::transform3x3( osg::Vec3( 0,0,-1), lightViewToWorld ) );
|
|
osg::Vec3 up( osg::Matrix::transform3x3( osg::Vec3(0,1,0), lightViewToWorld ) );
|
|
|
|
osg::Matrix lightView; // compute coarse light view matrix
|
|
lightView.makeLookAt( eyePos, eyePos + lightDir, up );
|
|
bb = hullShadowedView->computeBoundingBox( lightView );
|
|
|
|
const double dotProd = viewDir * lightDir;
|
|
const double sinGamma = sqrt(1.0- dotProd*dotProd);
|
|
const double factor = 1.0/sinGamma;
|
|
const double z_n = factor*nearDist; //often 1
|
|
//use the formulas of the paper to get n (and f)
|
|
const double d = fabs( bb._max[1]-bb._min[1]); //perspective transform depth //light space y extents
|
|
const double z_f = z_n + d*sinGamma;
|
|
const double n = (z_n+sqrt(z_f*z_n))/sinGamma;
|
|
const double f = n+d;
|
|
osg::Vec3d pos = eyePos-up*(n-nearDist);
|
|
|
|
#if PRINT_COMPUTED_N_OPT
|
|
std::cout
|
|
<< " N=" << std::setw(8) << n
|
|
<< " n=" << std::setw(8) << z_n
|
|
<< " f=" << std::setw(8) << z_f
|
|
<< "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
|
|
<< std::flush;
|
|
#endif
|
|
|
|
lightView.makeLookAt( pos, pos + lightDir, up );
|
|
|
|
//one possibility for a simple perspective transformation matrix
|
|
//with the two parameters n(near) and f(far) in y direction
|
|
double a = (f+n)/(f-n);
|
|
double b = -2*f*n/(f-n);
|
|
|
|
osg::Matrix lispProjection( 1, 0, 0, 0,
|
|
0, a, 0, 1,
|
|
0, 0,-1, 0,
|
|
0, b, 0, 0 );
|
|
|
|
// lispProjection.makeIdentity( );
|
|
#if 0
|
|
{
|
|
osg::Matrix mvp = _camera->getViewMatrix() *
|
|
_camera->getProjectionMatrix();
|
|
|
|
extendScenePolytope( mvp, osg::Matrix::inverse( mvp ) );
|
|
}
|
|
#endif
|
|
|
|
bb = hullShadowedView->computeBoundingBox( lightView * lispProjection );
|
|
|
|
osg::Matrix fitToUnitFrustum;
|
|
fitToUnitFrustum.makeOrtho( bb._min[0], bb._max[0],
|
|
bb._min[1], bb._max[1],
|
|
-(bb._min[2]-1), -bb._max[2] );
|
|
|
|
cameraShadow->setProjectionMatrix
|
|
( lightViewToWorld * lightView * lispProjection * fitToUnitFrustum );
|
|
|
|
|
|
#if 0 // DOUBLE CHECK!
|
|
bb = computeScenePolytopeBounds
|
|
( cameraShadow->getViewMatrix() * cameraShadow->getProjectionMatrix() );
|
|
|
|
if( !osg::equivalent( 0.f, (bb._min - osg::Vec3(-1,-1,-1)).length2() ) ||
|
|
!osg::equivalent( 0.f, (bb._max - osg::Vec3( 1, 1, 1)).length2() ) )
|
|
{
|
|
bb = computeScenePolytopeBounds
|
|
( cameraShadow->getViewMatrix() * cameraShadow->getProjectionMatrix() );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ( LISPSM_ALGO == DIRECTIONAL_ADAPTED )
|
|
|
|
LightSpacePerspectiveShadowMapAlgorithm::LightSpacePerspectiveShadowMapAlgorithm()
|
|
{
|
|
lispsm = NULL;
|
|
}
|
|
|
|
LightSpacePerspectiveShadowMapAlgorithm::~LightSpacePerspectiveShadowMapAlgorithm()
|
|
{
|
|
}
|
|
|
|
void LightSpacePerspectiveShadowMapAlgorithm::operator()
|
|
( const osgShadow::ConvexPolyhedron* hullShadowedView,
|
|
const osg::Camera* cameraMain,
|
|
osg::Camera* cameraShadow ) const
|
|
{
|
|
|
|
// all computations are done in post projection light space
|
|
// which means we are in left handed coordinate system
|
|
osg::Matrix mvpLight =
|
|
cameraShadow->getViewMatrix() * cameraShadow->getProjectionMatrix();
|
|
|
|
osg::Matrix m = cameraMain->getInverseViewMatrix() * mvpLight;
|
|
osg::Vec3 eye = osg::Vec3( 0, 0, 0 ) * m;
|
|
osg::Vec3 center = osg::Vec3( 0, 0, -1 ) * m;
|
|
osg::Vec3 up(0,1,0);
|
|
osg::Vec3 viewDir( center - eye );
|
|
viewDir.normalize();
|
|
|
|
m.makeLookAt( eye, center, up );
|
|
|
|
osg::BoundingBox bb = hullShadowedView->computeBoundingBox( mvpLight * m );
|
|
if( !bb.valid() )
|
|
return;
|
|
|
|
double nearDist = -bb._max[2];
|
|
|
|
#if 1
|
|
// Original LiSPSM Paper suggests that algorithm should work for all light types:
|
|
// infinte directional, omnidirectional and spot types may be treated as directional
|
|
// as all computations are performed in post projection light space.
|
|
// Frankly, I have my doubts if their error analysis and methodology still works
|
|
// in non directional lights post projective space. But since I can't prove it doesn't,
|
|
// I assume it does ;-). So I made an effort to modify their original directional algo
|
|
// to work in true light post perspective space and compute all params in this space.
|
|
// And here is a snag. Although shadowed hull fits completely into light space,
|
|
// camera position may not, and after projective transform it may land outside
|
|
// light frustum and even on/or below infinity plane. I need camera pos to compute
|
|
// minimal distance to shadowed hull. If its not right rest of the computation may
|
|
// be completely off. So in the end this approach is not singulartity free.
|
|
// I guess this problem is solvable in other way but "this other
|
|
// way" looks like a topic for other scientific paper and I am definitely not that
|
|
// ambitious ;-). So for the time being I simply try to discover when this happens and
|
|
// apply workaround, I found works. This workaround may mean that adjusted projection
|
|
// may not be optimal in original LisPSM Lmax norm sense. But as I wrote above,
|
|
// I doubt they are optimal when Light is not directional, anyway.
|
|
|
|
// Seems that most nasty case when algorithm fails is when cam pos is
|
|
// below light frustum near plane but above infinity plane - when this occurs
|
|
// shadows simply disappear. My workaround is to find this case by
|
|
// checking light postperspective transform camera z ( negative value means this )
|
|
// and make sure min distance to shadow hull is clamped to positive value.
|
|
|
|
if( eye[2] < 0 && nearDist <= 0 ) {
|
|
float clampedNearDist = 0.0001;
|
|
eye += viewDir * ( clampedNearDist - nearDist );
|
|
nearDist = clampedNearDist;
|
|
}
|
|
#endif
|
|
|
|
// Beware!!! Dirty Tricks:
|
|
// Light direction in light post proj space is actually (0,0,1)
|
|
// But since we want to pass it to std OpenGL right handed coordinate
|
|
// makeLookAt function we compensate the effects by also using right
|
|
// handed view forward vector (ie 0,0,-1) instead.
|
|
// So in the end we get left handed makeLookAt behaviour (D3D like)...
|
|
// I agree this method is bizarre. But it works so I left it as is.
|
|
// It sort of came out by itself through trial and error.
|
|
// I later understoood why it works.
|
|
|
|
osg::Vec3 lightDir(0,0,-1);
|
|
osg::Matrix lightView; // compute coarse light view matrix
|
|
lightView.makeLookAt( eye, eye + lightDir, up );
|
|
bb = hullShadowedView->computeBoundingBox( mvpLight * lightView );
|
|
if( !bb.valid() )
|
|
return;
|
|
|
|
//use the formulas from the LiSPSM paper to get n (and f)
|
|
const double dotProd = viewDir * lightDir;
|
|
const double sinGamma = sqrt(1.0- dotProd*dotProd);
|
|
const double factor = 1.0/sinGamma;
|
|
const double z_n = factor*nearDist;
|
|
//perspective transform depth light space y extents
|
|
const double d = fabs( bb._max[1]-bb._min[1]);
|
|
const double z_f = z_n + d*sinGamma;
|
|
const double n = (z_n+sqrt(z_f*z_n))/sinGamma;
|
|
const double f = n+d;
|
|
osg::Vec3d pos = eye-up*(n-nearDist);
|
|
lightView.makeLookAt( pos, pos + lightDir, up );
|
|
|
|
//one possibility for a simple perspective transformation matrix
|
|
//with the two parameters n(near) and f(far) in y direction
|
|
double a = (f+n)/(f-n);
|
|
double b = -2*f*n/(f-n);
|
|
|
|
osg::Matrix lispProjection( 1, 0, 0, 0,
|
|
0, a, 0, 1,
|
|
0, 0, 1, 0,
|
|
0, b, 0, 0 );
|
|
|
|
cameraShadow->setProjectionMatrix
|
|
( cameraShadow->getProjectionMatrix() * lightView * lispProjection );
|
|
}
|
|
|
|
#endif
|
|
|
|
#if ( LISPSM_ALGO == DIRECTIONAL_AND_SPOT )
|
|
|
|
|
|
// Adapted Modified version of LispSM authors implementation from 2006
|
|
// Nopt formula differs from the paper. I adopted original authors class to
|
|
// use with OSG
|
|
|
|
|
|
|
|
//we search the point in the LVS volume that is nearest to the camera
|
|
|
|
static const float INFINITY = FLT_MAX;
|
|
|
|
namespace osgShadow {
|
|
|
|
class LispSM {
|
|
public:
|
|
typedef std::vector<osg::Vec3d> Vertices;
|
|
|
|
void setProjectionMatrix( const osg::Matrix & projectionMatrix )
|
|
{ _projectionMatrix = projectionMatrix; }
|
|
|
|
void setViewMatrix( const osg::Matrix & viewMatrix )
|
|
{ _viewMatrix = viewMatrix; }
|
|
|
|
void setHull( const ConvexPolyhedron & hull )
|
|
{ _hull = hull; }
|
|
|
|
const ConvexPolyhedron & getHull( ) const
|
|
{ return _hull; }
|
|
|
|
const osg::Matrix & getProjectionMatrix( void ) const
|
|
{ return _projectionMatrix; }
|
|
|
|
const osg::Matrix & getViewMatrix( void ) const
|
|
{ return _viewMatrix; }
|
|
|
|
bool getUseLiSPSM() const
|
|
{ return _useLiSPSM; }
|
|
|
|
void setUseLiSPSM( bool use )
|
|
{ _useLiSPSM = use; }
|
|
|
|
bool getUseFormula() const
|
|
{ return _useFormula; }
|
|
|
|
void setUseFormula( bool use )
|
|
{ _useFormula = use; }
|
|
|
|
bool getUseOldFormula() const
|
|
{ return _useOldFormula; }
|
|
|
|
void setUseOldFormula( bool use )
|
|
{ _useOldFormula = use; }
|
|
|
|
void setN(const double& n )
|
|
{ _N = n; }
|
|
|
|
const double getN() const
|
|
{ return _N; }
|
|
|
|
//for old LispSM formula from paper
|
|
const double getNearDist() const
|
|
{ return _nearDist; }
|
|
|
|
void setNearDist( const double & nearDist )
|
|
{ _nearDist = nearDist; }
|
|
|
|
const double getFarDist() const
|
|
{ return _farDist; }
|
|
|
|
void setFarDist( const double & farDist )
|
|
{ _farDist = farDist; }
|
|
|
|
const osg::Vec3d & getEyeDir() const
|
|
{ return _eyeDir; }
|
|
|
|
const osg::Vec3d & getLightDir() const
|
|
{ return _lightDir; }
|
|
|
|
void setEyeDir( const osg::Vec3d eyeDir )
|
|
{ _eyeDir = eyeDir; }
|
|
|
|
void setLightDir( const osg::Vec3d lightDir )
|
|
{ _lightDir = lightDir; }
|
|
|
|
protected:
|
|
|
|
bool _useLiSPSM;
|
|
bool _useFormula;
|
|
bool _useOldFormula;
|
|
double _N;
|
|
double _nearDist;
|
|
double _farDist;
|
|
|
|
mutable osg::Vec3d _E;
|
|
osg::Vec3d _eyeDir;
|
|
osg::Vec3d _lightDir;
|
|
|
|
ConvexPolyhedron _hull;
|
|
|
|
osg::Matrix _viewMatrix;
|
|
osg::Matrix _projectionMatrix;
|
|
|
|
double getN(const osg::Matrix lightSpace, const osg::BoundingBox& B_ls) const;
|
|
|
|
osg::Vec3d getNearCameraPointE() const;
|
|
|
|
osg::Vec3d getZ0_ls
|
|
(const osg::Matrix& lightSpace, const osg::Vec3d& e, const double& b_lsZmax, const osg::Vec3d& eyeDir) const;
|
|
|
|
double calcNoptGeneral
|
|
(const osg::Matrix lightSpace, const osg::BoundingBox& B_ls) const;
|
|
|
|
double calcNoptOld
|
|
( const double gamma_ = 999) const;
|
|
|
|
osg::Matrix getLispSmMtx
|
|
(const osg::Matrix& lightSpace) const;
|
|
|
|
osg::Vec3d getProjViewDir_ls
|
|
(const osg::Matrix& lightSpace) const;
|
|
|
|
void updateLightMtx
|
|
(osg::Matrix& lightView, osg::Matrix& lightProj, const std::vector<osg::Vec3d>& B) const;
|
|
|
|
public:
|
|
LispSM( ) : _useLiSPSM( true ), _useFormula( true ), _useOldFormula( false ), _N( 1 ), _nearDist( 1 ), _farDist( 10 ) { }
|
|
|
|
virtual void updateLightMtx( osg::Matrix& lightView, osg::Matrix& lightProj ) const;
|
|
};
|
|
|
|
};
|
|
|
|
osg::Vec3d LispSM::getNearCameraPointE( ) const
|
|
{
|
|
const osg::Matrix& eyeView = getViewMatrix();
|
|
|
|
ConvexPolyhedron::Vertices LVS;
|
|
_hull.getPoints( LVS );
|
|
|
|
//the LVS volume is always in front of the camera
|
|
//the camera points along the neg z axis.
|
|
//-> so the nearest point is the maximum
|
|
|
|
unsigned max = 0;
|
|
for(unsigned i = 0; i < LVS.size(); i++) {
|
|
|
|
LVS[i] = LVS[i] * eyeView;
|
|
|
|
if( LVS[max].z() < LVS[i].z() ) {
|
|
max = i;
|
|
}
|
|
}
|
|
//transform back to world space
|
|
return LVS[max] * osg::Matrix::inverse( eyeView );
|
|
}
|
|
|
|
//z0 is the point that lies on the plane A parallel to the near plane through e
|
|
//and on the near plane of the C frustum (the plane z = bZmax) and on the line x = e.x
|
|
osg::Vec3d LispSM::getZ0_ls
|
|
(const osg::Matrix& lightSpace, const osg::Vec3d& e, const double& b_lsZmax, const osg::Vec3d& eyeDir) const
|
|
{
|
|
//to calculate the parallel plane to the near plane through e we
|
|
//calculate the plane A with the three points
|
|
osg::Plane A(eyeDir,e);
|
|
//to transform plane A into lightSpace
|
|
A.transform( lightSpace );
|
|
//transform to light space
|
|
const osg::Vec3d e_ls = e * lightSpace;
|
|
|
|
//z_0 has the x coordinate of e, the z coord of B_lsZmax
|
|
//and the y coord of the plane A and plane (z==B_lsZmax) intersection
|
|
#if 1
|
|
osg::Vec3d v = osg::Vec3d(e_ls.x(),0,b_lsZmax);
|
|
|
|
// x & z are given. We compute y from equations:
|
|
// A.distance( x,y,z ) == 0
|
|
// A.distance( x,y,z ) == A.distance( x,0,z ) + A.normal.y * y
|
|
// hence A.distance( x,0,z ) == -A.normal.y * y
|
|
|
|
v.y() = -A.distance( v ) / A.getNormal().y();
|
|
#else
|
|
//get the parameters of A from the plane equation n dot d = 0
|
|
const double d = A.asVec4()[3];
|
|
const osg::Vec3d n = A.getNormal();
|
|
osg::Vec3d v(e_ls.x(),(-d-n.z()*b_lsZmax-n.x()*e_ls.x())/n.y(),b_lsZmax);
|
|
#endif
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
double LispSM::calcNoptGeneral(const osg::Matrix lightSpace, const osg::BoundingBox& B_ls) const
|
|
{
|
|
const osg::Matrix& eyeView = getViewMatrix();
|
|
const osg::Matrix invLightSpace = osg::Matrix::inverse( lightSpace );
|
|
|
|
const osg::Vec3d z0_ls = getZ0_ls(lightSpace, _E,B_ls.zMax(),getEyeDir());
|
|
const osg::Vec3d z1_ls = osg::Vec3d(z0_ls.x(),z0_ls.y(),B_ls.zMin());
|
|
|
|
//to world
|
|
const osg::Vec4d z0_ws = osg::Vec4d( z0_ls, 1 ) * invLightSpace;
|
|
const osg::Vec4d z1_ws = osg::Vec4d( z1_ls, 1 ) * invLightSpace;
|
|
|
|
//to eye
|
|
const osg::Vec4d z0_cs = z0_ws * eyeView;
|
|
const osg::Vec4d z1_cs = z1_ws * eyeView;
|
|
|
|
double z0 = -z0_cs.z() / z0_cs.w();
|
|
double z1 = -z1_cs.z() / z1_cs.w();
|
|
|
|
if( z1 / z0 <= 1.0 ) {
|
|
|
|
// solve camera pos singularity in light space problem brutally:
|
|
// if extreme points of B projected to Light space extend beyond
|
|
// camera frustum simply use B extents in camera frustum
|
|
|
|
// Its not optimal selection but ceratainly better than negative N
|
|
osg::BoundingBox bb = _hull.computeBoundingBox( eyeView );
|
|
z0 = -bb.zMax();
|
|
if( z0 <= 0 )
|
|
z0 = 0.1;
|
|
|
|
z1 = -bb.zMin();
|
|
if( z1 <= z0 )
|
|
z1 = z0 + 0.1;
|
|
}
|
|
|
|
const double d = osg::absolute(B_ls.zMax()-B_ls.zMin());
|
|
|
|
double N = d/( sqrt( z1 / z0 ) - 1.0 );
|
|
#if PRINT_COMPUTED_N_OPT
|
|
std::cout
|
|
<< " N=" << std::setw(8) << N
|
|
<< " n=" << std::setw(8) << z0
|
|
<< " f=" << std::setw(8) << z1
|
|
<< "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
|
|
<< std::flush;
|
|
#endif
|
|
return N;
|
|
}
|
|
|
|
double LispSM::calcNoptOld( const double gamma_ ) const
|
|
{
|
|
const double& n = getNearDist();
|
|
const double& f = getFarDist();
|
|
const double d = abs(f-n);
|
|
double sinGamma(0);
|
|
if(999 == gamma_) {
|
|
double dot = getEyeDir() * getLightDir();
|
|
sinGamma = sqrt( 1.0 - dot * dot );
|
|
}
|
|
else {
|
|
sinGamma = sin(gamma_);
|
|
}
|
|
|
|
double N = (n+sqrt(n*(n+d*sinGamma)))/sinGamma;
|
|
#if PRINT_COMPUTED_N_OPT
|
|
std::cout
|
|
<< " N=" << std::setw(8) << N
|
|
<< " n=" << std::setw(8) << n
|
|
<< " f=" << std::setw(8) << f
|
|
<< "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
|
|
<< std::flush;
|
|
#endif
|
|
return N;
|
|
}
|
|
|
|
double LispSM::getN(const osg::Matrix lightSpace, const osg::BoundingBox& B_ls) const
|
|
{
|
|
if( getUseFormula()) {
|
|
if( getUseOldFormula() )
|
|
return calcNoptOld();
|
|
else
|
|
return calcNoptGeneral(lightSpace,B_ls);
|
|
}
|
|
else {
|
|
return getN();
|
|
}
|
|
}
|
|
//this is the algorithm discussed in the article
|
|
osg::Matrix LispSM::getLispSmMtx( const osg::Matrix& lightSpace ) const
|
|
{
|
|
const osg::BoundingBox B_ls = _hull.computeBoundingBox( lightSpace );
|
|
|
|
const double n = getN(lightSpace,B_ls);
|
|
|
|
//get the coordinates of the near camera point in light space
|
|
const osg::Vec3d e_ls = _E * lightSpace;
|
|
//c start has the x and y coordinate of e, the z coord of B.min()
|
|
const osg::Vec3d Cstart_lp(e_ls.x(),e_ls.y(),B_ls.zMax());
|
|
|
|
if( n >= INFINITY ) {
|
|
//if n is inf. than we should do uniform shadow mapping
|
|
return osg::Matrix::identity();
|
|
}
|
|
//calc C the projection center
|
|
//new projection center C, n behind the near plane of P
|
|
//we work along a negative axis so we transform +n*<the positive axis> == -n*<neg axis>
|
|
const osg::Vec3d C( Cstart_lp + osg::Vec3d(0,0,1) * n );
|
|
//construct a translation that moves to the projection center
|
|
const osg::Matrix projectionCenter = osg::Matrix::translate( -C );
|
|
|
|
//calc d the perspective transform depth or light space y extents
|
|
const double d = osg::absolute(B_ls.zMax()-B_ls.zMin());
|
|
|
|
//the lispsm perspective transformation
|
|
|
|
//here done with a standard frustum call that maps P onto the unit cube with
|
|
//corner points [-1,-1,-1] and [1,1,1].
|
|
//in directX you can use the same mapping and do a mapping to the directX post-perspective cube
|
|
//with corner points [-1,-1,0] and [1,1,1] as the final step after all the shadow mapping.
|
|
osg::Matrix P = osg::Matrix::frustum( -1.0,1.0,-1.0,1.0, n, n+d );
|
|
|
|
//invert the transform from right handed into left handed coordinate system for the ndc
|
|
//done by the openGL style frustumGL call
|
|
//so we stay in a right handed system
|
|
P = P * osg::Matrix::scale( 1.0,1.0,-1.0 );
|
|
//return the lispsm frustum with the projection center
|
|
return projectionCenter * P;
|
|
}
|
|
|
|
osg::Vec3d LispSM::getProjViewDir_ls(const osg::Matrix& lightSpace ) const {
|
|
//get the point in the LVS volume that is nearest to the camera
|
|
const osg::Vec3d e = _E;
|
|
//construct edge to transform into light-space
|
|
const osg::Vec3d b = e+getEyeDir();
|
|
//transform to light-space
|
|
osg::Vec4d e_lp = osg::Vec4d( e, 1.0 ) * lightSpace;
|
|
osg::Vec4d b_lp = osg::Vec4d( b, 1.0 ) * lightSpace;
|
|
|
|
if( e_lp[3] <= 0 )
|
|
{
|
|
e_lp[3] = e_lp[3];
|
|
}
|
|
|
|
if( b_lp[3] <= 0 )
|
|
{
|
|
osg::Vec4d v = (e_lp - b_lp)/(e_lp[3]-b_lp[3]);
|
|
|
|
v = ( e_lp + v ) * 0.5;
|
|
|
|
b_lp = v;
|
|
}
|
|
|
|
osg::Vec3d projDir( osg::Vec3( b_lp[0], b_lp[1], b_lp[2] ) / b_lp[3] -
|
|
osg::Vec3( e_lp[0], e_lp[1], e_lp[2] ) / e_lp[3] );
|
|
|
|
projDir.normalize();
|
|
|
|
//project the view direction into the shadow map plane
|
|
projDir.y() = 0.0;
|
|
return projDir;
|
|
}
|
|
|
|
void LispSM::updateLightMtx
|
|
( osg::Matrix& lightView, osg::Matrix& lightProj ) const
|
|
{
|
|
//calculate standard light space for spot or directional lights
|
|
//this routine returns two matrices:
|
|
//lightview contains the rotated translated frame
|
|
//lightproj contains in the case of a spot light the spot light perspective transformation
|
|
//in the case of a directional light a identity matrix
|
|
// calcLightSpace(lightView,lightProj);
|
|
|
|
if( _hull._faces.empty() ) {
|
|
//debug() << "empty intersection body -> completely inside shadow\n";//debug output
|
|
return;
|
|
}
|
|
|
|
_E = getNearCameraPointE();
|
|
|
|
lightProj = lightProj * osg::Matrix::scale( 1, 1, -1 );
|
|
|
|
//coordinate system change for calculations in the article
|
|
osg::Matrix switchToArticle = osg::Matrix::identity();
|
|
switchToArticle(1,1) = 0.0;
|
|
switchToArticle(1,2) =-1.0; // y -> -z
|
|
switchToArticle(2,1) = 1.0; // z -> y
|
|
switchToArticle(2,2) = 0.0;
|
|
//switch to the lightspace used in the article
|
|
lightProj = lightProj * switchToArticle;
|
|
|
|
osg::Matrix L = lightView * lightProj;
|
|
|
|
osg::Vec3d projViewDir = getProjViewDir_ls(L);
|
|
|
|
if( getUseLiSPSM() /* && projViewDir.z() < 0*/ ) {
|
|
//do Light Space Perspective shadow mapping
|
|
//rotate the lightspace so that the proj light view always points upwards
|
|
//calculate a frame matrix that uses the projViewDir[light-space] as up vector
|
|
//look(from position, into the direction of the projected direction, with unchanged up-vector)
|
|
lightProj = lightProj *
|
|
osg::Matrix::lookAt( osg::Vec3d(0,0,0), projViewDir, osg::Vec3d(0,1,0) );
|
|
|
|
osg::Matrix lispsm = getLispSmMtx( lightView * lightProj );
|
|
lightProj = lightProj * lispsm;
|
|
}
|
|
|
|
const osg::Matrix PL = lightView * lightProj;
|
|
|
|
osg::BoundingBox bb = _hull.computeBoundingBox( PL );
|
|
|
|
osg::Matrix fitToUnitFrustum;
|
|
fitToUnitFrustum.makeOrtho( bb._min[0], bb._max[0],
|
|
bb._min[1], bb._max[1],
|
|
-bb._max[2], -bb._min[2] );
|
|
|
|
//map to unit cube
|
|
lightProj = lightProj * fitToUnitFrustum;
|
|
|
|
//coordinate system change for calculations in the article
|
|
osg::Matrix switchToGL = osg::Matrix::identity();
|
|
switchToGL(1,1) = 0.0;
|
|
switchToGL(1,2) = 1.0; // y -> z
|
|
switchToGL(2,1) = -1.0; // z -> -y
|
|
switchToGL(2,2) = 0.0;
|
|
|
|
//back to open gl coordinate system y <-> z
|
|
lightProj = lightProj * switchToGL;
|
|
//transform from right handed system into left handed ndc
|
|
lightProj = lightProj * osg::Matrix::scale(1.0,1.0,-1.0);
|
|
}
|
|
|
|
void LightSpacePerspectiveShadowMapAlgorithm::operator()
|
|
( const ViewDependentShadow::ConvexPolyhedron* hullShadowedView,
|
|
const osg::Camera* cameraMain,
|
|
osg::Camera* cameraShadow ) const
|
|
{
|
|
lispsm->setHull( *hullShadowedView );
|
|
lispsm->setViewMatrix( cameraMain->getViewMatrix() );
|
|
lispsm->setProjectionMatrix( cameraMain->getViewMatrix() );
|
|
|
|
lispsm->setLightDir
|
|
( osg::Matrix::transform3x3( osg::Vec3d( 0, 0, -1 ),
|
|
osg::Matrix::inverse( cameraShadow->getViewMatrix() ) ) );
|
|
|
|
osg::Vec3d eyeDir = osg::Matrix::transform3x3( osg::Vec3d( 0, 0, -1 ),
|
|
osg::Matrix::inverse( cameraMain->getViewMatrix() ) );
|
|
|
|
osg::Matrix &proj = cameraShadow->getProjectionMatrix();
|
|
double l,r,b,t,n,f;
|
|
if( proj.getOrtho( l,r,b,t,n,f ) )
|
|
{
|
|
osg::Vec3d camPosInLightSpace =
|
|
osg::Vec3d( 0, 0, 0 ) *
|
|
osg::Matrix::inverse( cameraMain->getViewMatrix() ) *
|
|
cameraShadow->getViewMatrix() *
|
|
cameraShadow->getProjectionMatrix();
|
|
}
|
|
|
|
eyeDir.normalize();
|
|
|
|
lispsm->setEyeDir( eyeDir );
|
|
|
|
osg::BoundingBox bb =
|
|
hullShadowedView->computeBoundingBox( cameraMain->getViewMatrix() );
|
|
|
|
lispsm->setNearDist( -bb.zMax() );
|
|
lispsm->setFarDist( -bb.zMin() );
|
|
|
|
lispsm->updateLightMtx
|
|
( cameraShadow->getViewMatrix(), cameraShadow->getProjectionMatrix() );
|
|
}
|
|
|
|
LightSpacePerspectiveShadowMapAlgorithm::LightSpacePerspectiveShadowMapAlgorithm()
|
|
{
|
|
lispsm = new LispSM;
|
|
}
|
|
|
|
LightSpacePerspectiveShadowMapAlgorithm::~LightSpacePerspectiveShadowMapAlgorithm()
|
|
{
|
|
delete lispsm;
|
|
}
|
|
|
|
|
|
#endif
|