 Timestamp:
 03/21/12 18:36:20 (3 years ago)
 Files:

 1 modified
Legend:
 Unmodified
 Added
 Removed

OpenSceneGraph/trunk/src/osgShadow/LightSpacePerspectiveShadowMap.cpp
r12292 r13041 1 /* *c++* OpenSceneGraph  Copyright (C) 19982006 Robert Osfield 1 /* *c++* OpenSceneGraph  Copyright (C) 19982006 Robert Osfield 2 2 * 3 * This library is open source and may be redistributed and/or modified under 4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 3 * This library is open source and may be redistributed and/or modified under 4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 5 5 * (at your option) any later version. The full license is in LICENSE file 6 6 * included with this distribution, and on the openscenegraph.org website. 7 * 7 * 8 8 * This library is distributed in the hope that it will be useful, 9 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 11 * OpenSceneGraph Public License for more details. 12 12 * … … 36 36 37 37 //////////////////////////////////////////////////////////////////////////////// 38 // There are two slightly differing implemetations available on 38 // There are two slightly differing implemetations available on 39 39 // "Light Space Perspective Shadow Maps" page. One from 2004 and other from 2006. 40 40 // Our implementation is written in two versions based on these solutions. … … 48 48 // This code is copyright Vienna University of Technology, 2004. 49 49 // 50 // Please feel FREE to COPY and USE the code to include it in your own work, 50 // Please feel FREE to COPY and USE the code to include it in your own work, 51 51 // provided you include this copyright notice. 52 52 // This program is distributed in the hope that it will be useful, 53 53 // but WITHOUT ANY WARRANTY; without even the implied warranty of 54 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 55 // 54 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 55 // 56 56 // Authors Code: 57 57 // Daniel Scherzer (scherzer@cg.tuwien.ac.at) 58 // 59 // Authors Paper: 58 // 59 // Authors Paper: 60 60 // Michael Wimmer (wimmer@cg.tuwien.ac.at) 61 61 // Daniel Scherzer (scherzer@cg.tuwien.ac.at) … … 88 88 //temporal light View 89 89 //look from position(eyePos) 90 //into direction(lightDir) 90 //into direction(lightDir) 91 91 //with up vector(up) 92 92 look(lightView,eyePos,lightDir,up); … … 95 95 transformVecPoint(B,lightView); 96 96 97 //calculate the cubic hull (an AABB) 97 //calculate the cubic hull (an AABB) 98 98 //of the light space extents of the intersection body B 99 99 //and save the two extreme points min and max … … 103 103 //use the formulas of the paper to get n (and f) 104 104 const double factor = 1.0/sinGamma; 105 const double z_n = factor*nearDist; //often 1 105 const double z_n = factor*nearDist; //often 1 106 106 const double d = absDouble(max[1]min[1]); //perspective transform depth //light space y extents 107 107 const double z_f = z_n + d*sinGamma; … … 119 119 //with the two parameters n(near) and f(far) in y direction 120 120 copyMatrix(lispMtx,IDENTITY); // a = (f+n)/(fn); b = 2*f*n/(fn); 121 lispMtx[ 5] = (f+n)/(fn); // [ 1 0 0 0] 121 lispMtx[ 5] = (f+n)/(fn); // [ 1 0 0 0] 122 122 lispMtx[13] = 2*f*n/(fn); // [ 0 a 0 b] 123 123 lispMtx[ 7] = 1; // [ 0 0 1 0] … … 126 126 //temporal arrangement for the transformation of the points to postperspective space 127 127 mult(lightProjection,lispMtx,lightView); // ligthProjection = lispMtx*lightView 128 128 129 129 //transform the light volume points from world into the distorted light space 130 130 transformVecPoint(&Bcopy,lightProjection); 131 131 132 //calculate the cubic hull (an AABB) 132 //calculate the cubic hull (an AABB) 133 133 //of the light space extents of the intersection body B 134 134 //and save the two extreme points min and max … … 184 184 const double sinGamma = sqrt(1.0 dotProd*dotProd); 185 185 const double factor = 1.0/sinGamma; 186 const double z_n = factor*nearDist; //often 1 186 const double z_n = factor*nearDist; //often 1 187 187 //use the formulas of the paper to get n (and f) 188 188 const double d = fabs( bb._max[1]bb._min[1]); //perspective transform depth //light space y extents … … 194 194 #if PRINT_COMPUTED_N_OPT 195 195 std::cout 196 << " N=" << std::setw(8) << n 196 << " N=" << std::setw(8) << n 197 197 << " n=" << std::setw(8) << z_n 198 198 << " f=" << std::setw(8) << z_f 199 << "\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" 199 << "\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" 200 200 << std::flush; 201 201 #endif … … 216 216 #if 0 217 217 { 218 osg::Matrix mvp = _camera>getViewMatrix() * 218 osg::Matrix mvp = _camera>getViewMatrix() * 219 219 _camera>getProjectionMatrix(); 220 220 … … 232 232 cameraShadow>setProjectionMatrix 233 233 ( lightViewToWorld * lightView * lispProjection * fitToUnitFrustum ); 234 234 235 235 236 236 #if 0 // DOUBLE CHECK! 237 237 bb = computeScenePolytopeBounds 238 238 ( cameraShadow>getViewMatrix() * cameraShadow>getProjectionMatrix() ); 239 239 240 240 if( !osg::equivalent( 0.f, (bb._min  osg::Vec3d(1,1,1)).length2() )  241 241 !osg::equivalent( 0.f, (bb._max  osg::Vec3d( 1, 1, 1)).length2() ) ) … … 263 263 264 264 void LightSpacePerspectiveShadowMapAlgorithm::operator() 265 ( const osgShadow::ConvexPolyhedron* hullShadowedView, 266 const osg::Camera* cameraMain, 265 ( const osgShadow::ConvexPolyhedron* hullShadowedView, 266 const osg::Camera* cameraMain, 267 267 osg::Camera* cameraShadow ) const 268 268 { 269 269 270 270 // all computations are done in post projection light space 271 // which means we are in left handed coordinate system 272 osg::Matrix mvpLight = 271 // which means we are in left handed coordinate system 272 osg::Matrix mvpLight = 273 273 cameraShadow>getViewMatrix() * cameraShadow>getProjectionMatrix(); 274 274 … … 298 298 // as all computations are performed in post projection light space. 299 299 // Frankly, I have my doubts if their error analysis and methodology still works 300 // in non directional lights post projective space. But since I can't prove it doesn't, 301 // I assume it does ;). So I made an effort to modify their original directional algo 300 // in non directional lights post projective space. But since I can't prove it doesn't, 301 // I assume it does ;). So I made an effort to modify their original directional algo 302 302 // to work in true light post perspective space and compute all params in this space. 303 // And here is a snag. Although shadowed hull fits completely into light space, 304 // camera position may not, and after projective transform it may land outside 305 // light frustum and even on/or below infinity plane. I need camera pos to compute 303 // And here is a snag. Although shadowed hull fits completely into light space, 304 // camera position may not, and after projective transform it may land outside 305 // light frustum and even on/or below infinity plane. I need camera pos to compute 306 306 // minimal distance to shadowed hull. If its not right rest of the computation may 307 // be completely off. So in the end this approach is not singulartity free. 308 // I guess this problem is solvable in other way but "this other 309 // way" looks like a topic for other scientific paper and I am definitely not that 310 // ambitious ;). So for the time being I simply try to discover when this happens and 311 // apply workaround, I found works. This workaround may mean that adjusted projection 312 // may not be optimal in original LisPSM Lmax norm sense. But as I wrote above, 313 // I doubt they are optimal when Light is not directional, anyway. 314 315 // Seems that most nasty case when algorithm fails is when cam pos is 316 // below light frustum near plane but above infinity plane  when this occurs 317 // shadows simply disappear. My workaround is to find this case by 307 // be completely off. So in the end this approach is not singulartity free. 308 // I guess this problem is solvable in other way but "this other 309 // way" looks like a topic for other scientific paper and I am definitely not that 310 // ambitious ;). So for the time being I simply try to discover when this happens and 311 // apply workaround, I found works. This workaround may mean that adjusted projection 312 // may not be optimal in original LisPSM Lmax norm sense. But as I wrote above, 313 // I doubt they are optimal when Light is not directional, anyway. 314 315 // Seems that most nasty case when algorithm fails is when cam pos is 316 // below light frustum near plane but above infinity plane  when this occurs 317 // shadows simply disappear. My workaround is to find this case by 318 318 // checking light postperspective transform camera z ( negative value means this ) 319 319 // and make sure min distance to shadow hull is clamped to positive value. … … 327 327 // OSG_NOTICE<<"LightSpacePerspectiveShadowMapAlgorithm::operator() adjusting eye"<<std::endl; 328 328 #endif 329 329 330 330 } 331 331 #endif … … 341 341 342 342 // Beware!!! Dirty Tricks: 343 // Light direction in light post proj space is actually (0,0,1) 344 // But since we want to pass it to std OpenGL right handed coordinate 345 // makeLookAt function we compensate the effects by also using right 346 // handed view forward vector (ie 0,0,1) instead. 347 // So in the end we get left handed makeLookAt behaviour (D3D like)... 348 // I agree this method is bizarre. But it works so I left it as is. 349 // It sort of came out by itself through trial and error. 350 // I later understoood why it works. 343 // Light direction in light post proj space is actually (0,0,1) 344 // But since we want to pass it to std OpenGL right handed coordinate 345 // makeLookAt function we compensate the effects by also using right 346 // handed view forward vector (ie 0,0,1) instead. 347 // So in the end we get left handed makeLookAt behaviour (D3D like)... 348 // I agree this method is bizarre. But it works so I left it as is. 349 // It sort of came out by itself through trial and error. 350 // I later understoood why it works. 351 351 352 352 osg::Vec3 lightDir(0,0,1); … … 368 368 const double z_n = factor*nearDist; 369 369 //perspective transform depth light space y extents 370 const double d = fabs( bb._max[1]bb._min[1]); 370 const double d = fabs( bb._max[1]bb._min[1]); 371 371 const double z_f = z_n + d*sinGamma; 372 372 double n = (z_n+sqrt(z_f*z_n))/sinGamma; … … 383 383 const double f = n+d; 384 384 osg::Vec3d pos = eyeup*(nnearDist); 385 //pos = eye; 385 //pos = eye; 386 386 lightView.makeLookAt( pos, pos + lightDir, up ); 387 387 … … 409 409 410 410 // Adapted Modified version of LispSM authors implementation from 2006 411 // Nopt formula differs from the paper. I adopted original authors class to 411 // Nopt formula differs from the paper. I adopted original authors class to 412 412 // use with OSG 413 413 … … 424 424 typedef std::vector<osg::Vec3d> Vertices; 425 425 426 void setProjectionMatrix( const osg::Matrix & projectionMatrix ) 426 void setProjectionMatrix( const osg::Matrix & projectionMatrix ) 427 427 { _projectionMatrix = projectionMatrix; } 428 428 429 void setViewMatrix( const osg::Matrix & viewMatrix ) 429 void setViewMatrix( const osg::Matrix & viewMatrix ) 430 430 { _viewMatrix = viewMatrix; } 431 431 … … 436 436 { return _hull; } 437 437 438 const osg::Matrix & getProjectionMatrix( void ) const 438 const osg::Matrix & getProjectionMatrix( void ) const 439 439 { return _projectionMatrix; } 440 440 441 const osg::Matrix & getViewMatrix( void ) const 441 const osg::Matrix & getViewMatrix( void ) const 442 442 { return _viewMatrix; } 443 444 bool getUseLiSPSM() const 443 444 bool getUseLiSPSM() const 445 445 { return _useLiSPSM; } 446 446 447 void setUseLiSPSM( bool use ) 447 void setUseLiSPSM( bool use ) 448 448 { _useLiSPSM = use; } 449 449 … … 451 451 { return _useFormula; } 452 452 453 void setUseFormula( bool use ) 453 void setUseFormula( bool use ) 454 454 { _useFormula = use; } 455 455 … … 457 457 { return _useOldFormula; } 458 458 459 void setUseOldFormula( bool use ) 459 void setUseOldFormula( bool use ) 460 460 { _useOldFormula = use; } 461 461 462 void setN(const double& n ) 462 void setN(const double& n ) 463 463 { _N = n; } 464 464 465 const double getN() const 466 { return _N; } 465 const double getN() const 466 { return _N; } 467 467 468 468 //for old LispSM formula from paper … … 470 470 { return _nearDist; } 471 471 472 void setNearDist( const double & nearDist ) 472 void setNearDist( const double & nearDist ) 473 473 { _nearDist = nearDist; } 474 474 … … 476 476 { return _farDist; } 477 477 478 void setFarDist( const double & farDist ) 478 void setFarDist( const double & farDist ) 479 479 { _farDist = farDist; } 480 480 481 const osg::Vec3d & getEyeDir() const 481 const osg::Vec3d & getEyeDir() const 482 482 { return _eyeDir; } 483 483 484 const osg::Vec3d & getLightDir() const 484 const osg::Vec3d & getLightDir() const 485 485 { return _lightDir; } 486 486 487 void setEyeDir( const osg::Vec3d eyeDir ) 487 void setEyeDir( const osg::Vec3d eyeDir ) 488 488 { _eyeDir = eyeDir; } 489 489 490 void setLightDir( const osg::Vec3d lightDir ) 490 void setLightDir( const osg::Vec3d lightDir ) 491 491 { _lightDir = lightDir; } 492 492 … … 512 512 513 513 osg::Vec3d getNearCameraPointE() const; 514 514 515 515 osg::Vec3d getZ0_ls 516 516 (const osg::Matrix& lightSpace, const osg::Vec3d& e, const double& b_lsZmax, const osg::Vec3d& eyeDir) const; … … 539 539 } 540 540 541 osg::Vec3d LispSM::getNearCameraPointE( ) const 541 osg::Vec3d LispSM::getNearCameraPointE( ) const 542 542 { 543 543 const osg::Matrix& eyeView = getViewMatrix(); … … 566 566 //and on the near plane of the C frustum (the plane z = bZmax) and on the line x = e.x 567 567 osg::Vec3d LispSM::getZ0_ls 568 (const osg::Matrix& lightSpace, const osg::Vec3d& e, const double& b_lsZmax, const osg::Vec3d& eyeDir) const 569 { 570 //to calculate the parallel plane to the near plane through e we 568 (const osg::Matrix& lightSpace, const osg::Vec3d& e, const double& b_lsZmax, const osg::Vec3d& eyeDir) const 569 { 570 //to calculate the parallel plane to the near plane through e we 571 571 //calculate the plane A with the three points 572 572 osg::Plane A(eyeDir,e); … … 576 576 const osg::Vec3d e_ls = e * lightSpace; 577 577 578 //z_0 has the x coordinate of e, the z coord of B_lsZmax 578 //z_0 has the x coordinate of e, the z coord of B_lsZmax 579 579 //and the y coord of the plane A and plane (z==B_lsZmax) intersection 580 580 #if 1 581 581 osg::Vec3d v = osg::Vec3d(e_ls.x(),0,b_lsZmax); 582 582 583 // x & z are given. We compute y from equations: 583 // x & z are given. We compute y from equations: 584 584 // A.distance( x,y,z ) == 0 585 585 // A.distance( x,y,z ) == A.distance( x,0,z ) + A.normal.y * y … … 598 598 } 599 599 600 double LispSM::calcNoptGeneral(const osg::Matrix lightSpace, const osg::BoundingBox& B_ls) const 600 double LispSM::calcNoptGeneral(const osg::Matrix lightSpace, const osg::BoundingBox& B_ls) const 601 601 { 602 602 const osg::Matrix& eyeView = getViewMatrix(); … … 605 605 const osg::Vec3d z0_ls = getZ0_ls(lightSpace, _E,B_ls.zMax(),getEyeDir()); 606 606 const osg::Vec3d z1_ls = osg::Vec3d(z0_ls.x(),z0_ls.y(),B_ls.zMin()); 607 607 608 608 //to world 609 609 const osg::Vec4d z0_ws = osg::Vec4d( z0_ls, 1 ) * invLightSpace; … … 620 620 621 621 // solve camera pos singularity in light space problem brutally: 622 // if extreme points of B projected to Light space extend beyond 622 // if extreme points of B projected to Light space extend beyond 623 623 // camera frustum simply use B extents in camera frustum 624 624 625 // Its not optimal selection but ceratainly better than negative N 625 // Its not optimal selection but ceratainly better than negative N 626 626 osg::BoundingBox bb = _hull.computeBoundingBox( eyeView ); 627 627 z0 = bb.zMax(); 628 if( z0 <= 0 ) 628 if( z0 <= 0 ) 629 629 z0 = 0.1; 630 630 631 631 z1 = bb.zMin(); 632 if( z1 <= z0 ) 632 if( z1 <= z0 ) 633 633 z1 = z0 + 0.1; 634 634 } … … 639 639 #if PRINT_COMPUTED_N_OPT 640 640 std::cout 641 << " N=" << std::setw(8) << N 641 << " N=" << std::setw(8) << N 642 642 << " n=" << std::setw(8) << z0 643 643 << " f=" << std::setw(8) << z1 644 << "\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" 644 << "\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" 645 645 << std::flush; 646 646 #endif … … 648 648 } 649 649 650 double LispSM::calcNoptOld( const double gamma_ ) const 650 double LispSM::calcNoptOld( const double gamma_ ) const 651 651 { 652 652 const double& n = getNearDist(); … … 665 665 #if PRINT_COMPUTED_N_OPT 666 666 std::cout 667 << " N=" << std::setw(8) << N 667 << " N=" << std::setw(8) << N 668 668 << " n=" << std::setw(8) << n 669 669 << " f=" << std::setw(8) << f 670 << "\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" 670 << "\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" 671 671 << std::flush; 672 672 #endif … … 674 674 } 675 675 676 double LispSM::getN(const osg::Matrix lightSpace, const osg::BoundingBox& B_ls) const 676 double LispSM::getN(const osg::Matrix lightSpace, const osg::BoundingBox& B_ls) const 677 677 { 678 678 if( getUseFormula()) { … … 687 687 } 688 688 //this is the algorithm discussed in the article 689 osg::Matrix LispSM::getLispSmMtx( const osg::Matrix& lightSpace ) const 689 osg::Matrix LispSM::getLispSmMtx( const osg::Matrix& lightSpace ) const 690 690 { 691 691 const osg::BoundingBox B_ls = _hull.computeBoundingBox( lightSpace ); … … 695 695 //get the coordinates of the near camera point in light space 696 696 const osg::Vec3d e_ls = _E * lightSpace; 697 //c start has the x and y coordinate of e, the z coord of B.min() 697 //c start has the x and y coordinate of e, the z coord of B.min() 698 698 const osg::Vec3d Cstart_lp(e_ls.x(),e_ls.y(),B_ls.zMax()); 699 699 … … 713 713 714 714 //the lispsm perspective transformation 715 715 716 716 //here done with a standard frustum call that maps P onto the unit cube with 717 717 //corner points [1,1,1] and [1,1,1]. … … 751 751 } 752 752 753 osg::Vec3d projDir( osg::Vec3( b_lp[0], b_lp[1], b_lp[2] ) / b_lp[3]  753 osg::Vec3d projDir( osg::Vec3( b_lp[0], b_lp[1], b_lp[2] ) / b_lp[3]  754 754 osg::Vec3( e_lp[0], e_lp[1], e_lp[2] ) / e_lp[3] ); 755 755 … … 762 762 763 763 void LispSM::updateLightMtx 764 ( osg::Matrix& lightView, osg::Matrix& lightProj ) const 764 ( osg::Matrix& lightView, osg::Matrix& lightProj ) const 765 765 { 766 766 //calculate standard light space for spot or directional lights 767 //this routine returns two matrices: 767 //this routine returns two matrices: 768 768 //lightview contains the rotated translated frame 769 769 //lightproj contains in the case of a spot light the spot light perspective transformation … … 791 791 osg::Matrix L = lightView * lightProj; 792 792 793 osg::Vec3d projViewDir = getProjViewDir_ls(L); 793 osg::Vec3d projViewDir = getProjViewDir_ls(L); 794 794 795 795 if( getUseLiSPSM() /* && projViewDir.z() < 0*/ ) { … … 798 798 //calculate a frame matrix that uses the projViewDir[lightspace] as up vector 799 799 //look(from position, into the direction of the projected direction, with unchanged upvector) 800 lightProj = lightProj * 800 lightProj = lightProj * 801 801 osg::Matrix::lookAt( osg::Vec3d(0,0,0), projViewDir, osg::Vec3d(0,1,0) ); 802 802 … … 832 832 void LightSpacePerspectiveShadowMapAlgorithm::operator() 833 833 ( const osgShadow::ConvexPolyhedron* hullShadowedView, 834 const osg::Camera* cameraMain, 834 const osg::Camera* cameraMain, 835 835 osg::Camera* cameraShadow ) const 836 836 { … … 844 844 845 845 #else 846 846 847 847 osg::Vec3d lightDir = osg::Matrix::transform3x3( cameraShadow>getViewMatrix(), osg::Vec3d( 0.0, 0.0, 1.0 ) ); 848 848 osg::Vec3d eyeDir = osg::Matrix::transform3x3( cameraMain>getViewMatrix(), osg::Vec3d( 0.0, 0.0, 1.0 ) ); … … 860 860 if( proj.getOrtho( l,r,b,t,n,f ) ) 861 861 { 862 osg::Vec3d camPosInLightSpace = 863 osg::Vec3d( 0, 0, 0 ) * 864 osg::Matrix::inverse( cameraMain>getViewMatrix() ) * 862 osg::Vec3d camPosInLightSpace = 863 osg::Vec3d( 0, 0, 0 ) * 864 osg::Matrix::inverse( cameraMain>getViewMatrix() ) * 865 865 cameraShadow>getViewMatrix() * 866 866 cameraShadow>getProjectionMatrix(); … … 871 871 lispsm>setEyeDir( eyeDir ); 872 872 873 osg::BoundingBox bb = 873 osg::BoundingBox bb = 874 874 hullShadowedView>computeBoundingBox( cameraMain>getViewMatrix() ); 875 875