/* -*-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 #include #include #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 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& 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* == -n* 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