From Wojciech Lewandowski, a range of new ShadowMap implementations include View Dependent Shadow Maps - LispSM
From Robert Osfield, refactored Wojciech's submission to integrated with osgShadow and use coding sytle more consistent with rest of OSG.
This commit is contained in:
parent
f388d6a938
commit
bf15614df6
123
include/osgShadow/ConvexPolyhedron
Normal file
123
include/osgShadow/ConvexPolyhedron
Normal file
@ -0,0 +1,123 @@
|
||||
/* -*-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.
|
||||
*/
|
||||
|
||||
#ifndef OSGSHADOW_CONVEXPOLYHEDRON
|
||||
#define OSGSHADOW_CONVEXPOLYHEDRON 1
|
||||
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Polytope>
|
||||
#include <osgShadow/Export>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Class based on CustomPolytope defined and used in osgSim::OverlayNode.cpp.
|
||||
// Honors should go to Robert Osfield for writing such useful piece of code.
|
||||
// First incarnations of my ConvexPolyhedron were derived from CustomPolytope.
|
||||
// Later I made a number of modifications aimed at improving convex hull
|
||||
// precision of intersection & extrusion operations and ended up with code
|
||||
// so mixed that I decided to rewrite it as separate class.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace osgShadow {
|
||||
|
||||
class OSGSHADOW_EXPORT ConvexPolyhedron
|
||||
{
|
||||
public:
|
||||
typedef std::vector<osg::Vec3d> Vertices;
|
||||
|
||||
static const osg::Matrix & defaultMatrix;
|
||||
|
||||
struct Face
|
||||
{
|
||||
std::string name;
|
||||
osg::Plane plane;
|
||||
Vertices vertices;
|
||||
};
|
||||
|
||||
typedef std::list<Face> Faces;
|
||||
Faces _faces;
|
||||
|
||||
ConvexPolyhedron( void ) { }
|
||||
|
||||
ConvexPolyhedron( const osg::Matrix& matrix, const osg::Matrix& inverse,
|
||||
const osg::BoundingBox& bb = osg::BoundingBox(-1,-1,-1,1,1,1));
|
||||
|
||||
Face& createFace() { _faces.push_back(Face()); return _faces.back(); }
|
||||
void clear() { _faces.clear(); }
|
||||
|
||||
|
||||
void setToUnitFrustum(bool withNear=true, bool withFar=true);
|
||||
void setToBoundingBox(const osg::BoundingBox& bb);
|
||||
void transform(const osg::Matrix& matrix, const osg::Matrix& inverse);
|
||||
void transformClip(const osg::Matrix& matrix, const osg::Matrix& inverse);
|
||||
|
||||
|
||||
bool mergeFaces
|
||||
( const Face & face0, const Face & face1, Face & face );
|
||||
|
||||
void mergeCoplanarFaces( const double & plane_normal_dot_tolerance = 0.0,
|
||||
const double & plane_distance_tolerance = 0.0 );
|
||||
|
||||
void removeDuplicateVertices( void );
|
||||
|
||||
|
||||
static int pointsColinear
|
||||
( const osg::Vec3d & va, const osg::Vec3d & vb, const osg::Vec3d & vc,
|
||||
const double & edge_normal_dot_tolerance = 0.0,
|
||||
const double & null_edge_length_tolerance = 0.0 );
|
||||
|
||||
static int isFacePolygonConvex( Face & face, bool ignoreCollinearVertices = true );
|
||||
|
||||
bool checkCoherency
|
||||
( bool checkForNonConvexPolys = false, const char * errorPrefix = NULL );
|
||||
|
||||
|
||||
void cut(const osg::Polytope& polytope);
|
||||
|
||||
void cut(const ConvexPolyhedron& polytope);
|
||||
|
||||
void cut(const osg::Plane& plane, const std::string& name=std::string());
|
||||
|
||||
void extrude( const osg::Vec3d & offset );
|
||||
|
||||
void translate( const osg::Vec3d & offset );
|
||||
|
||||
|
||||
void getPolytope(osg::Polytope& polytope) const;
|
||||
void getPoints(Vertices& vertices) const;
|
||||
osg::BoundingBox computeBoundingBox( const osg::Matrix & m = osgShadow::ConvexPolyhedron::defaultMatrix ) const;
|
||||
|
||||
osg::Geometry* buildGeometry( const osg::Vec4d& colorOutline,
|
||||
const osg::Vec4d& colorInside,
|
||||
osg::Geometry* useGeometry = NULL ) const;
|
||||
|
||||
|
||||
bool dumpGeometry( const Face * face = NULL,
|
||||
const osg::Plane * plane = NULL,
|
||||
ConvexPolyhedron * basehull = NULL,
|
||||
const char * filename = "convexpolyhedron.osg",
|
||||
const osg::Vec4d& colorOutline = osg::Vec4( 0,1,0,0.5 ),
|
||||
const osg::Vec4d& colorInside = osg::Vec4( 0,1,0,0.25 ),
|
||||
const osg::Vec4d& faceColorOutline = osg::Vec4( 0,0,1,0.5 ),
|
||||
const osg::Vec4d& faceColorInside = osg::Vec4( 0,0,1,0.25 ),
|
||||
const osg::Vec4d& planeColorOutline = osg::Vec4( 1,0,0,0.5 ),
|
||||
const osg::Vec4d& planeColorInside = osg::Vec4( 1,0,0,0.25 ),
|
||||
const osg::Vec4d& baseColorOutline = osg::Vec4( 0,0,0,0.5 ),
|
||||
const osg::Vec4d& baseColorInside = osg::Vec4( 0,0,0,0.25 ) ) const;
|
||||
};
|
||||
|
||||
} // namespace osgShadow
|
||||
|
||||
#endif
|
178
include/osgShadow/DebugShadowMap
Normal file
178
include/osgShadow/DebugShadowMap
Normal file
@ -0,0 +1,178 @@
|
||||
/* -*-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.
|
||||
*/
|
||||
|
||||
#ifndef OSGSHADOW_DEBUGSHADOWMAP
|
||||
#define OSGSHADOW_DEBUGSHADOWMAP 1
|
||||
|
||||
#include <osgShadow/ViewDependentShadowTechnique>
|
||||
#include <osgShadow/ConvexPolyhedron>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace osgShadow {
|
||||
|
||||
/**
|
||||
Class used as a layer for debuging resources used by derived xxxShadowMap classes.
|
||||
As designed by its base ViewDepndentShadowTechnique, DebugShadowMap serves mainly as container of
|
||||
DebugShadowMap::ViewData objects. Most of the debuging support work is done by these objects.
|
||||
DebugShadowMap technique only initializes them in initViewDependentData method.
|
||||
|
||||
Debuging outputs present:
|
||||
Shadow maps (pseudo colored to improve readability)
|
||||
Shadow and related volumes (represented as convex polyhedra)
|
||||
*/
|
||||
|
||||
class OSGSHADOW_EXPORT DebugShadowMap : public ViewDependentShadowTechnique
|
||||
{
|
||||
public :
|
||||
|
||||
/*
|
||||
All classes stemming from ViewDependentShadowTechnique follow the same pattern.
|
||||
|
||||
They are always based on some underlying level base Technique and they always
|
||||
derive their ViewData from ViewData structure defined in underlying base Technique.
|
||||
|
||||
I use some typedefs to make these inheritance patterns easier to declare/define.
|
||||
*/
|
||||
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef DebugShadowMap ThisClass;
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef ViewDependentShadowTechnique BaseClass;
|
||||
|
||||
/** Classic OSG constructor */
|
||||
DebugShadowMap();
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
DebugShadowMap(const DebugShadowMap& dsm, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, DebugShadowMap );
|
||||
|
||||
/** Turn on/off debuging hud & rendering of debug volumes in main view */
|
||||
void setDebugDraw( bool draw ) { _doDebugDraw = draw; }
|
||||
|
||||
/** Tell if debuging hud & rendering of debug volumes is active */
|
||||
bool getDebugDraw( void ) { return _doDebugDraw; }
|
||||
|
||||
protected:
|
||||
/** Classic protected OSG destructor */
|
||||
virtual ~DebugShadowMap();
|
||||
|
||||
osg::Vec2s _hudSize;
|
||||
osg::Vec2s _hudOrigin;
|
||||
osg::Vec2s _viewportSize;
|
||||
osg::Vec2s _viewportOrigin;
|
||||
osg::Vec2s _orthoSize;
|
||||
osg::Vec2s _orthoOrigin;
|
||||
|
||||
bool _doDebugDraw;
|
||||
|
||||
osg::ref_ptr< osg::Shader > _depthColorFragmentShader;
|
||||
|
||||
struct ViewData: public BaseClass::ViewData
|
||||
{
|
||||
/**
|
||||
Texture used as ShadowMap - initialized by derived classes.
|
||||
But it has to be defined now since DebugShadowMap::ViewData methods use it
|
||||
*/
|
||||
osg::ref_ptr< osg::Texture2D > _texture;
|
||||
/**
|
||||
Camera used to render ShadowMap - initialized by derived classes.
|
||||
But it has to be defined now since DebugShadowMap::ViewData methods use it
|
||||
*/
|
||||
osg::ref_ptr< osg::Camera > _camera;
|
||||
|
||||
osg::Matrix _viewProjection;
|
||||
osg::Camera * _viewCamera;
|
||||
|
||||
// Debug hud variables
|
||||
|
||||
/** Coloring Shader used to present shadow depth map contents */
|
||||
osg::ref_ptr< osg::Shader > _depthColorFragmentShader;
|
||||
|
||||
struct PolytopeGeometry {
|
||||
|
||||
ConvexPolyhedron _polytope;
|
||||
osg::ref_ptr< osg::Geometry > _geometry[2];
|
||||
osg::Vec4 _colorOutline;
|
||||
osg::Vec4 _colorInside;
|
||||
};
|
||||
|
||||
typedef std::map< std::string, PolytopeGeometry > PolytopeGeometryMap;
|
||||
|
||||
osg::Vec2s _hudSize;
|
||||
osg::Vec2s _hudOrigin;
|
||||
osg::Vec2s _viewportSize;
|
||||
osg::Vec2s _viewportOrigin;
|
||||
osg::Vec2s _orthoSize;
|
||||
osg::Vec2s _orthoOrigin;
|
||||
|
||||
bool *_doDebugDrawPtr;
|
||||
|
||||
PolytopeGeometryMap _polytopeGeometryMap;
|
||||
osg::ref_ptr< osg::Geode > _geode[2];
|
||||
osg::ref_ptr< osg::MatrixTransform > _transform[2];
|
||||
|
||||
std::map< std::string, osg::Matrix > _matrixMap;
|
||||
std::map< std::string, osg::Polytope > _polytopeMap;
|
||||
std::map< std::string, osg::BoundingBox > _boundingBoxMap;
|
||||
|
||||
osg::ref_ptr<osg::Camera> _cameraDebugHUD;
|
||||
|
||||
bool getDebugDraw() { return *_doDebugDrawPtr; }
|
||||
|
||||
virtual void init( ThisClass * st, osgUtil::CullVisitor * cv );
|
||||
|
||||
virtual void cull( );
|
||||
|
||||
virtual void createDebugHUD( void );
|
||||
|
||||
virtual void cullDebugGeometry( );
|
||||
|
||||
virtual void updateDebugGeometry( const osg::Camera * screenCam,
|
||||
const osg::Camera * shadowCam );
|
||||
|
||||
void setDebugPolytope( const char * name,
|
||||
const ConvexPolyhedron & polytope = *(ConvexPolyhedron*)( NULL ),
|
||||
osg::Vec4 colorOutline = osg::Vec4(0,0,0,0),
|
||||
osg::Vec4 colorInside = osg::Vec4(0,0,0,0) );
|
||||
|
||||
bool DebugBoundingBox( const osg::BoundingBox & bb, const char * name = "" );
|
||||
bool DebugPolytope( const osg::Polytope & p, const char * name = "" );
|
||||
bool DebugMatrix( const osg::Matrix & m, const char * name = "" );
|
||||
|
||||
static osg::Vec3d computeShadowTexelToPixelError
|
||||
( const osg::Matrix & mvpwView,
|
||||
const osg::Matrix & mvpwShadow,
|
||||
const osg::Vec3d & vWorld,
|
||||
const osg::Vec3d & vDelta = osg::Vec3d( 0.01,0.01,0.01 ) );
|
||||
|
||||
static void displayShadowTexelToPixelErrors
|
||||
( const osg::Camera * viewCam,
|
||||
const osg::Camera * shadowCam,
|
||||
const ConvexPolyhedron * hull );
|
||||
};
|
||||
|
||||
META_ViewDependentShadowTechniqueData( ThisClass, ViewData )
|
||||
};
|
||||
|
||||
} // namespace osgShadow
|
||||
|
||||
#endif
|
122
include/osgShadow/LightSpacePerspectiveShadowMap
Normal file
122
include/osgShadow/LightSpacePerspectiveShadowMap
Normal file
@ -0,0 +1,122 @@
|
||||
/* -*-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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OSGSHADOW_LIGHTSPACEPERSPECTIVESHADOWMAP
|
||||
#define OSGSHADOW_LIGHTSPACEPERSPECTIVESHADOWMAP 1
|
||||
|
||||
#include <osgShadow/MinimalCullBoundsShadowMap>
|
||||
#include <osgShadow/MinimalDrawBoundsShadowMap>
|
||||
#include <osgShadow/ProjectionShadowMap>
|
||||
|
||||
namespace osgShadow {
|
||||
|
||||
// Class implements
|
||||
// "Light Space Perspective Shadow Maps" algorithm by
|
||||
// Michael Wimmer, Daniel Scherzer, Werner Purgathofer
|
||||
// http://www.cg.tuwien.ac.at/research/vr/lispsm/
|
||||
|
||||
class LispSM;
|
||||
|
||||
class OSGSHADOW_EXPORT LightSpacePerspectiveShadowMapAlgorithm
|
||||
{
|
||||
public:
|
||||
LightSpacePerspectiveShadowMapAlgorithm();
|
||||
~LightSpacePerspectiveShadowMapAlgorithm();
|
||||
|
||||
void operator() (
|
||||
const osgShadow::ConvexPolyhedron* hullShadowedView,
|
||||
const osg::Camera* cameraMain,
|
||||
osg::Camera* cameraShadow ) const;
|
||||
|
||||
protected:
|
||||
LispSM * lispsm;
|
||||
};
|
||||
|
||||
// Optimized for draw traversal shadow bounds
|
||||
class OSGSHADOW_EXPORT LightSpacePerspectiveShadowMapDB: public ProjectionShadowMap< MinimalDrawBoundsShadowMap, LightSpacePerspectiveShadowMapAlgorithm >
|
||||
{
|
||||
public:
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef ProjectionShadowMap< MinimalDrawBoundsShadowMap, LightSpacePerspectiveShadowMapAlgorithm > BaseClass;
|
||||
|
||||
/** Classic OSG constructor */
|
||||
LightSpacePerspectiveShadowMapDB()
|
||||
{
|
||||
}
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
LightSpacePerspectiveShadowMapDB(
|
||||
const LightSpacePerspectiveShadowMapDB& copy,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY) : BaseClass(copy,copyop)
|
||||
{
|
||||
}
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, LightSpacePerspectiveShadowMapDB );
|
||||
};
|
||||
|
||||
// Optimized for cull traversal shadow bounds
|
||||
class OSGSHADOW_EXPORT LightSpacePerspectiveShadowMapCB: public ProjectionShadowMap< MinimalCullBoundsShadowMap, LightSpacePerspectiveShadowMapAlgorithm >
|
||||
{
|
||||
public:
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef ProjectionShadowMap< MinimalCullBoundsShadowMap, LightSpacePerspectiveShadowMapAlgorithm > BaseClass;
|
||||
|
||||
/** Classic OSG constructor */
|
||||
LightSpacePerspectiveShadowMapCB()
|
||||
{
|
||||
}
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
LightSpacePerspectiveShadowMapCB(
|
||||
const LightSpacePerspectiveShadowMapCB& copy,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY) : BaseClass(copy,copyop)
|
||||
{
|
||||
}
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, LightSpacePerspectiveShadowMapCB );
|
||||
};
|
||||
|
||||
// Optimized for view frustum bounds
|
||||
class OSGSHADOW_EXPORT LightSpacePerspectiveShadowMapVB: public ProjectionShadowMap< MinimalShadowMap, LightSpacePerspectiveShadowMapAlgorithm >
|
||||
{
|
||||
public:
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef ProjectionShadowMap< MinimalShadowMap, LightSpacePerspectiveShadowMapAlgorithm > BaseClass;
|
||||
|
||||
/** Classic OSG constructor */
|
||||
LightSpacePerspectiveShadowMapVB()
|
||||
{
|
||||
}
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
LightSpacePerspectiveShadowMapVB(
|
||||
const LightSpacePerspectiveShadowMapVB& copy,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY) : BaseClass(copy,copyop)
|
||||
{
|
||||
}
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, LightSpacePerspectiveShadowMapVB );
|
||||
};
|
||||
|
||||
typedef LightSpacePerspectiveShadowMapDB LightSpacePerspectiveShadowMap;
|
||||
|
||||
} // namespace osgShadow
|
||||
|
||||
#endif
|
82
include/osgShadow/MinimalCullBoundsShadowMap
Normal file
82
include/osgShadow/MinimalCullBoundsShadowMap
Normal file
@ -0,0 +1,82 @@
|
||||
/* -*-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.
|
||||
*/
|
||||
|
||||
#ifndef OSGSHADOW_MINIMALCULLBOUNDSSHADOWMAP
|
||||
#define OSGSHADOW_MINIMALCULLBOUNDSSHADOWMAP 1
|
||||
|
||||
#include <osgShadow/MinimalShadowMap>
|
||||
|
||||
namespace osgShadow {
|
||||
|
||||
class OSGSHADOW_EXPORT MinimalCullBoundsShadowMap
|
||||
: public MinimalShadowMap
|
||||
{
|
||||
public :
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef MinimalCullBoundsShadowMap ThisClass;
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef MinimalShadowMap BaseClass;
|
||||
|
||||
/** Classic OSG constructor */
|
||||
MinimalCullBoundsShadowMap();
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
MinimalCullBoundsShadowMap(
|
||||
const MinimalCullBoundsShadowMap& mcbsm,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, MinimalCullBoundsShadowMap );
|
||||
|
||||
protected:
|
||||
/** Classic protected OSG destructor */
|
||||
virtual ~MinimalCullBoundsShadowMap(void);
|
||||
|
||||
struct ViewData: public MinimalShadowMap::ViewData
|
||||
{
|
||||
virtual void init( ThisClass * st, osgUtil::CullVisitor * cv );
|
||||
|
||||
virtual void cullShadowReceivingScene( );
|
||||
|
||||
virtual void aimShadowCastingCamera( const osg::Light *light,
|
||||
const osg::Vec4 &worldLightPos,
|
||||
const osg::Vec3 &worldLightDir,
|
||||
const osg::Vec3 &worldLightUp
|
||||
= osg::Vec3(0,1,0) );
|
||||
|
||||
typedef std::vector< osgUtil::RenderLeaf* > RenderLeafList;
|
||||
|
||||
static unsigned RemoveOldRenderLeaves
|
||||
( RenderLeafList &rllNew, RenderLeafList &rllOld );
|
||||
|
||||
static unsigned RemoveIgnoredRenderLeaves( RenderLeafList &rll );
|
||||
|
||||
static osg::BoundingBox ComputeRenderLeavesBounds
|
||||
( RenderLeafList &rll, osg::Matrix & projectionToWorld );
|
||||
|
||||
static osg::BoundingBox ComputeRenderLeavesBounds
|
||||
( RenderLeafList &rll, osg::Matrix & projectionToWorld, osg::Polytope & polytope );
|
||||
|
||||
static void GetRenderLeaves
|
||||
( osgUtil::RenderBin *rb, RenderLeafList &rll );
|
||||
};
|
||||
|
||||
META_ViewDependentShadowTechniqueData( ThisClass, ThisClass::ViewData )
|
||||
};
|
||||
|
||||
} // namespace osgShadow
|
||||
|
||||
#endif
|
126
include/osgShadow/MinimalDrawBoundsShadowMap
Normal file
126
include/osgShadow/MinimalDrawBoundsShadowMap
Normal file
@ -0,0 +1,126 @@
|
||||
/* -*-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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OSGSHADOW_MINIMALDRAWBOUNDSSHADOWMAP
|
||||
#define OSGSHADOW_MINIMALDRAWBOUNDSSHADOWMAP 1
|
||||
|
||||
#include <osgShadow/MinimalShadowMap>
|
||||
|
||||
namespace osgShadow {
|
||||
|
||||
class OSGSHADOW_EXPORT MinimalDrawBoundsShadowMap
|
||||
: public MinimalShadowMap
|
||||
{
|
||||
public :
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef MinimalDrawBoundsShadowMap ThisClass;
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef MinimalShadowMap BaseClass;
|
||||
|
||||
/** Classic OSG constructor */
|
||||
MinimalDrawBoundsShadowMap();
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
MinimalDrawBoundsShadowMap(
|
||||
const MinimalDrawBoundsShadowMap& mdbsm,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, MinimalDrawBoundsShadowMap );
|
||||
|
||||
protected:
|
||||
/** Classic protected OSG destructor */
|
||||
virtual ~MinimalDrawBoundsShadowMap(void);
|
||||
|
||||
struct ViewData: public BaseClass::ViewData
|
||||
{
|
||||
osg::ref_ptr< osg::RefMatrix > _projection;
|
||||
osg::Vec2s _boundAnalysisSize;
|
||||
osg::ref_ptr< osg::Image > _boundAnalysisImage;
|
||||
osg::ref_ptr< osg::Texture2D > _boundAnalysisTexture;
|
||||
osg::ref_ptr< osg::Camera > _boundAnalysisCamera;
|
||||
osg::ref_ptr< osg::Camera > _mainCamera;
|
||||
|
||||
void setShadowCameraProjectionMatrixPtr( osg::RefMatrix * projection )
|
||||
{ _projection = projection; }
|
||||
|
||||
osg::RefMatrix * getShadowCameraProjectionMatrixPtr( void )
|
||||
{ return _projection.get(); }
|
||||
|
||||
virtual void init( ThisClass * st, osgUtil::CullVisitor * cv );
|
||||
|
||||
virtual void cullShadowReceivingScene( );
|
||||
|
||||
virtual void createDebugHUD( );
|
||||
|
||||
virtual void recordShadowMapParams( );
|
||||
|
||||
virtual void cullBoundAnalysisScene( );
|
||||
|
||||
static osg::BoundingBox scanImage( const osg::Image * image, osg::Matrix m );
|
||||
|
||||
virtual void performBoundAnalysis( const osg::Camera& camera );
|
||||
|
||||
ViewData( void ): _boundAnalysisSize( 64, 64 ) {}
|
||||
};
|
||||
|
||||
META_ViewDependentShadowTechniqueData( ThisClass, ThisClass::ViewData )
|
||||
|
||||
|
||||
struct CameraPostDrawCallback : public osg::Camera::DrawCallback {
|
||||
|
||||
CameraPostDrawCallback( ViewData * vd ): _vd( vd )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator ()( const osg::Camera& camera ) const
|
||||
{
|
||||
if( _vd.valid() )
|
||||
_vd->performBoundAnalysis( camera );
|
||||
}
|
||||
|
||||
osg::observer_ptr< ViewData > _vd;
|
||||
};
|
||||
|
||||
struct CameraCullCallback: public osg::NodeCallback {
|
||||
|
||||
CameraCullCallback(ViewData * vd, osg::NodeCallback * nc): _vd(vd), _nc(nc)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
osgUtil::CullVisitor *cv = dynamic_cast< osgUtil::CullVisitor *>( nv );
|
||||
|
||||
if( _nc.valid() )
|
||||
_nc->operator()(node,nv);
|
||||
else
|
||||
traverse(node,nv);
|
||||
|
||||
if( cv )
|
||||
_vd->recordShadowMapParams( );
|
||||
}
|
||||
|
||||
protected:
|
||||
osg::ref_ptr< osg::NodeCallback > _nc;
|
||||
osg::observer_ptr< ViewData > _vd;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace osgShadow
|
||||
|
||||
#endif
|
156
include/osgShadow/MinimalShadowMap
Normal file
156
include/osgShadow/MinimalShadowMap
Normal file
@ -0,0 +1,156 @@
|
||||
/* -*-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.
|
||||
*/
|
||||
|
||||
#ifndef OSGSHADOW_MINIMALSHADOWMAP
|
||||
#define OSGSHADOW_MINIMALSHADOWMAP 1
|
||||
|
||||
#include <osgShadow/StandardShadowMap>
|
||||
|
||||
namespace osgShadow {
|
||||
|
||||
class OSGSHADOW_EXPORT MinimalShadowMap : public StandardShadowMap
|
||||
{
|
||||
public :
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef MinimalShadowMap ThisClass;
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef StandardShadowMap BaseClass;
|
||||
|
||||
/** Classic OSG constructor */
|
||||
MinimalShadowMap();
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
MinimalShadowMap(
|
||||
const MinimalShadowMap& msm,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, MinimalShadowMap );
|
||||
|
||||
void setModellingSpaceToWorldTransform( const osg::Matrix & modellingSpaceToWorld )
|
||||
{ _modellingSpaceToWorld = modellingSpaceToWorld; }
|
||||
|
||||
const osg::Matrix & getModellingSpaceToWorldTransform( void )
|
||||
{ return _modellingSpaceToWorld; }
|
||||
|
||||
float getMaxFarPlane( )
|
||||
{ return _maxFarPlane; }
|
||||
|
||||
void setMaxFarPlane( float maxFarPlane )
|
||||
{ _maxFarPlane = maxFarPlane; }
|
||||
|
||||
float getMinLightMargin( )
|
||||
{ return _minLightMargin; }
|
||||
|
||||
void setMinLightMargin( float minLightMargin )
|
||||
{ _minLightMargin = minLightMargin; }
|
||||
|
||||
enum ShadowReceivingCoarseBoundAccuracy {
|
||||
EMPTY_BOX,
|
||||
BOUNDING_SPHERE,
|
||||
BOUNDING_BOX,
|
||||
DEFAULT_ACCURACY = BOUNDING_BOX,
|
||||
};
|
||||
|
||||
void setShadowReceivingCoarseBoundAccuracy
|
||||
( ShadowReceivingCoarseBoundAccuracy accuracy )
|
||||
{ _shadowReceivingCoarseBoundAccuracy = accuracy; }
|
||||
|
||||
ShadowReceivingCoarseBoundAccuracy
|
||||
getShadowReceivingCoarseBoundAccuracy()
|
||||
{ return _shadowReceivingCoarseBoundAccuracy; }
|
||||
|
||||
protected:
|
||||
/** Classic protected OSG destructor */
|
||||
virtual ~MinimalShadowMap(void);
|
||||
|
||||
protected:
|
||||
// Matrix modellingSpaceToWorld and its inverse
|
||||
// are used to define Modelling Space where shadowed scene drawables
|
||||
// have minimal (smallest possible extent) bounding boxes.
|
||||
|
||||
// Computing visible shadow range in this space
|
||||
// allows for optimal use of ShadowMap resolution.
|
||||
|
||||
// By default it is set to identity ie computations are in world space.
|
||||
// But it should be set to ElipsoidModel::localToWorld
|
||||
// when scene objects are put on earth ellipsoid surface.
|
||||
|
||||
// Other scenarios are also possible for example when models are
|
||||
// built in XZY space which would require identity matrix with swapped colums
|
||||
|
||||
osg::Matrix _modellingSpaceToWorld;
|
||||
float _maxFarPlane;
|
||||
float _minLightMargin;
|
||||
ShadowReceivingCoarseBoundAccuracy _shadowReceivingCoarseBoundAccuracy;
|
||||
|
||||
struct ViewData: public BaseClass::ViewData
|
||||
{
|
||||
osg::Matrix *_modellingSpaceToWorldPtr;
|
||||
float *_maxFarPlanePtr;
|
||||
float *_minLightMarginPtr;
|
||||
|
||||
ConvexPolyhedron _sceneReceivingShadowPolytope;
|
||||
std::vector< osg::Vec3d > _sceneReceivingShadowPolytopePoints;
|
||||
|
||||
osg::Matrix _clampedProjection;
|
||||
|
||||
virtual void init( ThisClass * st, osgUtil::CullVisitor * cv );
|
||||
|
||||
virtual osg::BoundingBox computeShadowReceivingCoarseBounds( );
|
||||
|
||||
virtual void cullShadowReceivingScene( );
|
||||
|
||||
virtual void aimShadowCastingCamera( const osg::Light *light,
|
||||
const osg::Vec4 &worldLightPos,
|
||||
const osg::Vec3 &worldLightDir,
|
||||
const osg::Vec3 &worldLightUp
|
||||
= osg::Vec3(0,1,0) );
|
||||
|
||||
virtual void frameShadowCastingCamera
|
||||
( const osg::Camera* cameraMain, osg::Camera* cameraShadow, int pass = 1 );
|
||||
|
||||
void cutScenePolytope( const osg::Matrix & matrix,
|
||||
const osg::Matrix & inverse,
|
||||
const osg::BoundingBox &bb =
|
||||
osg::BoundingBox(-1,-1,-1,1,1,1) );
|
||||
|
||||
osg::BoundingBox computeScenePolytopeBounds
|
||||
( const osg::Matrix & m = *(osg::Matrix*)(NULL) );
|
||||
|
||||
// Utility methods for adjusting projection matrices
|
||||
|
||||
// Modify projection matrix so that some output subrange
|
||||
// is remapped to whole clip space (-1..1,-1..1,-1..1).
|
||||
// Bit mask can be used to limit remaping to selected bounds only.
|
||||
static void trimProjection
|
||||
( osg::Matrix & projection, osg::BoundingBox subrange,
|
||||
unsigned int trimMask = (1|2|4|8|16|32)
|
||||
/*1=left|2=right|4=bottom|8=top|16=near|32=far*/);
|
||||
|
||||
static void clampProjection
|
||||
( osg::Matrix & projection, float n = 0, float f = FLT_MAX );
|
||||
|
||||
static void extendProjection
|
||||
( osg::Matrix & projection, osg::Viewport * viewport, const osg::Vec2& margin );
|
||||
};
|
||||
|
||||
META_ViewDependentShadowTechniqueData( ThisClass, ThisClass::ViewData )
|
||||
};
|
||||
|
||||
} // namespace osgShadow
|
||||
|
||||
#endif
|
82
include/osgShadow/ProjectionShadowMap
Normal file
82
include/osgShadow/ProjectionShadowMap
Normal file
@ -0,0 +1,82 @@
|
||||
/* -*-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.
|
||||
*/
|
||||
|
||||
#ifndef OSGSHADOW_PROJECTIONSHADOWMAP
|
||||
#define OSGSHADOW_PROJECTIONSHADOWMAP 1
|
||||
|
||||
#include <osgShadow/MinimalShadowMap>
|
||||
|
||||
namespace osgShadow {
|
||||
|
||||
template< typename MinimalBoundsBaseClass, typename ShadowProjectionAlgorithmClass >
|
||||
class OSGSHADOW_EXPORT ProjectionShadowMap : public MinimalBoundsBaseClass
|
||||
{
|
||||
public :
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef MinimalBoundsBaseClass BaseClass;
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef ProjectionShadowMap<MinimalBoundsBaseClass,ShadowProjectionAlgorithmClass> ThisClass;
|
||||
|
||||
/** Classic OSG constructor */
|
||||
ProjectionShadowMap() : BaseClass()
|
||||
{
|
||||
}
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
ProjectionShadowMap(
|
||||
const ProjectionShadowMap& copy,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY) : BaseClass(copy,copyop)
|
||||
{
|
||||
}
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, ProjectionShadowMap );
|
||||
|
||||
protected:
|
||||
|
||||
/** Classic protected OSG destructor */
|
||||
virtual ~ProjectionShadowMap(void)
|
||||
{
|
||||
}
|
||||
|
||||
struct ViewData: public BaseClass::ViewData,
|
||||
public ShadowProjectionAlgorithmClass
|
||||
{
|
||||
#if 0
|
||||
virtual void init( ThisClass * st, osgUtil::CullVisitor * cv );
|
||||
{
|
||||
BaseClass::ViewData::init( st, cv );
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void frameShadowCastingCamera
|
||||
( const osg::Camera* cameraMain, osg::Camera* cameraShadow, int pass = 1 )
|
||||
{
|
||||
// Force dependent name lookup
|
||||
ShadowProjectionAlgorithmClass::operator()
|
||||
( &this->_sceneReceivingShadowPolytope, cameraMain, cameraShadow );
|
||||
|
||||
// DebugBoundingBox( computeScenePolytopeBounds(), "ProjectionShadowMap" );
|
||||
BaseClass::ViewData::frameShadowCastingCamera( cameraMain, cameraShadow, pass );
|
||||
}
|
||||
};
|
||||
|
||||
META_ViewDependentShadowTechniqueData( ThisClass, typename ThisClass::ViewData )
|
||||
};
|
||||
|
||||
} // namespace osgShadow
|
||||
|
||||
#endif
|
195
include/osgShadow/StandardShadowMap
Normal file
195
include/osgShadow/StandardShadowMap
Normal file
@ -0,0 +1,195 @@
|
||||
/* -*-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.
|
||||
*/
|
||||
|
||||
#ifndef OSGSHADOW_STANDARDSHADOWMAP
|
||||
#define OSGSHADOW_STANDARDSHADOWMAP 1
|
||||
|
||||
#include <osgShadow/DebugShadowMap>
|
||||
|
||||
namespace osgShadow {
|
||||
|
||||
class OSGSHADOW_EXPORT StandardShadowMap : public DebugShadowMap
|
||||
{
|
||||
public :
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef StandardShadowMap ThisClass;
|
||||
/** Convenient typedef used in definition of ViewData struct and methods */
|
||||
typedef DebugShadowMap BaseClass;
|
||||
|
||||
/** Classic OSG constructor */
|
||||
StandardShadowMap();
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
StandardShadowMap(const StandardShadowMap& ssm,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, StandardShadowMap );
|
||||
|
||||
void setBaseTextureUnit( unsigned int unit )
|
||||
{ _baseTextureUnit = unit; dirty(); }
|
||||
|
||||
unsigned int getBaseTextureUnit( void )
|
||||
{ return _baseTextureUnit; }
|
||||
|
||||
void setShadowTextureUnit( unsigned int unit )
|
||||
{ _shadowTextureUnit = unit; dirty(); }
|
||||
|
||||
unsigned int getShadowTextureUnit( void )
|
||||
{ return _shadowTextureUnit; }
|
||||
|
||||
// Texture Indices are changed by search and replace on shader source
|
||||
// Carefully order these calls when changing both base and shadow indices
|
||||
// In worst case when intend to swap indices
|
||||
// one will have to call these methods more than once
|
||||
// with one extra pass to change index to unused value to avoid
|
||||
// unwanted superfluous replace:
|
||||
//
|
||||
// Example: Imagine we want to swap base(0) and shadow(1) indices:
|
||||
// We have to do an extra step to make sure both do not end up as 1
|
||||
//
|
||||
// // initialy change base to something else than 1
|
||||
// setBaseTextureCoordIndex( 100 );
|
||||
// // now search and replace all gl_TexCord[1] to gl_TexCord[0]
|
||||
// setShadowTextureCoordIndex( 0 );
|
||||
// // finally change base from 100 to 0
|
||||
// setBaseTextureCoordIndex( 1 );
|
||||
|
||||
void setBaseTextureCoordIndex( unsigned int index )
|
||||
{ updateTextureCoordIndices( _baseTextureCoordIndex, index );
|
||||
_baseTextureCoordIndex = index; }
|
||||
|
||||
unsigned int getBaseTextureCoordIndex( void )
|
||||
{ return _baseTextureCoordIndex; }
|
||||
|
||||
// Texture Indices are changed by search and replace on shader source
|
||||
// Look at the comment above setBaseTextureCoordIndex
|
||||
|
||||
void setShadowTextureCoordIndex( unsigned int index )
|
||||
{ updateTextureCoordIndices( _shadowTextureCoordIndex, index );
|
||||
_shadowTextureCoordIndex = index; }
|
||||
|
||||
unsigned int getShadowTextureCoordIndex( void )
|
||||
{ return _shadowTextureCoordIndex; }
|
||||
|
||||
void setTextureSize( const osg::Vec2s& textureSize )
|
||||
{ _textureSize = textureSize; dirty(); }
|
||||
|
||||
osg::Vec2s getTextureSize( )
|
||||
{ return _textureSize; }
|
||||
|
||||
void setLight( osg::Light* light )
|
||||
{ _light = light; }
|
||||
|
||||
osg::Light* getLight( void )
|
||||
{ return _light.get(); }
|
||||
|
||||
osg::Shader * getShadowVertexShader()
|
||||
{ return _shadowVertexShader.get(); }
|
||||
|
||||
osg::Shader * getShadowFragmentShader()
|
||||
{ return _shadowFragmentShader.get(); }
|
||||
|
||||
osg::Shader * getMainVertexShader( )
|
||||
{ return _mainVertexShader.get(); }
|
||||
|
||||
osg::Shader * getMainFragmentShader( )
|
||||
{ return _mainFragmentShader.get(); }
|
||||
|
||||
void setShadowVertexShader( osg::Shader * shader )
|
||||
{ _shadowVertexShader = shader; }
|
||||
|
||||
void setShadowFragmentShader( osg::Shader * shader )
|
||||
{ _shadowFragmentShader = shader; }
|
||||
|
||||
void setMainVertexShader( osg::Shader * shader )
|
||||
{ _mainVertexShader = shader; }
|
||||
|
||||
void setMainFragmentShader( osg::Shader * shader )
|
||||
{ _mainFragmentShader = shader; }
|
||||
|
||||
protected:
|
||||
/** Classic protected OSG destructor */
|
||||
virtual ~StandardShadowMap(void);
|
||||
|
||||
virtual void updateTextureCoordIndices
|
||||
( unsigned int baseTexCoordIndex, unsigned int shadowTexCoordIndex );
|
||||
|
||||
virtual void searchAndReplaceShaderSource
|
||||
( osg::Shader*, std::string fromString, std::string toString );
|
||||
|
||||
osg::ref_ptr< osg::Shader > _mainVertexShader;
|
||||
osg::ref_ptr< osg::Shader > _mainFragmentShader;
|
||||
osg::ref_ptr< osg::Shader > _shadowVertexShader;
|
||||
osg::ref_ptr< osg::Shader > _shadowFragmentShader;
|
||||
|
||||
osg::ref_ptr< osg::Light > _light;
|
||||
float _polygonOffsetFactor;
|
||||
float _polygonOffsetUnits;
|
||||
osg::Vec2s _textureSize;
|
||||
unsigned int _baseTextureUnit;
|
||||
unsigned int _shadowTextureUnit;
|
||||
unsigned int _baseTextureCoordIndex;
|
||||
unsigned int _shadowTextureCoordIndex;
|
||||
|
||||
struct ViewData: public BaseClass::ViewData
|
||||
{
|
||||
osg::ref_ptr< osg::Light > * _lightPtr;
|
||||
unsigned int * _baseTextureUnitPtr;
|
||||
unsigned int * _shadowTextureUnitPtr;
|
||||
|
||||
// ShadowMap texture is defined by base DebugShadowMap
|
||||
// osg::ref_ptr<osg::Texture2D> _texture;
|
||||
|
||||
// ShadowMap camera is defined by base DebugShadowMap
|
||||
// osg::ref_ptr<osg::Camera> _camera;
|
||||
|
||||
osg::ref_ptr<osg::TexGen> _texgen;
|
||||
osg::ref_ptr<osg::StateSet> _stateset;
|
||||
|
||||
virtual void init( ThisClass * st, osgUtil::CullVisitor * cv );
|
||||
|
||||
virtual void cull( );
|
||||
|
||||
virtual void aimShadowCastingCamera(
|
||||
const osg::BoundingSphere &bounds,
|
||||
const osg::Light *light,
|
||||
const osg::Vec4 &worldLightPos,
|
||||
const osg::Vec3 &worldLightDir,
|
||||
const osg::Vec3 &worldLightUp = osg::Vec3(0,1,0) );
|
||||
|
||||
virtual void cullShadowReceivingScene( );
|
||||
|
||||
virtual void cullShadowCastingScene( );
|
||||
|
||||
virtual void addShadowReceivingTexGen( );
|
||||
|
||||
virtual const osg::Light* selectLight( osg::Vec4 &viewLightPos,
|
||||
osg::Vec3 &viewLightDir );
|
||||
|
||||
virtual void aimShadowCastingCamera( const osg::Light *light,
|
||||
const osg::Vec4 &worldLightPos,
|
||||
const osg::Vec3 &worldLightDir,
|
||||
const osg::Vec3 &worldLightUp
|
||||
= osg::Vec3(0,1,0) );
|
||||
};
|
||||
|
||||
META_ViewDependentShadowTechniqueData( ThisClass, ThisClass::ViewData )
|
||||
};
|
||||
|
||||
} // namespace osgShadow
|
||||
|
||||
#endif
|
229
include/osgShadow/ViewDependentShadowTechnique
Normal file
229
include/osgShadow/ViewDependentShadowTechnique
Normal file
@ -0,0 +1,229 @@
|
||||
/* -*-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.
|
||||
*/
|
||||
|
||||
#ifndef OSGSHADOW_VIEWDEPENDENTSHADOWTECHINIQUE
|
||||
#define OSGSHADOW_VIEWDEPENDENTSHADOWTECHINIQUE 1
|
||||
|
||||
#include <osgShadow/ShadowTechnique>
|
||||
#include <map>
|
||||
#include <osgShadow/Export>
|
||||
|
||||
namespace osgShadow {
|
||||
/**
|
||||
META_ViewDependentShadowTechniqueData macro defines initViewDependentData
|
||||
method used by derived shadow techniques to initialize their specific
|
||||
ViewData objects. initViewDependentData will be called from
|
||||
ViewDependentShadowTechnique base class to init derived class
|
||||
*/
|
||||
#define META_ViewDependentShadowTechniqueData( ShadowTechnique, TechniqueData )\
|
||||
virtual ViewDependentShadowTechnique::ViewData * initViewDependentData \
|
||||
( osgUtil::CullVisitor *cv, ViewDependentShadowTechnique::ViewData * vd ) \
|
||||
{ \
|
||||
TechniqueData* td = dynamic_cast<TechniqueData*>( vd ); \
|
||||
if ( !td ) td = new TechniqueData; \
|
||||
td->init( this, cv ); \
|
||||
return td; \
|
||||
}
|
||||
|
||||
/**
|
||||
ViewDependentShadowTechnique is a base class for all
|
||||
View Dependent Shadow techniques. It defines fundamental object structure
|
||||
and methods to manage separate shadow resources for each view of the scene.
|
||||
By view we understand osg::View or SceneView instance and their associated
|
||||
Camera. Typical osg application has one or more such views. View Dependent
|
||||
Shadow techniques manage shadow generation for them.
|
||||
|
||||
View Dependent Shadow techniques are used to optimize shadow algorithms for
|
||||
part of the scene seen on the view. If rest of the scene is behind view
|
||||
frustum, there is no sense in computing shadows for it. Since in practice we
|
||||
often use 3d databases extending far beyond current camera frustum View
|
||||
Dependent Shadow approach may produce much better shadows.
|
||||
|
||||
The other goal is to provide framework for thread safe rendering of
|
||||
the shadows. It allows to use shadows with different OSG threading models.
|
||||
|
||||
Conceptually ViewDependentShadowTechnique is similar to osgSim::OverlayNode.
|
||||
Its a container class for number of ViewData (or ViewData derived) objects
|
||||
doing actual shadow work for each of the scene views.
|
||||
|
||||
But ViewDependentShadowTechnique is intended as foundation layer for all
|
||||
derived classes so in some way it extends osgSim::OverlayNode approach a bit.
|
||||
|
||||
HOW IT WORKS:
|
||||
|
||||
ViewDependendentShadowTechnique is derived from osgShadow::ShadowTechnique
|
||||
and as such overrides virtual methods of osgShadow::ShadowTechnique.
|
||||
But most of the shadow dirty work is done by ViewData objects,
|
||||
ViewDependendentShadowTechnique::cull is the only osgShadow::ShadowTechnique
|
||||
method where ViewDependendentShadowTechnique does something significant:
|
||||
|
||||
What ViewDependentShadowTechnique::cull( CullVisitor & cv ) does ?
|
||||
It identifies View. CullVisitor ptr is used as View identificator.
|
||||
In practice we do not check and interpret what are actual Views and SceneViews
|
||||
set up by application. We focus on Camera and CullVisitors as a identificators
|
||||
of views. We can safely do this because each such view will have associated
|
||||
unique CullVisitor used to cull the scene in every frame.
|
||||
|
||||
Based on CullVisitor ptr passed to cull method, associated Technique::ViewData
|
||||
object is created (if neccessary) and then seleced. Then control is passed to
|
||||
this ViewData object. So, each view has its associated unique ViewData
|
||||
(or derived) object performing dirty work of shadow resources management and
|
||||
shadow generation for the view.
|
||||
|
||||
To support creation of classes derived from ViewDependentShadowTechnique it
|
||||
was neccessary to provide mechanism to override ViewData and allow for
|
||||
initialization of new derived ViewData objects. Creation and initialization
|
||||
is performed when ViewDependendentShadowTechnique::cull gets called with
|
||||
CullVistor ptr which does not yet have associated ViewData object. When it
|
||||
happens, virtual initViewDependentData method is called to give
|
||||
derived techniques a chance to allocate and iniitalize its specific
|
||||
resources as new ViewData derived instance. In practice initViewDependentData
|
||||
in derived techniques should look the same as in base class so as a convenience
|
||||
it was defined as META_ViewDependentShadowTechnique macro. Derived techniques
|
||||
use this macro to override initViewDependentData method for their usage.
|
||||
|
||||
After ViewData derived object is construted and selected, control is passed
|
||||
to this object by call to virtual ViewData::cull method. The rest of work
|
||||
is the done by this object. ViewDependentShadowTechnique::ViewData is intended
|
||||
as a base class so it does nothing. In practice the rest of dirty work will
|
||||
do new ViewData classes implemented in derived techniques.
|
||||
*/
|
||||
class OSGSHADOW_EXPORT ViewDependentShadowTechnique
|
||||
: public osgShadow::ShadowTechnique
|
||||
{
|
||||
public:
|
||||
/**
|
||||
osgShadow::ShadowTechnique equivalent methods for view dependent techniques
|
||||
*/
|
||||
|
||||
/** Classic OSG constructor */
|
||||
ViewDependentShadowTechnique( void );
|
||||
|
||||
/** Classic OSG cloning constructor */
|
||||
ViewDependentShadowTechnique(
|
||||
const ViewDependentShadowTechnique& vdst,
|
||||
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY );
|
||||
|
||||
|
||||
/** Declaration of standard OSG object methods */
|
||||
META_Object( ViewDependentShadow, ViewDependentShadowTechnique );
|
||||
|
||||
/** Dirty view data bits and force update of view data resources */
|
||||
virtual void dirty();
|
||||
|
||||
/** Initialize the ShadowedScene and some data structures.*/
|
||||
virtual void init();
|
||||
|
||||
/** Run the update traversal of the ShadowedScene and update any local cached data structures.*/
|
||||
virtual void update(osg::NodeVisitor& nv);
|
||||
|
||||
/** Run the cull traversal of the ShadowedScene and set up the rendering for this ShadowTechnique.*/
|
||||
virtual void cull(osgUtil::CullVisitor& cv);
|
||||
|
||||
/** Clean scene graph from any shadow technique specific nodes, state and drawables.*/
|
||||
virtual void cleanSceneGraph();
|
||||
|
||||
/** Traverse shadow scene graph.*/
|
||||
virtual void traverse(osg::NodeVisitor& nv);
|
||||
|
||||
protected:
|
||||
/** Classic protected OSG destructor */
|
||||
~ViewDependentShadowTechnique( void );
|
||||
|
||||
/**
|
||||
Base container class for view dependent shadow resources.
|
||||
Techniques based on ViewDependentShadowTechnique will usually define
|
||||
similar struct and derive it from ViewData to contain their specufic resources.
|
||||
*/
|
||||
struct ViewData: public osg::Referenced
|
||||
{
|
||||
/**
|
||||
Method called upon ViewData instance to initialize internal variables
|
||||
*/
|
||||
virtual void init
|
||||
( ViewDependentShadowTechnique *st, osgUtil::CullVisitor *cv );
|
||||
|
||||
/**
|
||||
Method called by ViewDependentShadowTechnique to allow ViewData
|
||||
do the hard work computing shadows for its associated view
|
||||
*/
|
||||
virtual void cull();
|
||||
|
||||
/**
|
||||
Dirty is called by parent ViewDependentShadowTechnique to force
|
||||
update of resources after some of them were modified in parent technique
|
||||
*/
|
||||
virtual void dirty( bool flag );
|
||||
|
||||
/**
|
||||
Simple constructor zeroing all variables.
|
||||
*/
|
||||
ViewData(): _dirty( true ), _cv( NULL ), _st( NULL ) { };
|
||||
|
||||
/**
|
||||
Mutex used to guard _dirty flag from override in case when parent technique calls
|
||||
dirty() simultaneously with ViewData while it is updating resources inside init method.
|
||||
*/
|
||||
OpenThreads::Mutex _mutex;
|
||||
|
||||
/**
|
||||
View's CullVisitor associated with this ViewData instance
|
||||
*/
|
||||
osg::observer_ptr< osgUtil::CullVisitor > _cv;
|
||||
|
||||
/**
|
||||
Parent ViewDependentShadowTechnique
|
||||
*/
|
||||
osg::observer_ptr< ViewDependentShadowTechnique > _st;
|
||||
|
||||
/**
|
||||
Dirty flag tells this instance to update its resources
|
||||
*/
|
||||
bool _dirty;
|
||||
};
|
||||
|
||||
/**
|
||||
Map of view dependent data per view cull visitor (CVs are used as indices)
|
||||
ViewDependentShadowTechnique uses this map to find VieData for each cull vitior
|
||||
*/
|
||||
|
||||
typedef std::map< osg::ref_ptr< osgUtil::CullVisitor >,
|
||||
osg::ref_ptr< ViewData > > ViewDataMap;
|
||||
|
||||
ViewDataMap _viewDataMap;
|
||||
|
||||
|
||||
/**
|
||||
Mutex used to serialize accesses to ViewDataMap
|
||||
*/
|
||||
OpenThreads::Mutex _viewDataMapMutex;
|
||||
|
||||
/** Return view dependent data for the cull visitor */
|
||||
virtual ViewDependentShadowTechnique::ViewData * getViewDependentData( osgUtil::CullVisitor * cv );
|
||||
|
||||
/** Define view dependent data for the cull visitor */
|
||||
virtual void setViewDependentData( osgUtil::CullVisitor * cv, ViewDependentShadowTechnique::ViewData * data );
|
||||
|
||||
/**
|
||||
Declare standard initViewDependentData method.
|
||||
*/
|
||||
META_ViewDependentShadowTechniqueData( ViewDependentShadowTechnique, ViewData )
|
||||
};
|
||||
|
||||
|
||||
} // namespace osgShadow
|
||||
|
||||
#endif
|
@ -18,6 +18,16 @@ SET(LIB_PUBLIC_HEADERS
|
||||
${HEADER_PATH}/SoftShadowMap
|
||||
${HEADER_PATH}/ParallelSplitShadowMap
|
||||
${HEADER_PATH}/Version
|
||||
|
||||
${HEADER_PATH}/ConvexPolyhedron
|
||||
${HEADER_PATH}/DebugShadowMap
|
||||
${HEADER_PATH}/LightSpacePerspectiveShadowMap
|
||||
${HEADER_PATH}/MinimalCullBoundsShadowMap
|
||||
${HEADER_PATH}/MinimalDrawBoundsShadowMap
|
||||
${HEADER_PATH}/MinimalShadowMap
|
||||
${HEADER_PATH}/ProjectionShadowMap
|
||||
${HEADER_PATH}/StandardShadowMap
|
||||
${HEADER_PATH}/ViewDependentShadowTechnique
|
||||
)
|
||||
|
||||
# FIXME: For OS X, need flag for Framework or dylib
|
||||
@ -33,6 +43,14 @@ ADD_LIBRARY(${LIB_NAME}
|
||||
SoftShadowMap.cpp
|
||||
ParallelSplitShadowMap.cpp
|
||||
Version.cpp
|
||||
ConvexPolyhedron.cpp
|
||||
DebugShadowMap.cpp
|
||||
LightSpacePerspectiveShadowMap.cpp
|
||||
MinimalCullBoundsShadowMap.cpp
|
||||
MinimalDrawBoundsShadowMap.cpp
|
||||
MinimalShadowMap.cpp
|
||||
StandardShadowMap.cpp
|
||||
ViewDependentShadowTechnique.cpp
|
||||
)
|
||||
|
||||
LINK_INTERNAL(${LIB_NAME}
|
||||
|
1876
src/osgShadow/ConvexPolyhedron.cpp
Normal file
1876
src/osgShadow/ConvexPolyhedron.cpp
Normal file
File diff suppressed because it is too large
Load Diff
589
src/osgShadow/DebugShadowMap.cpp
Normal file
589
src/osgShadow/DebugShadowMap.cpp
Normal file
@ -0,0 +1,589 @@
|
||||
/* -*-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/DebugShadowMap>
|
||||
#include <osgShadow/ConvexPolyhedron>
|
||||
#include <osgUtil/RenderLeaf>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Depth>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace osgShadow;
|
||||
|
||||
|
||||
#define VECTOR_LENGTH( v ) ( sizeof(v)/sizeof(v[0]) )
|
||||
|
||||
#define DEFAULT_DEBUG_HUD_SIZE_X 256
|
||||
#define DEFAULT_DEBUG_HUD_SIZE_Y 256
|
||||
#define DEFAULT_DEBUG_HUD_ORIGIN_X 8
|
||||
#define DEFAULT_DEBUG_HUD_ORIGIN_Y 8
|
||||
|
||||
DebugShadowMap::DebugShadowMap():
|
||||
BaseClass(),
|
||||
_doDebugDraw( false ),
|
||||
_hudSize( 2, 2 ),
|
||||
_hudOrigin( -1, -1 ),
|
||||
_viewportSize( DEFAULT_DEBUG_HUD_SIZE_X, DEFAULT_DEBUG_HUD_SIZE_Y ),
|
||||
_viewportOrigin( DEFAULT_DEBUG_HUD_ORIGIN_X, DEFAULT_DEBUG_HUD_ORIGIN_Y ),
|
||||
_orthoSize( 2, 2 ),
|
||||
_orthoOrigin( -1, -1 )
|
||||
{
|
||||
|
||||
// Why this fancy 24 bit depth to 24 bit rainbow colors shader ?
|
||||
//
|
||||
// Depth values cannot be easily cast on color component because they are:
|
||||
// a) 24 or 32 bit and we loose lots of precision when cast on 8 bit
|
||||
// b) depth value distribution is non linear due to projection division
|
||||
// when cast on componenent color there is usually very minor shade variety
|
||||
// and its often difficult to notice that there is anything in the buffer
|
||||
//
|
||||
// Shader looks complex but it is used only for debug head-up rectangle
|
||||
// so performance impact is not significant.
|
||||
|
||||
_depthColorFragmentShader = new osg::Shader( osg::Shader::FRAGMENT,
|
||||
#if 0
|
||||
"uniform sampler2D texture; \n"
|
||||
" \n"
|
||||
"void main(void) \n"
|
||||
"{ \n"
|
||||
" float f = texture2D( texture, vec3( gl_TexCoord[0].xy, 1.0).xy ).r; \n"
|
||||
" gl_FragColor = vec4( 0.0, 1.0 - f, 0.5 - f, 0.5 ); \n"
|
||||
"} \n"
|
||||
#else
|
||||
"uniform sampler2D texture; \n"
|
||||
" \n"
|
||||
"void main(void) \n"
|
||||
"{ \n"
|
||||
" float f = texture2D( texture, vec3( gl_TexCoord[0].xy, 1.0).xy ).r; \n"
|
||||
" \n"
|
||||
" f = 256.0 * f; \n"
|
||||
" float fC = floor( f ) / 256.0; \n"
|
||||
" \n"
|
||||
" f = 256.0 * fract( f ); \n"
|
||||
" float fS = floor( f ) / 256.0; \n"
|
||||
" \n"
|
||||
" f = 256.0 * fract( f ); \n"
|
||||
" float fH = floor( f ) / 256.0; \n"
|
||||
" \n"
|
||||
" fS *= 0.5; \n"
|
||||
" fH = ( fH * 0.34 + 0.66 ) * ( 1.0 - fS ); \n"
|
||||
" \n"
|
||||
" vec3 rgb = vec3( ( fC > 0.5 ? ( 1.0 - fC ) : fC ), \n"
|
||||
" abs( fC - 0.333333 ), \n"
|
||||
" abs( fC - 0.666667 ) ); \n"
|
||||
" \n"
|
||||
" rgb = min( vec3( 1.0, 1.0, 1.0 ), 3.0 * rgb ); \n"
|
||||
" \n"
|
||||
" float fMax = max( max( rgb.r, rgb.g ), rgb.b ); \n"
|
||||
" fMax = 1.0 / fMax; \n"
|
||||
" \n"
|
||||
" vec3 color = fMax * rgb; \n"
|
||||
" \n"
|
||||
" gl_FragColor = vec4( fS + fH * color, 1 ) * gl_Color; \n"
|
||||
"} \n"
|
||||
#endif
|
||||
); // end _depthColorFragmentShader
|
||||
}
|
||||
|
||||
DebugShadowMap::DebugShadowMap
|
||||
(const DebugShadowMap& copy, const osg::CopyOp& copyop) :
|
||||
BaseClass(copy,copyop),
|
||||
_doDebugDraw( copy._doDebugDraw ),
|
||||
_hudSize( copy._hudSize ),
|
||||
_hudOrigin( copy._hudOrigin ),
|
||||
_viewportSize( copy._viewportSize ),
|
||||
_viewportOrigin( copy._viewportOrigin ),
|
||||
_orthoSize( copy._viewportOrigin ),
|
||||
_orthoOrigin( copy._viewportOrigin )
|
||||
{
|
||||
if( copy._depthColorFragmentShader.valid() )
|
||||
_depthColorFragmentShader =
|
||||
dynamic_cast<osg::Shader*>
|
||||
( copy._depthColorFragmentShader->clone(copyop) );
|
||||
}
|
||||
|
||||
DebugShadowMap::~DebugShadowMap()
|
||||
{
|
||||
}
|
||||
|
||||
void DebugShadowMap::ViewData::cull( void )
|
||||
{
|
||||
if( getDebugDraw() && !_cameraDebugHUD.valid() )
|
||||
createDebugHUD();
|
||||
|
||||
BaseClass::ViewData::cull( );
|
||||
|
||||
cullDebugGeometry( );
|
||||
}
|
||||
|
||||
bool DebugShadowMap::ViewData::DebugBoundingBox
|
||||
( const osg::BoundingBox & bb, const char * name )
|
||||
{
|
||||
bool result = false;
|
||||
#if defined( _DEBUG ) || defined( DEBUG )
|
||||
if( !name ) name = "";
|
||||
|
||||
osg::BoundingBox & bb_prev = _boundingBoxMap[ std::string( name ) ];
|
||||
|
||||
result = bb.center() != bb_prev.center() || bb.radius() != bb_prev.radius();
|
||||
if( result )
|
||||
std::cout << "Box<" << name << "> ("
|
||||
<< ( bb._max._v[0] + bb._min._v[0] ) * 0.5 << " "
|
||||
<< ( bb._max._v[1] + bb._min._v[1] ) * 0.5 << " "
|
||||
<< ( bb._max._v[2] + bb._min._v[2] ) * 0.5 << ") ["
|
||||
<< ( bb._max._v[0] - bb._min._v[0] ) << " "
|
||||
<< ( bb._max._v[1] - bb._min._v[1] ) << " "
|
||||
<< ( bb._max._v[2] - bb._min._v[2] ) << "] "
|
||||
<< std::endl;
|
||||
|
||||
bb_prev = bb;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DebugShadowMap::ViewData::DebugPolytope
|
||||
( const osg::Polytope & p, const char * name )
|
||||
{
|
||||
bool result = false;
|
||||
#if defined( _DEBUG ) || defined( DEBUG )
|
||||
if( !name ) name = "";
|
||||
|
||||
osg::Polytope & p_prev = _polytopeMap[ std::string( name ) ];
|
||||
|
||||
result = ( p.getPlaneList() != p_prev.getPlaneList() );
|
||||
|
||||
if( result ) {
|
||||
std::cout << "Polytope<" << name
|
||||
<< "> size(" << p.getPlaneList().size() << ")"
|
||||
<< std::endl;
|
||||
|
||||
if( p.getPlaneList().size() == p_prev.getPlaneList().size() ) {
|
||||
for( unsigned i = 0; i < p.getPlaneList().size(); ++i )
|
||||
{
|
||||
if( p.getPlaneList()[i] != p_prev.getPlaneList()[i] )
|
||||
{
|
||||
std::cout << "Plane<" << i
|
||||
<< "> ("
|
||||
<< p.getPlaneList()[i].asVec4()[0] << ", "
|
||||
<< p.getPlaneList()[i].asVec4()[1] << ", "
|
||||
<< p.getPlaneList()[i].asVec4()[2] << ", "
|
||||
<< p.getPlaneList()[i].asVec4()[3] << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p_prev = p;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DebugShadowMap::ViewData::DebugMatrix
|
||||
( const osg::Matrix & m, const char * name )
|
||||
{
|
||||
bool result = false;
|
||||
#if defined( _DEBUG ) || defined( DEBUG )
|
||||
if( !name ) name = "";
|
||||
|
||||
osg::Matrix & m_prev = _matrixMap[ std::string( name ) ];
|
||||
|
||||
result = ( m != m_prev );
|
||||
|
||||
if( result )
|
||||
std::cout << "Matrix<" << name << "> " << std::endl
|
||||
<<"[ " << m(0,0) << " " << m(0,1) << " " << m(0,2) << " " << m(0,3) << " ] " << std::endl
|
||||
<<"[ " << m(1,0) << " " << m(1,1) << " " << m(1,2) << " " << m(1,3) << " ] " << std::endl
|
||||
<<"[ " << m(2,0) << " " << m(2,1) << " " << m(2,2) << " " << m(2,3) << " ] " << std::endl
|
||||
<<"[ " << m(3,0) << " " << m(3,1) << " " << m(3,2) << " " << m(3,3) << " ] " << std::endl;
|
||||
|
||||
m_prev = m;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void DebugShadowMap::ViewData::setDebugPolytope
|
||||
( const char * name, const ConvexPolyhedron & polytope,
|
||||
osg::Vec4 colorOutline, osg::Vec4 colorInside )
|
||||
{
|
||||
if( !getDebugDraw() ) return;
|
||||
|
||||
if( &polytope == NULL ) { // delete
|
||||
PolytopeGeometry & pg = _polytopeGeometryMap[ std::string( name ) ];
|
||||
for( int i = 0; i < VECTOR_LENGTH( pg._geometry ) ; i++ )
|
||||
{
|
||||
if( pg._geometry[i].valid() ) {
|
||||
if( _geode[i].valid() &&
|
||||
_geode[i]->containsDrawable( pg._geometry[i].get() ) )
|
||||
_geode[i]->removeDrawable( pg._geometry[i].get() );
|
||||
|
||||
pg._geometry[i] = NULL;
|
||||
}
|
||||
}
|
||||
_polytopeGeometryMap.erase( std::string( name ) );
|
||||
} else { // update
|
||||
PolytopeGeometry & pg = _polytopeGeometryMap[ std::string( name ) ];
|
||||
|
||||
pg._polytope = polytope;
|
||||
if( colorOutline.a() > 0 )
|
||||
pg._colorOutline = colorOutline;
|
||||
if( colorInside.a() > 0 )
|
||||
pg._colorInside = colorInside;
|
||||
|
||||
for( int i = 0; i < VECTOR_LENGTH( pg._geometry ) ; i++ )
|
||||
{
|
||||
if( !pg._geometry[i].valid() ) {
|
||||
pg._geometry[i] = new osg::Geometry;
|
||||
pg._geometry[i]->setDataVariance( osg::Object::DYNAMIC );
|
||||
pg._geometry[i]->setUseDisplayList( false );
|
||||
pg._geometry[i]->setSupportsDisplayList( false );
|
||||
}
|
||||
|
||||
if( _geode[i].valid() &&
|
||||
!_geode[i]->containsDrawable( pg._geometry[i].get() ) ) {
|
||||
osg::Geode::DrawableList & dl =
|
||||
const_cast< osg::Geode::DrawableList &>
|
||||
( _geode[i]->getDrawableList() );
|
||||
dl.insert( dl.begin(), pg._geometry[i].get() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugShadowMap::ViewData::updateDebugGeometry
|
||||
( const osg::Camera * viewCam, const osg::Camera * shadowCam )
|
||||
{
|
||||
if( !getDebugDraw() ) return;
|
||||
if( _polytopeGeometryMap.empty() ) return;
|
||||
|
||||
const int num = 2; // = VECTOR_LENGTH( PolytopeGeometry::_geometry );
|
||||
|
||||
const osg::Camera *camera[2] = { viewCam, shadowCam };
|
||||
|
||||
osg::Matrix
|
||||
transform[ num ] =
|
||||
{ viewCam->getViewMatrix() *
|
||||
// use near far clamped projection ( precomputed in cullDebugGeometry )
|
||||
( viewCam == _viewCamera ? _viewProjection : viewCam->getProjectionMatrix() ),
|
||||
shadowCam->getViewMatrix() * shadowCam->getProjectionMatrix() },
|
||||
inverse[ num ] =
|
||||
{ osg::Matrix::inverse( transform[0] ),
|
||||
osg::Matrix::inverse( transform[1] ) };
|
||||
|
||||
#if 0
|
||||
ConvexPolyhedron frustum[ num ];
|
||||
for( int i = 0; i < num; i++ ) {
|
||||
frustum[i].setToUnitFrustum( );
|
||||
#if 1
|
||||
frustum[i].transform( inverse[i], transform[i] );
|
||||
#else
|
||||
frustum[i].transform
|
||||
( osg::Matrix::inverse( camera[i]->getProjectionMatrix() ),
|
||||
camera[i]->getProjectionMatrix() );
|
||||
frustum[i].transform
|
||||
( osg::Matrix::inverse( camera[i]->getViewMatrix() ),
|
||||
camera[i]->getViewMatrix() );
|
||||
#endif
|
||||
};
|
||||
#else
|
||||
osg::Polytope frustum[ num ];
|
||||
for( int i = 0; i < num; i++ ) {
|
||||
frustum[i].setToUnitFrustum( );
|
||||
frustum[i].transformProvidingInverse( transform[i] );
|
||||
}
|
||||
#endif
|
||||
|
||||
transform[0] = viewCam->getViewMatrix();
|
||||
inverse[0] = viewCam->getInverseViewMatrix();
|
||||
|
||||
for( PolytopeGeometryMap::iterator itr = _polytopeGeometryMap.begin();
|
||||
itr != _polytopeGeometryMap.end();
|
||||
++itr )
|
||||
{
|
||||
PolytopeGeometry & pg = itr->second;
|
||||
|
||||
for( int i = 0; i < num ; i++ )
|
||||
{
|
||||
|
||||
ConvexPolyhedron cp( pg._polytope );
|
||||
cp.cut( frustum[i] );
|
||||
cp.transform( transform[i], inverse[i] );
|
||||
|
||||
pg._geometry[i] = cp.buildGeometry
|
||||
( pg._colorOutline, pg._colorInside, pg._geometry[i].get() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugShadowMap::ViewData::cullDebugGeometry( )
|
||||
{
|
||||
if( !getDebugDraw() ) return;
|
||||
if( !_camera.valid() ) return;
|
||||
|
||||
// View camera may use clamping projection matrix after traversal.
|
||||
// Since we need to know exact matrix for drawing the frusta,
|
||||
// we have to compute it here in exactly the same way as cull visitor
|
||||
// will after cull traversal completes view camera subgraph.
|
||||
{
|
||||
_viewProjection = *_cv->getProjectionMatrix();
|
||||
_viewCamera = _cv->getRenderStage()->getCamera();
|
||||
|
||||
if( _cv->getComputeNearFarMode() ) {
|
||||
|
||||
// Redo steps from CullVisitor::popProjectionMatrix()
|
||||
// which clamps projection matrix when Camera & Projection
|
||||
// completes traversal of their children
|
||||
|
||||
// We have to do this now manually
|
||||
// because we did not complete camera traversal yet but
|
||||
// we need to know how this clamped projection matrix will be
|
||||
|
||||
_cv->computeNearPlane();
|
||||
|
||||
double n = _cv->getCalculatedNearPlane();
|
||||
double f = _cv->getCalculatedFarPlane();
|
||||
|
||||
if( n < f )
|
||||
_cv->clampProjectionMatrix(_viewProjection, n, f );
|
||||
}
|
||||
}
|
||||
|
||||
updateDebugGeometry( _viewCamera, _camera.get() );
|
||||
|
||||
#if 1 // Add geometries of polytopes to main cam Render Stage
|
||||
_transform[0]->accept( *_cv );
|
||||
#else
|
||||
for( PolytopeGeometryMap::iterator itr = _polytopeGeometryMap.begin();
|
||||
itr != _polytopeGeometryMap.end();
|
||||
++itr )
|
||||
{
|
||||
PolytopeGeometry & pg = itr->second;
|
||||
_cv->pushStateSet( _geode[0]->getStateSet() );
|
||||
_cv->addDrawableAndDepth( pg._geometry[0].get(), NULL, FLT_MAX );
|
||||
_cv->popStateSet( );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add geometries of polytopes to hud cam Render Stage
|
||||
_cameraDebugHUD->accept( *_cv );
|
||||
}
|
||||
|
||||
void DebugShadowMap::ViewData::init( ThisClass *st, osgUtil::CullVisitor *cv )
|
||||
{
|
||||
BaseClass::ViewData::init( st, cv );
|
||||
|
||||
_doDebugDrawPtr = &st->_doDebugDraw;
|
||||
|
||||
_hudSize = st->_hudSize;
|
||||
_hudOrigin = st->_hudOrigin;
|
||||
_viewportSize = st->_viewportSize;
|
||||
_viewportOrigin = st->_viewportOrigin;
|
||||
_orthoSize = st->_orthoSize;
|
||||
_orthoOrigin = st->_orthoOrigin;
|
||||
|
||||
_depthColorFragmentShader = st->_depthColorFragmentShader;
|
||||
|
||||
// create placeholder geodeds for polytope geometries
|
||||
// rest of their initialization will be performed during camera hud init
|
||||
_geode[0] = new osg::Geode;
|
||||
_geode[1] = new osg::Geode;
|
||||
|
||||
_cameraDebugHUD = NULL;//Force debug HUD rebuild ( if needed )
|
||||
}
|
||||
|
||||
// Callback used by debugging hud to display Shadow Map to color buffer
|
||||
// Had to do it this way because OSG does not allow to use
|
||||
// the same GL Texture Id with different glTexParams.
|
||||
// Callback simply turns compare mode off via GL while rendering hud and
|
||||
// restores it before rendering the scene with shadows.
|
||||
|
||||
class DrawableDrawWithDepthShadowComparisonOffCallback:
|
||||
public osg::Drawable::DrawCallback
|
||||
{
|
||||
public:
|
||||
DrawableDrawWithDepthShadowComparisonOffCallback( osg::Texture2D *pTex )
|
||||
: _pTexture( pTex )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void drawImplementation
|
||||
( osg::RenderInfo & ri,const osg::Drawable* drawable ) const
|
||||
{
|
||||
ri.getState()->applyTextureAttribute( 0, _pTexture.get() );
|
||||
|
||||
// Turn off depth comparison mode
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE );
|
||||
|
||||
drawable->drawImplementation(ri);
|
||||
|
||||
// Turn it back on
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB,
|
||||
GL_COMPARE_R_TO_TEXTURE_ARB );
|
||||
}
|
||||
|
||||
osg::ref_ptr< osg::Texture2D > _pTexture;
|
||||
};
|
||||
|
||||
void DebugShadowMap::ViewData::createDebugHUD( )
|
||||
{
|
||||
_cameraDebugHUD = new osg::Camera;
|
||||
|
||||
{ // Make sure default HUD layout makes sense
|
||||
if( _hudSize[0] <= 0 ) _hudSize[0] = DEFAULT_DEBUG_HUD_SIZE_X;
|
||||
if( _hudSize[1] <= 0 ) _hudSize[1] = DEFAULT_DEBUG_HUD_SIZE_Y;
|
||||
|
||||
if( _viewportSize[0] <= 0 ) _viewportSize[0] = _hudSize[0];
|
||||
if( _viewportSize[1] <= 0 ) _viewportSize[1] = _hudSize[1];
|
||||
|
||||
if( _orthoSize[0] <= 0 ) _orthoSize[0] = _viewportSize[0];
|
||||
if( _orthoSize[1] <= 0 ) _orthoSize[1] = _viewportSize[1];
|
||||
}
|
||||
|
||||
{ // Initialize hud camera
|
||||
osg::Camera * camera = _cameraDebugHUD.get();
|
||||
camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||
camera->setViewMatrix(osg::Matrix::identity());
|
||||
camera->setViewport( _viewportOrigin[0], _viewportOrigin[1],
|
||||
_viewportSize[0], _viewportSize[1] );
|
||||
|
||||
camera->setProjectionMatrixAsOrtho(
|
||||
_orthoOrigin[0], _orthoOrigin[0] + _orthoSize[0],
|
||||
_orthoOrigin[1], _orthoOrigin[1] + _orthoSize[1],
|
||||
-10, 10 );
|
||||
|
||||
camera->setClearMask(GL_DEPTH_BUFFER_BIT);
|
||||
camera->setRenderOrder(osg::Camera::POST_RENDER);
|
||||
}
|
||||
|
||||
{ // Add geode and drawable with BaseClass display
|
||||
// create geode to contain hud drawables
|
||||
osg::Geode* geode = new osg::Geode;
|
||||
_cameraDebugHUD->addChild(geode);
|
||||
|
||||
// finally create and attach hud geometry
|
||||
osg::Geometry* geometry = osg::createTexturedQuadGeometry
|
||||
( osg::Vec3(_hudOrigin[0],_hudOrigin[1],0),
|
||||
osg::Vec3(_hudSize[0],0,0),
|
||||
osg::Vec3(0,_hudSize[1],0) );
|
||||
|
||||
osg::StateSet* stateset = _cameraDebugHUD->getOrCreateStateSet();
|
||||
stateset->setTextureAttributeAndModes(0,_texture.get(),osg::StateAttribute::ON );
|
||||
stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
|
||||
// stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
|
||||
stateset->setAttributeAndModes
|
||||
( new osg::Depth( osg::Depth::ALWAYS, 0, 1, false ) );
|
||||
stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
|
||||
|
||||
osg::Program* program = new osg::Program;
|
||||
program->addShader( _depthColorFragmentShader.get() );
|
||||
stateset->setAttribute( program );
|
||||
stateset->addUniform( new osg::Uniform( "texture" , 0 ) );
|
||||
|
||||
geometry->setDrawCallback
|
||||
( new DrawableDrawWithDepthShadowComparisonOffCallback( _texture.get() ) );
|
||||
|
||||
geode->addDrawable( geometry );
|
||||
}
|
||||
|
||||
{ // Create transforms and geodes to manage polytope drawing
|
||||
osg::StateSet * stateset = new osg::StateSet;
|
||||
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||
stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
|
||||
stateset->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::OFF);
|
||||
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
|
||||
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
||||
stateset->setAttribute( new osg::Program() );
|
||||
stateset->setAttributeAndModes
|
||||
( new osg::Depth( osg::Depth::LEQUAL, 0, 1, false ) );
|
||||
|
||||
for( int i = 0; i < 2; i++ ) {
|
||||
_geode[i]->setStateSet( stateset );
|
||||
_transform[i] = new osg::MatrixTransform;
|
||||
_transform[i]->addChild( _geode[i].get() );
|
||||
_transform[i]->setMatrix( osg::Matrix::identity() );
|
||||
_transform[i]->setReferenceFrame( osg::MatrixTransform::ABSOLUTE_RF );
|
||||
}
|
||||
|
||||
_transform[1]->setMatrix
|
||||
( osg::Matrix::translate( 1, 1, 0 ) *
|
||||
osg::Matrix::scale( 0.5, 0.5, 1 ) *
|
||||
osg::Matrix::scale( _hudSize[0], _hudSize[1], 1 ) *
|
||||
osg::Matrix::translate( _hudOrigin[0], _hudOrigin[1], 0 ) );
|
||||
|
||||
_cameraDebugHUD->addChild( _transform[1].get() );
|
||||
}
|
||||
}
|
||||
|
||||
osg::Vec3d DebugShadowMap::ViewData::computeShadowTexelToPixelError
|
||||
( const osg::Matrix & mvpwView,
|
||||
const osg::Matrix & mvpwShadow,
|
||||
const osg::Vec3d & vWorld,
|
||||
const osg::Vec3d & vDelta )
|
||||
{
|
||||
osg::Vec3d vS0 = mvpwShadow * vWorld;
|
||||
osg::Vec3d vS1 = mvpwShadow * ( vWorld + vDelta );
|
||||
|
||||
osg::Vec3d vV0 = mvpwView * vWorld;
|
||||
osg::Vec3d vV1 = mvpwView * ( vWorld + vDelta );
|
||||
|
||||
osg::Vec3d dV = vV1 - vV0;
|
||||
osg::Vec3d dS = vS1 - vS0;
|
||||
|
||||
return osg::Vec3( dS[0] / dV[0], dS[1] / dV[1], dS[2] / dV[2] );
|
||||
}
|
||||
|
||||
void DebugShadowMap::ViewData::displayShadowTexelToPixelErrors
|
||||
( const osg::Camera* viewCamera,
|
||||
const osg::Camera* shadowCamera,
|
||||
const ConvexPolyhedron* hull )
|
||||
{
|
||||
osg::Matrix mvpwMain =
|
||||
viewCamera->getViewMatrix() *
|
||||
viewCamera->getProjectionMatrix() *
|
||||
viewCamera->getViewport()->computeWindowMatrix();
|
||||
|
||||
osg::Matrix mvpwShadow =
|
||||
shadowCamera->getViewMatrix() *
|
||||
shadowCamera->getProjectionMatrix() *
|
||||
shadowCamera->getViewport()->computeWindowMatrix();
|
||||
|
||||
osg::BoundingBox bb =
|
||||
hull->computeBoundingBox( viewCamera->getViewMatrix() );
|
||||
|
||||
osg::Matrix m = viewCamera->getInverseViewMatrix();
|
||||
|
||||
osg::Vec3d vn = osg::Vec3d( 0, 0, bb._max[2] ) * m;
|
||||
osg::Vec3d vf = osg::Vec3d( 0, 0, bb._min[2] ) * m;
|
||||
osg::Vec3d vm = osg::Vec3d( 0, 0, ( bb._max[2] + bb._min[2] ) * 0.5 ) * m;
|
||||
|
||||
osg::Vec3d vne = computeShadowTexelToPixelError( mvpwMain, mvpwShadow, vn );
|
||||
osg::Vec3d vfe = computeShadowTexelToPixelError( mvpwMain, mvpwShadow, vf );
|
||||
osg::Vec3d vme = computeShadowTexelToPixelError( mvpwMain, mvpwShadow, vm );
|
||||
|
||||
std::cout << std::setprecision( 3 ) << " "
|
||||
<< "ne=(" << vne[0] << "," << vne[1] << "," << vne[2] << ") "
|
||||
<< "fe=(" << vfe[0] << "," << vfe[1] << "," << vfe[2] << ") "
|
||||
<< "me=(" << vme[0] << "," << vme[1] << "," << vme[2] << ") "
|
||||
<< "\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"
|
||||
<< "\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"
|
||||
<< "\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;
|
||||
}
|
||||
|
843
src/osgShadow/LightSpacePerspectiveShadowMap.cpp
Normal file
843
src/osgShadow/LightSpacePerspectiveShadowMap.cpp
Normal file
@ -0,0 +1,843 @@
|
||||
/* -*-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
|
368
src/osgShadow/MinimalCullBoundsShadowMap.cpp
Normal file
368
src/osgShadow/MinimalCullBoundsShadowMap.cpp
Normal file
@ -0,0 +1,368 @@
|
||||
/* -*-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/MinimalCullBoundsShadowMap>
|
||||
#include <osgUtil/RenderLeaf>
|
||||
#include <string.h>
|
||||
|
||||
#define IGNORE_OBJECTS_LARGER_THAN_HEIGHT 0
|
||||
|
||||
using namespace osgShadow;
|
||||
|
||||
MinimalCullBoundsShadowMap::MinimalCullBoundsShadowMap(): BaseClass()
|
||||
{
|
||||
}
|
||||
|
||||
MinimalCullBoundsShadowMap::MinimalCullBoundsShadowMap
|
||||
(const MinimalCullBoundsShadowMap& copy, const osg::CopyOp& copyop) :
|
||||
BaseClass(copy,copyop)
|
||||
{
|
||||
}
|
||||
|
||||
MinimalCullBoundsShadowMap::~MinimalCullBoundsShadowMap()
|
||||
{
|
||||
}
|
||||
|
||||
void MinimalCullBoundsShadowMap::ViewData::init
|
||||
( ThisClass *st, osgUtil::CullVisitor *cv )
|
||||
{
|
||||
BaseClass::ViewData::init( st, cv );
|
||||
}
|
||||
|
||||
void MinimalCullBoundsShadowMap::ViewData::aimShadowCastingCamera
|
||||
( const osg::Light *light,
|
||||
const osg::Vec4 &lightPos,
|
||||
const osg::Vec3 &lightDir,
|
||||
const osg::Vec3 &lightUp )
|
||||
{
|
||||
MinimalShadowMap::ViewData::aimShadowCastingCamera
|
||||
( light, lightPos, lightDir, lightUp );
|
||||
|
||||
frameShadowCastingCamera
|
||||
( _cv->getCurrentRenderBin()->getStage()->getCamera(), _camera.get() );
|
||||
}
|
||||
|
||||
void MinimalCullBoundsShadowMap::ViewData::cullShadowReceivingScene( )
|
||||
{
|
||||
RenderLeafList rllOld, rllNew;
|
||||
|
||||
GetRenderLeaves( _cv->getRenderStage(), rllOld );
|
||||
|
||||
MinimalShadowMap::ViewData::cullShadowReceivingScene( );
|
||||
|
||||
GetRenderLeaves( _cv->getRenderStage(), rllNew );
|
||||
|
||||
RemoveOldRenderLeaves( rllNew, rllOld );
|
||||
RemoveIgnoredRenderLeaves( rllNew );
|
||||
|
||||
osg::Matrix projectionToModelSpace =
|
||||
osg::Matrix::inverse( *_modellingSpaceToWorldPtr *
|
||||
*_cv->getModelViewMatrix() * *_cv->getProjectionMatrix() );
|
||||
|
||||
osg::BoundingBox bb;
|
||||
if( *_cv->getProjectionMatrix() != _clampedProjection ) {
|
||||
|
||||
osg::Polytope polytope;
|
||||
#if 1
|
||||
polytope.setToUnitFrustum();
|
||||
#else
|
||||
polytope.add( osg::Plane( 0.0,0.0,-1.0,1.0 ) ); // only far plane
|
||||
#endif
|
||||
polytope.transformProvidingInverse( *_modellingSpaceToWorldPtr *
|
||||
*_cv->getModelViewMatrix() * _clampedProjection );
|
||||
|
||||
bb = ComputeRenderLeavesBounds( rllNew, projectionToModelSpace, polytope );
|
||||
} else {
|
||||
bb = ComputeRenderLeavesBounds( rllNew, projectionToModelSpace );
|
||||
}
|
||||
|
||||
cutScenePolytope( *_modellingSpaceToWorldPtr,
|
||||
osg::Matrix::inverse( *_modellingSpaceToWorldPtr ), bb );
|
||||
}
|
||||
|
||||
void MinimalCullBoundsShadowMap::ViewData::GetRenderLeaves
|
||||
( osgUtil::RenderBin *rb, RenderLeafList & rll )
|
||||
{
|
||||
osgUtil::RenderBin::RenderBinList& bins = rb->getRenderBinList();
|
||||
osgUtil::RenderBin::RenderBinList::const_iterator rbitr;
|
||||
|
||||
// scan pre render bins
|
||||
for(rbitr = bins.begin(); rbitr!=bins.end() && rbitr->first<0; ++rbitr )
|
||||
GetRenderLeaves( rbitr->second.get(), rll );
|
||||
|
||||
// scan fine grained ordering.
|
||||
osgUtil::RenderBin::RenderLeafList& renderLeafList = rb->getRenderLeafList();
|
||||
osgUtil::RenderBin::RenderLeafList::const_iterator rlitr;
|
||||
for( rlitr= renderLeafList.begin(); rlitr!= renderLeafList.end(); ++rlitr )
|
||||
{
|
||||
rll.push_back( *rlitr );
|
||||
}
|
||||
|
||||
// scan coarse grained ordering.
|
||||
osgUtil::RenderBin::StateGraphList& stateGraphList = rb->getStateGraphList();
|
||||
osgUtil::RenderBin::StateGraphList::const_iterator oitr;
|
||||
for( oitr= stateGraphList.begin(); oitr!= stateGraphList.end(); ++oitr )
|
||||
{
|
||||
for( osgUtil::StateGraph::LeafList::const_iterator dw_itr =
|
||||
(*oitr)->_leaves.begin(); dw_itr != (*oitr)->_leaves.end(); ++dw_itr)
|
||||
{
|
||||
rll.push_back( dw_itr->get() );
|
||||
}
|
||||
}
|
||||
|
||||
// scan post render bins
|
||||
for(; rbitr!=bins.end(); ++rbitr )
|
||||
GetRenderLeaves( rbitr->second.get(), rll );
|
||||
}
|
||||
|
||||
class CompareRenderLeavesByMatrices {
|
||||
public:
|
||||
bool operator()( const osgUtil::RenderLeaf *a, const osgUtil::RenderLeaf *b )
|
||||
{
|
||||
if ( !a ) return false; // NULL render leaf goes last
|
||||
return !b ||
|
||||
a->_projection < b->_projection ||
|
||||
a->_projection == b->_projection && a->_modelview < b->_modelview;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool CheckAndMultiplyBoxIfWithinPolytope
|
||||
( osg::BoundingBox & bb, osg::Matrix & m, osg::Polytope &p )
|
||||
{
|
||||
if( !bb.valid() ) return false;
|
||||
|
||||
osg::Vec3 o = bb._min * m, s[3];
|
||||
|
||||
for( int i = 0; i < 3; i ++ )
|
||||
s[i] = osg::Vec3( m(i,0), m(i,1), m(i,2) ) * ( bb._max[i] - bb._min[i] );
|
||||
|
||||
for( osg::Polytope::PlaneList::iterator it = p.getPlaneList().begin();
|
||||
it != p.getPlaneList().end();
|
||||
++it )
|
||||
{
|
||||
float dist = it->distance( o ), dist_min = dist, dist_max = dist;
|
||||
|
||||
for( int i = 0; i < 3; i ++ ) {
|
||||
dist = it->dotProductNormal( s[i] );
|
||||
if( dist < 0 ) dist_min += dist; else dist_max += dist;
|
||||
}
|
||||
|
||||
if( dist_max < 0 )
|
||||
return false;
|
||||
}
|
||||
|
||||
bb._max = bb._min = o;
|
||||
#if 1
|
||||
for( int i = 0; i < 3; i ++ )
|
||||
for( int j = 0; j < 3; j ++ )
|
||||
if( s[i][j] < 0 ) bb._min[j] += s[i][j]; else bb._max[j] += s[i][j];
|
||||
#else
|
||||
b.expandBy( o + s[0] );
|
||||
b.expandBy( o + s[1] );
|
||||
b.expandBy( o + s[2] );
|
||||
b.expandBy( o + s[0] + s[1] );
|
||||
b.expandBy( o + s[0] + s[2] );
|
||||
b.expandBy( o + s[1] + s[2] );
|
||||
b.expandBy( o + s[0] + s[1] + s[2] );
|
||||
#endif
|
||||
|
||||
#if ( IGNORE_OBJECTS_LARGER_THAN_HEIGHT > 0 )
|
||||
if( bb._max[2] - bb._min[2] > IGNORE_OBJECTS_LARGER_THAN_HEIGHT ) // ignore huge objects
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned MinimalCullBoundsShadowMap::ViewData::RemoveOldRenderLeaves
|
||||
( RenderLeafList &rllNew, RenderLeafList &rllOld )
|
||||
{
|
||||
unsigned count = 0;
|
||||
|
||||
std::sort( rllOld.begin(), rllOld.end() );
|
||||
RenderLeafList::iterator itNew, itOld;
|
||||
for( itNew = rllNew.begin(); itNew != rllNew.end() && rllOld.size(); ++itNew )
|
||||
{
|
||||
itOld = std::lower_bound( rllOld.begin(), rllOld.end(), *itNew );
|
||||
|
||||
if( itOld == rllOld.end() || *itOld != *itNew ) continue;
|
||||
// found !
|
||||
rllOld.erase( itOld ); //remove it from old range to speed up search
|
||||
*itNew = NULL; //its not new = invalidate it among new render leaves
|
||||
count ++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
unsigned MinimalCullBoundsShadowMap::ViewData::RemoveIgnoredRenderLeaves
|
||||
( RenderLeafList &rll )
|
||||
{
|
||||
unsigned count = 0;
|
||||
|
||||
for( RenderLeafList::iterator it = rll.begin(); it != rll.end(); ++it )
|
||||
{
|
||||
if( !*it ) continue;
|
||||
|
||||
const char * name = (*it)->_drawable->className();
|
||||
|
||||
// Its a dirty but quick test (not very future proof)
|
||||
if( !name || name[0] != 'L' ) continue;
|
||||
|
||||
if( !strcmp( name, "LightPointDrawable" ) ||
|
||||
!strcmp( name, "LightPointSpriteDrawable" ) )
|
||||
{
|
||||
*it = NULL; //ignored = invalidate this in new render leaves list
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
osg::BoundingBox MinimalCullBoundsShadowMap::ViewData::ComputeRenderLeavesBounds
|
||||
( RenderLeafList &rll, osg::Matrix & projectionToWorld )
|
||||
{
|
||||
osg::BoundingBox bbResult;
|
||||
|
||||
if( rll.size() == 0 ) return bbResult;
|
||||
|
||||
std::sort( rll.begin(), rll.end(), CompareRenderLeavesByMatrices() );
|
||||
|
||||
osg::ref_ptr< osg::RefMatrix > modelview;
|
||||
osg::ref_ptr< osg::RefMatrix > projection;
|
||||
osg::Matrix viewToWorld, modelToWorld, *ptrProjection = NULL,
|
||||
*ptrViewToWorld = &projectionToWorld, *ptrModelToWorld;
|
||||
|
||||
osg::BoundingBox bb;
|
||||
|
||||
// compute bounding boxes but skip old ones (placed at the end as NULLs)
|
||||
for( RenderLeafList::iterator it = rll.begin(); ; ++it ) {
|
||||
// we actually allow to pass one element behind end to flush bb queue
|
||||
osgUtil::RenderLeaf *rl = ( it != rll.end() ? *it : NULL );
|
||||
|
||||
// Don't trust already computed bounds for cull generated drawables
|
||||
// LightPointDrawable & LightPointSpriteDrawable are such examples
|
||||
// they store wrong recorded bounds from very first pass
|
||||
if( rl && rl->_modelview == NULL )
|
||||
rl->_drawable->dirtyBound();
|
||||
|
||||
// Stay as long as possible in model space to minimize matrix ops
|
||||
if( rl && rl->_modelview == modelview && rl->_projection == projection ){
|
||||
bb.expandBy( rl->_drawable->getBound() );
|
||||
} else {
|
||||
if( bb.valid() ) {
|
||||
// Conditions to avoid matrix multiplications
|
||||
if( projection.valid() )
|
||||
{
|
||||
if( projection.get() != ptrProjection )
|
||||
{
|
||||
ptrProjection = projection.get();
|
||||
viewToWorld = *ptrProjection * projectionToWorld;
|
||||
}
|
||||
ptrViewToWorld = &viewToWorld;
|
||||
} else {
|
||||
ptrViewToWorld = &projectionToWorld;
|
||||
}
|
||||
|
||||
if( modelview.valid() )
|
||||
{
|
||||
modelToWorld = *modelview.get() * *ptrViewToWorld;
|
||||
ptrModelToWorld = &modelToWorld;
|
||||
} else {
|
||||
ptrModelToWorld = ptrViewToWorld;
|
||||
}
|
||||
|
||||
for( int i = 0; i < 8; i++ )
|
||||
bbResult.expandBy( bb.corner( i ) * *ptrModelToWorld );
|
||||
}
|
||||
if( !rl ) break;
|
||||
bb = rl->_drawable->getBound();
|
||||
modelview = rl->_modelview;
|
||||
projection = rl->_projection;
|
||||
}
|
||||
}
|
||||
|
||||
rll.clear();
|
||||
|
||||
return bbResult;
|
||||
}
|
||||
|
||||
osg::BoundingBox MinimalCullBoundsShadowMap::ViewData::ComputeRenderLeavesBounds
|
||||
( RenderLeafList &rll, osg::Matrix & projectionToWorld, osg::Polytope & p )
|
||||
{
|
||||
osg::BoundingBox bbResult, bb;
|
||||
|
||||
if( rll.size() == 0 ) return bbResult;
|
||||
|
||||
std::sort( rll.begin(), rll.end(), CompareRenderLeavesByMatrices() );
|
||||
|
||||
osg::ref_ptr< osg::RefMatrix > modelview;
|
||||
osg::ref_ptr< osg::RefMatrix > projection;
|
||||
osg::Matrix viewToWorld, modelToWorld, *ptrProjection = NULL,
|
||||
*ptrViewToWorld = &projectionToWorld, *ptrModelToWorld;
|
||||
|
||||
// compute bounding boxes but skip old ones (placed at the end as NULLs)
|
||||
for( RenderLeafList::iterator it = rll.begin(); it != rll.end(); ++it ) {
|
||||
// we actually allow to pass one element behind end to flush bb queue
|
||||
osgUtil::RenderLeaf *rl = *it;
|
||||
if( !rl ) break;
|
||||
|
||||
// Don't trust already computed bounds for cull generated drawables
|
||||
// LightPointDrawable & LightPointSpriteDrawable are such examples
|
||||
// they store wrong recorded bounds from very first pass
|
||||
if( rl && rl->_modelview == NULL )
|
||||
rl->_drawable->dirtyBound();
|
||||
|
||||
bb = rl->_drawable->getBound();
|
||||
if( !bb.valid() ) continue;
|
||||
|
||||
// Stay as long as possible in model space to minimize matrix ops
|
||||
if( rl->_modelview != modelview || rl->_projection != projection ) {
|
||||
|
||||
projection = rl->_projection;
|
||||
if( projection.valid() )
|
||||
{
|
||||
if( projection.get() != ptrProjection )
|
||||
{
|
||||
ptrProjection = projection.get();
|
||||
viewToWorld = *ptrProjection * projectionToWorld;
|
||||
}
|
||||
ptrViewToWorld = &viewToWorld;
|
||||
} else {
|
||||
ptrViewToWorld = &projectionToWorld;
|
||||
}
|
||||
|
||||
modelview = rl->_modelview;
|
||||
if( modelview.valid() )
|
||||
{
|
||||
modelToWorld = *modelview.get() * *ptrViewToWorld;
|
||||
ptrModelToWorld = &modelToWorld;
|
||||
} else {
|
||||
ptrModelToWorld = ptrViewToWorld;
|
||||
}
|
||||
}
|
||||
|
||||
if( CheckAndMultiplyBoxIfWithinPolytope( bb, *ptrModelToWorld, p ) )
|
||||
bbResult.expandBy( bb );
|
||||
}
|
||||
|
||||
rll.clear();
|
||||
|
||||
return bbResult;
|
||||
}
|
||||
|
406
src/osgShadow/MinimalDrawBoundsShadowMap.cpp
Normal file
406
src/osgShadow/MinimalDrawBoundsShadowMap.cpp
Normal file
@ -0,0 +1,406 @@
|
||||
/* -*-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/MinimalDrawBoundsShadowMap>
|
||||
#include <osgShadow/ConvexPolyhedron>
|
||||
#include <osg/PolygonOffset>
|
||||
#include <osgUtil/RenderLeaf>
|
||||
#include <osgShadow/ShadowedScene>
|
||||
#include <osg/FrameBufferObject>
|
||||
#include <osg/BlendEquation>
|
||||
#include <osg/Depth>
|
||||
#include <osg/AlphaFunc>
|
||||
#include <osg/Image>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
#define ANALYSIS_DEPTH 1
|
||||
#define USE_FLOAT_IMAGE 1
|
||||
|
||||
using namespace osgShadow;
|
||||
|
||||
MinimalDrawBoundsShadowMap::MinimalDrawBoundsShadowMap(): BaseClass()
|
||||
{
|
||||
}
|
||||
|
||||
MinimalDrawBoundsShadowMap::MinimalDrawBoundsShadowMap
|
||||
(const MinimalDrawBoundsShadowMap& copy, const osg::CopyOp& copyop) :
|
||||
BaseClass(copy,copyop)
|
||||
{
|
||||
}
|
||||
|
||||
MinimalDrawBoundsShadowMap::~MinimalDrawBoundsShadowMap()
|
||||
{
|
||||
}
|
||||
|
||||
void MinimalDrawBoundsShadowMap::ViewData::cullShadowReceivingScene( )
|
||||
{
|
||||
BaseClass::ViewData::cullShadowReceivingScene( );
|
||||
ThisClass::ViewData::cullBoundAnalysisScene( );
|
||||
}
|
||||
|
||||
void MinimalDrawBoundsShadowMap::ViewData::cullBoundAnalysisScene( )
|
||||
{
|
||||
_boundAnalysisCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF );
|
||||
_boundAnalysisCamera->setViewMatrix( *_cv->getModelViewMatrix() );
|
||||
_boundAnalysisCamera->setProjectionMatrix( _clampedProjection );
|
||||
|
||||
osg::Matrix::value_type l,r,b,t,n,f;
|
||||
_boundAnalysisCamera->getProjectionMatrixAsFrustum( l,r,b,t,n,f );
|
||||
|
||||
_mainCamera = _cv->getRenderStage()->getCamera();
|
||||
|
||||
extendProjection( _boundAnalysisCamera->getProjectionMatrix(),
|
||||
_boundAnalysisCamera->getViewport(), osg::Vec2( 2,2 ) );
|
||||
|
||||
// record the traversal mask on entry so we can reapply it later.
|
||||
unsigned int traversalMask = _cv->getTraversalMask();
|
||||
|
||||
_cv->setTraversalMask( traversalMask &
|
||||
_st->getShadowedScene()->getReceivesShadowTraversalMask() );
|
||||
|
||||
// do RTT camera traversal
|
||||
_boundAnalysisCamera->accept(*_cv);
|
||||
|
||||
// reapply the original traversal mask
|
||||
_cv->setTraversalMask( traversalMask );
|
||||
}
|
||||
|
||||
|
||||
void MinimalDrawBoundsShadowMap::ViewData::createDebugHUD( )
|
||||
{
|
||||
// _hudSize[0] *= 2;
|
||||
_viewportSize[0] *= 2;
|
||||
_orthoSize[0] *= 2;
|
||||
|
||||
MinimalShadowMap::ViewData::createDebugHUD( );
|
||||
|
||||
osg::Camera * camera = _cameraDebugHUD.get();
|
||||
|
||||
osg::Geode* geode = new osg::Geode;
|
||||
camera->addChild( geode );
|
||||
|
||||
osg::Geometry* geometry = osg::createTexturedQuadGeometry
|
||||
( osg::Vec3(_hudOrigin[0]+_hudSize[0],_hudOrigin[1],0),
|
||||
osg::Vec3(_hudSize[0],0,0),
|
||||
osg::Vec3(0,_hudSize[1],0) );
|
||||
|
||||
geode->addDrawable(geometry);
|
||||
|
||||
osg::StateSet* stateset = geometry->getOrCreateStateSet();
|
||||
stateset->setTextureAttributeAndModes
|
||||
(0, _boundAnalysisTexture.get(),osg::StateAttribute::ON );
|
||||
|
||||
#if ANALYSIS_DEPTH
|
||||
osg::Program* program = new osg::Program;
|
||||
program->addShader( _depthColorFragmentShader.get() );
|
||||
stateset->setAttribute( program );
|
||||
stateset->addUniform( new osg::Uniform( "texture" , 0 ) );
|
||||
#else
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
osg::BoundingBox MinimalDrawBoundsShadowMap::ViewData::scanImage
|
||||
( const osg::Image * image, osg::Matrix m )
|
||||
{
|
||||
osg::BoundingBox bb, bbProj;
|
||||
|
||||
int components = osg::Image::computeNumComponents( image->getPixelFormat() );
|
||||
|
||||
if( image->getDataType() == GL_FLOAT ) {
|
||||
float scale = 255.f / 254.f;
|
||||
float * pf = (float *)image->data();
|
||||
for( int y = 0; y < image->t(); y++ ) {
|
||||
float fY = ( 0.5f + y ) / image->t();
|
||||
for( int x = 0; x < image->s(); x++ ) {
|
||||
float fX = ( 0.5f + x ) / image->s();
|
||||
|
||||
if( pf[0] < 1.0 ) {
|
||||
float fMinZ = pf[0] * scale;
|
||||
bbProj.expandBy( osg::Vec3( fX, fY, fMinZ ) );
|
||||
bb.expandBy( osg::Vec3( fX, fY, fMinZ ) * m );
|
||||
|
||||
if( components > 1 ) {
|
||||
float fMaxZ = scale * ( 1.f - pf[1] );
|
||||
bbProj.expandBy( osg::Vec3( fX, fY, fMaxZ ) );
|
||||
bb.expandBy( osg::Vec3( fX, fY, fMaxZ ) * m );
|
||||
}
|
||||
}
|
||||
|
||||
pf += components;
|
||||
}
|
||||
}
|
||||
} else if( image->getDataType() == GL_UNSIGNED_BYTE ) {
|
||||
|
||||
unsigned char * pb = (unsigned char *)image->data();
|
||||
|
||||
float scale = 1.f / 254, bias = 0.5f;
|
||||
for( int y = 0; y < image->t(); y++ ) {
|
||||
float fY = ( 0.5f + y ) / image->t();
|
||||
for( int x = 0; x < image->s(); x++ ) {
|
||||
float fX = ( 0.5f + x ) / image->s();
|
||||
|
||||
if( pb[0] < 255 ) {
|
||||
float fMinZ = scale * (pb[0] - 0.5f);
|
||||
fMinZ = osg::clampTo( fMinZ, 0.f, 1.f );
|
||||
|
||||
bbProj.expandBy( osg::Vec3( fX, fY, fMinZ ) );
|
||||
bb.expandBy( osg::Vec3( fX, fY, fMinZ ) * m );
|
||||
|
||||
if( components > 1 ) {
|
||||
float fMaxZ = scale * (255 - pb[1] + 0.5f);
|
||||
fMaxZ = osg::clampTo( fMaxZ, 0.f, 1.f );
|
||||
|
||||
bbProj.expandBy( osg::Vec3( fX, fY, fMaxZ ) );
|
||||
bb.expandBy( osg::Vec3( fX, fY, fMaxZ ) * m );
|
||||
}
|
||||
}
|
||||
|
||||
pb += components;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
void MinimalDrawBoundsShadowMap::ViewData::performBoundAnalysis( const osg::Camera& camera )
|
||||
{
|
||||
if( !_projection.valid() )
|
||||
return;
|
||||
|
||||
osg::Camera::BufferAttachmentMap & bam
|
||||
= const_cast<osg::Camera&>( camera ).getBufferAttachmentMap();
|
||||
#if ANALYSIS_DEPTH
|
||||
osg::Camera::Attachment & attachment = bam[ osg::Camera::DEPTH_BUFFER ];
|
||||
#else
|
||||
osg::Camera::Attachment & attachment = bam[ osg::Camera::COLOR_BUFFER ];
|
||||
#endif
|
||||
|
||||
const osg::ref_ptr< osg::Image > image = attachment._image.get();
|
||||
if( !image.valid() )
|
||||
return;
|
||||
|
||||
osg::Matrix m;
|
||||
m.invert( *_modellingSpaceToWorldPtr *
|
||||
camera.getViewMatrix() *
|
||||
camera.getProjectionMatrix() );
|
||||
|
||||
m.preMult( osg::Matrix::scale( osg::Vec3( 2.f, 2.f, 2.f ) ) *
|
||||
osg::Matrix::translate( osg::Vec3( -1.f, -1.f, -1.f ) ) );
|
||||
|
||||
osg::BoundingBox bb = scanImage( image.get(), m );
|
||||
|
||||
if( getDebugDraw() ) {
|
||||
ConvexPolyhedron p;
|
||||
p.setToBoundingBox( bb );
|
||||
p.transform( *_modellingSpaceToWorldPtr,
|
||||
osg::Matrix::inverse( *_modellingSpaceToWorldPtr ) );
|
||||
|
||||
setDebugPolytope( "scan", p,
|
||||
osg::Vec4( 0,0,0,1 ), osg::Vec4( 0,0,0,0.1 ) );
|
||||
}
|
||||
|
||||
cutScenePolytope( *_modellingSpaceToWorldPtr,
|
||||
osg::Matrix::inverse( *_modellingSpaceToWorldPtr ), bb );
|
||||
|
||||
frameShadowCastingCamera( _mainCamera.get(), _camera.get() );
|
||||
|
||||
_projection->set( _camera->getProjectionMatrix( ) );
|
||||
|
||||
BaseClass::ViewData::_texgen->setPlanesFromMatrix(
|
||||
_camera->getProjectionMatrix() *
|
||||
osg::Matrix::translate(1.0,1.0,1.0) *
|
||||
osg::Matrix::scale(0.5,0.5,0.5) );
|
||||
|
||||
updateDebugGeometry( _mainCamera.get(), _camera.get() );
|
||||
}
|
||||
|
||||
void MinimalDrawBoundsShadowMap::ViewData::recordShadowMapParams( )
|
||||
{
|
||||
const osgUtil::RenderStage * rs = _cv->getCurrentRenderBin()->getStage();
|
||||
|
||||
setShadowCameraProjectionMatrixPtr( _cv->getProjectionMatrix() );
|
||||
|
||||
if( !rs->getRenderBinList().empty() || rs->getBinNum() != 0 )
|
||||
{
|
||||
|
||||
}
|
||||
#if 0
|
||||
MinimalShadowMap::RenderLeafList rll;
|
||||
|
||||
static unsigned pass = 0, c = 0;
|
||||
|
||||
pass++;
|
||||
|
||||
std::set< osg::ref_ptr< osg::RefMatrix > > projections;
|
||||
|
||||
MinimalShadowMap::GetRenderLeaves( , rll );
|
||||
for( unsigned i =0; i < rll.size(); i++ ) {
|
||||
if( rll[i]->_projection.get() != _projection.get() ) {
|
||||
osg::RefMatrix * projection = rll[i]->_projection.get();
|
||||
projections.insert( rll[i]->_projection );
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
if( projections.size() > 0 )
|
||||
_projection = (*projections.begin()).get();
|
||||
|
||||
|
||||
c = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MinimalDrawBoundsShadowMap::ViewData::init
|
||||
( ThisClass *st, osgUtil::CullVisitor *cv )
|
||||
{
|
||||
BaseClass::ViewData::init( st, cv );
|
||||
|
||||
_camera->setCullCallback
|
||||
( new CameraCullCallback( this, _camera->getCullCallback() ) );
|
||||
|
||||
_boundAnalysisTexture = new osg::Texture2D;
|
||||
_boundAnalysisTexture->setTextureSize
|
||||
( _boundAnalysisSize[0], _boundAnalysisSize[1] );
|
||||
|
||||
_boundAnalysisImage = new osg::Image;
|
||||
|
||||
|
||||
#if ANALYSIS_DEPTH
|
||||
_boundAnalysisImage->allocateImage( _boundAnalysisSize[0],
|
||||
_boundAnalysisSize[1], 1,
|
||||
GL_DEPTH_COMPONENT, GL_FLOAT );
|
||||
|
||||
_boundAnalysisTexture->setInternalFormat(GL_DEPTH_COMPONENT);
|
||||
// _boundAnalysisTexture->setShadowComparison(true);
|
||||
_boundAnalysisTexture->setShadowTextureMode(osg::Texture2D::LUMINANCE);
|
||||
|
||||
|
||||
_boundAnalysisImage->setInternalTextureFormat( GL_DEPTH_COMPONENT );
|
||||
_boundAnalysisTexture->setInternalFormat( GL_DEPTH_COMPONENT );
|
||||
#else
|
||||
|
||||
#if USE_FLOAT_IMAGE
|
||||
_boundAnalysisImage->allocateImage( _boundAnalysisSize[0],
|
||||
_boundAnalysisSize[1], 1,
|
||||
GL_RGBA, GL_FLOAT );
|
||||
|
||||
_boundAnalysisImage->setInternalTextureFormat( GL_RGBA16F_ARB );
|
||||
_boundAnalysisTexture->setInternalFormat(GL_RGBA16F_ARB);
|
||||
#else
|
||||
_boundAnalysisImage->allocateImage( _boundAnalysisSize[0],
|
||||
_boundAnalysisSize[1], 1,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE );
|
||||
|
||||
_boundAnalysisImage->setInternalTextureFormat( GL_RGBA );
|
||||
_boundAnalysisTexture->setInternalFormat( GL_RGBA );
|
||||
#endif
|
||||
|
||||
#endif
|
||||
memset( _boundAnalysisImage->data(), 0, _boundAnalysisImage->getImageSizeInBytes() );
|
||||
|
||||
if( getDebugDraw() )
|
||||
_boundAnalysisTexture->setImage(0, _boundAnalysisImage.get() );
|
||||
|
||||
_boundAnalysisTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);
|
||||
_boundAnalysisTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);
|
||||
|
||||
// the shadow comparison should fail if object is outside the texture
|
||||
_boundAnalysisTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::REPEAT);
|
||||
_boundAnalysisTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT);
|
||||
|
||||
// set up the render to texture camera.
|
||||
// create the camera
|
||||
_boundAnalysisCamera = new osg::Camera;
|
||||
_boundAnalysisCamera->setName( "AnalysisCamera" );
|
||||
|
||||
_boundAnalysisCamera->setCullCallback( new BaseClass::CameraCullCallback(st) );
|
||||
// _boundAnalysisCamera->setPreDrawCallback( _camera->getPreDrawCallback() );
|
||||
_boundAnalysisCamera->setPostDrawCallback( new CameraPostDrawCallback(this) );
|
||||
|
||||
_boundAnalysisCamera->setClearColor( osg::Vec4(1,1,1,1) );
|
||||
_boundAnalysisCamera->setClearMask(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
|
||||
_boundAnalysisCamera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
|
||||
// set viewport
|
||||
_boundAnalysisCamera->setViewport
|
||||
( 0, 0, _boundAnalysisSize[0], _boundAnalysisSize[1] );
|
||||
|
||||
// set the camera to render before the main camera.
|
||||
_boundAnalysisCamera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
|
||||
// tell the camera to use OpenGL frame buffer object where supported.
|
||||
_boundAnalysisCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
||||
//_boundAnalysisCamera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW);
|
||||
|
||||
const int OVERRIDE_ON = osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON;
|
||||
const int OVERRIDE_OFF = osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF;
|
||||
|
||||
osg::StateSet* stateset = _boundAnalysisCamera->getOrCreateStateSet();
|
||||
stateset->setAttributeAndModes
|
||||
( new osg::Depth( osg::Depth::LESS, 0.0, 254.f/255.f ), OVERRIDE_ON );
|
||||
|
||||
// stateset->setAttributeAndModes
|
||||
// ( new osg::AlphaFunc( osg::AlphaFunc::EQUAL, 1.f ), OVERRIDE_ON );
|
||||
|
||||
stateset->setRenderBinDetails( 0, "RenderBin",
|
||||
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS );
|
||||
|
||||
osg::Program* program = new osg::Program;
|
||||
|
||||
program->addShader( new osg::Shader( osg::Shader::FRAGMENT,
|
||||
"uniform sampler2D texture; \n"
|
||||
"void main(void) \n"
|
||||
"{ \n"
|
||||
#if ANALYSIS_DEPTH
|
||||
" gl_FragColor = texture2D( texture, gl_TexCoord[0].xy ); \n"
|
||||
#else
|
||||
" gl_FragColor = vec4( gl_FragCoord.z, \n"
|
||||
" 1.-gl_FragCoord.z, \n"
|
||||
" 1., \n"
|
||||
" texture2D( texture, gl_TexCoord[0].xy ).a ); \n"
|
||||
#endif
|
||||
"} \n"
|
||||
) ); // program->addShader Fragment
|
||||
|
||||
program->addShader( new osg::Shader( osg::Shader::VERTEX,
|
||||
"void main(void) \n"
|
||||
"{ \n"
|
||||
" gl_Position = ftransform(); \n"
|
||||
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
|
||||
"} \n"
|
||||
) ); // program->addShader Vertex
|
||||
|
||||
stateset->setAttribute( program, OVERRIDE_ON );
|
||||
|
||||
// attach the texture and use it as the color buffer.
|
||||
#if ANALYSIS_DEPTH
|
||||
// _boundAnalysisCamera->attach(osg::Camera::DEPTH_BUFFER, _boundAnalysisTexture.get());
|
||||
_boundAnalysisCamera->attach(osg::Camera::DEPTH_BUFFER, _boundAnalysisImage.get());
|
||||
|
||||
stateset->setMode( GL_BLEND, OVERRIDE_OFF );
|
||||
#else
|
||||
// _boundAnalysisCamera->attach(osg::Camera::COLOR_BUFFER, _boundAnalysisTexture.get());
|
||||
_boundAnalysisCamera->attach(osg::Camera::COLOR_BUFFER, _boundAnalysisImage.get());
|
||||
|
||||
stateset->setAttributeAndModes
|
||||
( new osg::BlendEquation( osg::BlendEquation::RGBA_MIN ), OVERRIDE_ON );
|
||||
|
||||
stateset->setMode( GL_DEPTH_TEST, OVERRIDE_OFF );
|
||||
#endif
|
||||
}
|
500
src/osgShadow/MinimalShadowMap.cpp
Normal file
500
src/osgShadow/MinimalShadowMap.cpp
Normal file
@ -0,0 +1,500 @@
|
||||
/* -*-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/MinimalShadowMap>
|
||||
#include <osgShadow/ConvexPolyhedron>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osgShadow/ShadowedScene>
|
||||
#include <osg/ComputeBoundsVisitor>
|
||||
|
||||
using namespace osgShadow;
|
||||
|
||||
#define PRINT_SHADOW_TEXEL_TO_PIXEL_ERROR 0
|
||||
|
||||
MinimalShadowMap::MinimalShadowMap():
|
||||
BaseClass(),
|
||||
_minLightMargin( 0 ),
|
||||
_maxFarPlane( FLT_MAX ),
|
||||
_shadowReceivingCoarseBoundAccuracy( BOUNDING_BOX )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MinimalShadowMap::MinimalShadowMap
|
||||
(const MinimalShadowMap& copy, const osg::CopyOp& copyop) :
|
||||
BaseClass(copy,copyop),
|
||||
_maxFarPlane( copy._maxFarPlane ),
|
||||
_minLightMargin( copy._minLightMargin ),
|
||||
_shadowReceivingCoarseBoundAccuracy( copy._shadowReceivingCoarseBoundAccuracy )
|
||||
{
|
||||
}
|
||||
|
||||
MinimalShadowMap::~MinimalShadowMap()
|
||||
{
|
||||
}
|
||||
|
||||
osg::BoundingBox MinimalShadowMap::ViewData::computeShadowReceivingCoarseBounds()
|
||||
{
|
||||
// Default slowest but most precise
|
||||
ShadowReceivingCoarseBoundAccuracy accuracy = DEFAULT_ACCURACY;
|
||||
|
||||
MinimalShadowMap * msm = dynamic_cast< MinimalShadowMap* >( _st.get() );
|
||||
if( msm ) accuracy = msm->getShadowReceivingCoarseBoundAccuracy();
|
||||
|
||||
if( accuracy == MinimalShadowMap::EMPTY_BOX )
|
||||
{
|
||||
// One may skip coarse scene bounds computation if light is infinite.
|
||||
// Empty box will be intersected with view frustum so in the end
|
||||
// view frustum will be used as bounds approximation.
|
||||
// But if light is nondirectional and bounds come out too large
|
||||
// they may bring the effect of almost 180 deg perspective set
|
||||
// up for shadow camera. Such projection will significantly impact
|
||||
// precision of further math.
|
||||
|
||||
return osg::BoundingBox();
|
||||
}
|
||||
|
||||
if( accuracy == MinimalShadowMap::BOUNDING_SPHERE )
|
||||
{
|
||||
// faster but less precise rough scene bound computation
|
||||
// however if compute near far is active it may bring quite good result
|
||||
osg::Camera * camera = _cv->getRenderStage()->getCamera();
|
||||
osg::Matrix m = camera->getViewMatrix() * _clampedProjection;
|
||||
|
||||
ConvexPolyhedron frustum;
|
||||
frustum.setToUnitFrustum();
|
||||
frustum.transform( osg::Matrix::inverse( m ), m );
|
||||
|
||||
osg::BoundingSphere bs =_st->getShadowedScene()->getBound();
|
||||
osg::BoundingBox bb;
|
||||
bb.expandBy( bs );
|
||||
osg::Polytope box;
|
||||
box.setToBoundingBox( bb );
|
||||
|
||||
frustum.cut( box );
|
||||
|
||||
// approximate sphere with octahedron. Ie first cut by box then
|
||||
// additionaly cut with the same box rotated 45, 45, 45 deg.
|
||||
box.transform( // rotate box around its center
|
||||
osg::Matrix::translate( -bs.center() ) *
|
||||
osg::Matrix::rotate( osg::PI_4, 0, 0, 1 ) *
|
||||
osg::Matrix::rotate( osg::PI_4, 1, 1, 0 ) *
|
||||
osg::Matrix::translate( bs.center() ) );
|
||||
frustum.cut( box );
|
||||
|
||||
return frustum.computeBoundingBox( );
|
||||
}
|
||||
|
||||
if( accuracy == MinimalShadowMap::BOUNDING_BOX ) // Default
|
||||
{
|
||||
// more precise method but slower method
|
||||
// bound visitor traversal takes lot of time for complex scenes
|
||||
// (note that this adds to cull time)
|
||||
|
||||
osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
|
||||
cbbv.setTraversalMask(_st->getShadowedScene()->getCastsShadowTraversalMask());
|
||||
_st->getShadowedScene()->osg::Group::traverse(cbbv);
|
||||
|
||||
return cbbv.getBoundingBox();
|
||||
}
|
||||
|
||||
return osg::BoundingBox();
|
||||
}
|
||||
|
||||
void MinimalShadowMap::ViewData::aimShadowCastingCamera
|
||||
( const osg::Light *light, const osg::Vec4 &lightPos,
|
||||
const osg::Vec3 &lightDir, const osg::Vec3 &lightUp )
|
||||
{
|
||||
osg::BoundingBox bb = computeScenePolytopeBounds();
|
||||
if( !bb.valid() ) { // empty scene or looking at the sky - substitute something
|
||||
bb.expandBy( osg::BoundingSphere( _cv->getEyePoint(), 1 ) );
|
||||
}
|
||||
|
||||
osg::Vec3 up = lightUp;
|
||||
|
||||
if( up.length2() <= 0 )
|
||||
{
|
||||
// This is extra step (not really needed but helpful in debuging)
|
||||
// Compute such lightUp vector that shadow cam is intuitively aligned with eye
|
||||
// We compute this vector on -ZY view plane, perpendicular to light direction
|
||||
// Matrix m = ViewToWorld
|
||||
#if 0
|
||||
osg::Matrix m = osg::Matrix::inverse( *cv.getModelViewMatrix() );
|
||||
osg::Vec3 camFw( -m( 2, 0 ), -m( 2, 1 ), -m( 2, 2 ) );
|
||||
camFw.normalize();
|
||||
|
||||
osg::Vec3 camUp( m( 1, 0 ), m( 1, 1 ), m( 1, 2 ) );
|
||||
camUp.normalize();
|
||||
|
||||
up = camUp * ( camFw * lightDir ) - camFw * ( camUp * lightDir );
|
||||
up.normalize();
|
||||
#else
|
||||
osg::Matrix m = osg::Matrix::inverse( *_cv->getModelViewMatrix() );
|
||||
// OpenGL std cam looks along -Z axis so Cam Fw = [ 0 0 -1 0 ] * m
|
||||
up.set( -m( 2, 0 ), -m( 2, 1 ), -m( 2, 2 ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
BaseClass::ViewData::aimShadowCastingCamera
|
||||
( bb, light, lightPos, lightDir, up );
|
||||
|
||||
// Intersect scene Receiving Shadow Polytope with shadow camera frustum
|
||||
// Important for cases where Scene extend beyond shadow camera frustum
|
||||
// From this moment shadowed scene portion is fully contained by both
|
||||
// main camera frustum and shadow camera frustum
|
||||
osg::Matrix mvp = _camera->getViewMatrix() * _camera->getProjectionMatrix();
|
||||
cutScenePolytope( osg::Matrix::inverse( mvp ), mvp );
|
||||
|
||||
MinimalShadowMap::ViewData::frameShadowCastingCamera
|
||||
( _cv->getRenderStage()->getCamera(), _camera.get(), 0 );
|
||||
}
|
||||
|
||||
void MinimalShadowMap::ViewData::frameShadowCastingCamera
|
||||
( const osg::Camera* cameraMain, osg::Camera* cameraShadow, int pass )
|
||||
{
|
||||
osg::Matrix mvp =
|
||||
cameraShadow->getViewMatrix() * cameraShadow->getProjectionMatrix();
|
||||
|
||||
ConvexPolyhedron polytope = _sceneReceivingShadowPolytope;
|
||||
std::vector<osg::Vec3d> points = _sceneReceivingShadowPolytopePoints;
|
||||
|
||||
osg::BoundingBox bb = computeScenePolytopeBounds( mvp );
|
||||
|
||||
// projection was trimmed above, need to recompute mvp
|
||||
if( bb.valid() && *_minLightMarginPtr > 0 ) {
|
||||
// bb._max += osg::Vec3( 1, 1, 1 );
|
||||
// bb._min -= osg::Vec3( 1, 1, 1 );
|
||||
|
||||
osg::Matrix transform = osg::Matrix::inverse( mvp );
|
||||
|
||||
osg::Vec3d normal = osg::Matrix::transform3x3( osg::Vec3d(0,0,-1), transform );
|
||||
normal.normalize();
|
||||
_sceneReceivingShadowPolytope.extrude( normal * *_minLightMarginPtr );
|
||||
|
||||
// Zero pass does crude shadowed scene hull approximation.
|
||||
// Its important to cut it to coarse light frustum properly
|
||||
// at this stage.
|
||||
// If not cut and polytope extends beyond shadow projection clip
|
||||
// space (-1..1), it may get "twisted" by precisely adjusted shadow cam
|
||||
// projection in second pass.
|
||||
|
||||
if ( pass == 0 )
|
||||
{ // Make sure extruded polytope does not extend beyond light frustum
|
||||
osg::Polytope lightFrustum;
|
||||
lightFrustum.setToUnitFrustum();
|
||||
lightFrustum.transformProvidingInverse( mvp );
|
||||
_sceneReceivingShadowPolytope.cut( lightFrustum );
|
||||
}
|
||||
|
||||
_sceneReceivingShadowPolytopePoints.clear();
|
||||
_sceneReceivingShadowPolytope.getPoints
|
||||
( _sceneReceivingShadowPolytopePoints );
|
||||
|
||||
bb = computeScenePolytopeBounds( mvp );
|
||||
}
|
||||
|
||||
setDebugPolytope( "extended",
|
||||
_sceneReceivingShadowPolytope, osg::Vec4( 1, 0.5, 0, 1 ), osg::Vec4( 1, 0.5, 0, 0.1 ) );
|
||||
|
||||
_sceneReceivingShadowPolytope = polytope;
|
||||
_sceneReceivingShadowPolytopePoints = points;
|
||||
|
||||
// Warning: Trim light projection at near plane may remove shadowing
|
||||
// from objects outside of view space but still casting shadows into it.
|
||||
// I have not noticed this issue so I left mask at default: all bits set.
|
||||
if( bb.valid() )
|
||||
trimProjection( cameraShadow->getProjectionMatrix(), bb, 1|2|4|8|16|32 );
|
||||
|
||||
///// Debuging stuff //////////////////////////////////////////////////////////
|
||||
setDebugPolytope( "scene", _sceneReceivingShadowPolytope, osg::Vec4(0,1,0,1) );
|
||||
|
||||
|
||||
#if PRINT_SHADOW_TEXEL_TO_PIXEL_ERROR
|
||||
if( pass == 1 )
|
||||
displayShadowTexelToPixelErrors
|
||||
( cameraMain, cameraShadow, &_sceneReceivingShadowPolytope );
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void MinimalShadowMap::ViewData::cullShadowReceivingScene( )
|
||||
{
|
||||
BaseClass::ViewData::cullShadowReceivingScene( );
|
||||
|
||||
_clampedProjection = *_cv->getProjectionMatrix();
|
||||
|
||||
if( _cv->getComputeNearFarMode() ) {
|
||||
|
||||
// Redo steps from CullVisitor::popProjectionMatrix()
|
||||
// which clamps projection matrix when Camera & Projection
|
||||
// completes traversal of their children
|
||||
|
||||
// We have to do this now manually
|
||||
// because we did not complete camera traversal yet but
|
||||
// we need to know how this clamped projection matrix will be
|
||||
|
||||
_cv->computeNearPlane();
|
||||
|
||||
double n = _cv->getCalculatedNearPlane();
|
||||
double f = _cv->getCalculatedFarPlane();
|
||||
|
||||
if( n < f )
|
||||
_cv->clampProjectionMatrix(_clampedProjection, n, f );
|
||||
}
|
||||
|
||||
// Aditionally clamp far plane if shadows are don't need to be cast as
|
||||
// far as main projection far plane
|
||||
if( 0 < *_maxFarPlanePtr )
|
||||
clampProjection( _clampedProjection, 0.f, *_maxFarPlanePtr );
|
||||
|
||||
// Give derived classes chance to initialize _sceneReceivingShadowPolytope
|
||||
osg::BoundingBox bb = computeShadowReceivingCoarseBounds( );
|
||||
if( bb.valid() )
|
||||
_sceneReceivingShadowPolytope.setToBoundingBox( bb );
|
||||
else
|
||||
_sceneReceivingShadowPolytope.clear();
|
||||
|
||||
// Cut initial scene using main camera frustum.
|
||||
// Cutting will work correctly on empty polytope too.
|
||||
// Take into consideration near far calculation and _maxFarPlane variable
|
||||
|
||||
|
||||
osg::Matrix mvp = *_cv->getModelViewMatrix() * _clampedProjection;
|
||||
|
||||
cutScenePolytope( osg::Matrix::inverse( mvp ), mvp );
|
||||
|
||||
setDebugPolytope
|
||||
( "frustum", _sceneReceivingShadowPolytope, osg::Vec4(1,0,1,1));
|
||||
|
||||
}
|
||||
|
||||
void MinimalShadowMap::ViewData::init( ThisClass *st, osgUtil::CullVisitor *cv )
|
||||
{
|
||||
BaseClass::ViewData::init( st, cv );
|
||||
|
||||
_modellingSpaceToWorldPtr = &st->_modellingSpaceToWorld;
|
||||
_minLightMarginPtr = &st->_minLightMargin;
|
||||
_maxFarPlanePtr = &st->_maxFarPlane;
|
||||
}
|
||||
|
||||
void MinimalShadowMap::ViewData::cutScenePolytope
|
||||
( const osg::Matrix & transform,
|
||||
const osg::Matrix & inverse,
|
||||
const osg::BoundingBox & bb )
|
||||
{
|
||||
_sceneReceivingShadowPolytopePoints.clear();
|
||||
|
||||
if( bb.valid() ) {
|
||||
osg::Polytope polytope;
|
||||
polytope.setToBoundingBox( bb );
|
||||
polytope.transformProvidingInverse( inverse );
|
||||
_sceneReceivingShadowPolytope.cut( polytope );
|
||||
_sceneReceivingShadowPolytope.getPoints
|
||||
( _sceneReceivingShadowPolytopePoints );
|
||||
} else
|
||||
_sceneReceivingShadowPolytope.clear();
|
||||
}
|
||||
|
||||
osg::BoundingBox
|
||||
MinimalShadowMap::ViewData::computeScenePolytopeBounds( const osg::Matrix & m )
|
||||
{
|
||||
osg::BoundingBox bb;
|
||||
|
||||
if( &m )
|
||||
for( unsigned i = 0; i < _sceneReceivingShadowPolytopePoints.size(); ++i )
|
||||
bb.expandBy( _sceneReceivingShadowPolytopePoints[i] * m );
|
||||
else
|
||||
for( unsigned i = 0; i < _sceneReceivingShadowPolytopePoints.size(); ++i )
|
||||
bb.expandBy( _sceneReceivingShadowPolytopePoints[i] );
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Utility methods for adjusting projection matrices
|
||||
|
||||
void MinimalShadowMap::ViewData::trimProjection
|
||||
( osg::Matrix & projectionMatrix, osg::BoundingBox bb, unsigned int trimMask )
|
||||
{
|
||||
#if 1
|
||||
if( !bb.valid() || !( trimMask & (1|2|4|8|16|32) ) ) return;
|
||||
double l = -1, r = 1, b = -1, t = 1, n = 1, f = -1;
|
||||
|
||||
#if 0
|
||||
// make sure bounding box does not extend beyond unit frustum clip range
|
||||
for( int i = 0; i < 3; i ++ ) {
|
||||
if( bb._min[i] < -1 ) bb._min[i] = -1;
|
||||
if( bb._max[i] > 1 ) bb._max[i] = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( trimMask & 1 ) l = bb._min[0];
|
||||
if( trimMask & 2 ) r = bb._max[0];
|
||||
if( trimMask & 4 ) b = bb._min[1];
|
||||
if( trimMask & 8 ) t = bb._max[1];
|
||||
if( trimMask & 16 ) n = -bb._min[2];
|
||||
if( trimMask & 32 ) f = -bb._max[2];
|
||||
|
||||
projectionMatrix.postMult( osg::Matrix::ortho( l,r,b,t,n,f ) );
|
||||
#else
|
||||
if( !bb.valid() || !( trimMask & (1|2|4|8|16|32) ) ) return;
|
||||
double l, r, t, b, n, f;
|
||||
bool ortho = projectionMatrix.getOrtho( l, r, b, t, n, f );
|
||||
if( !ortho && !projectionMatrix.getFrustum( l, r, b, t, n, f ) )
|
||||
return; // rotated or skewed or other crooked projection - give up
|
||||
|
||||
// make sure bounding box does not extend beyond unit frustum clip range
|
||||
for( int i = 0; i < 3; i ++ ) {
|
||||
if( bb._min[i] < -1 ) bb._min[i] = -1;
|
||||
if( bb._max[i] > 1 ) bb._max[i] = 1;
|
||||
}
|
||||
|
||||
osg::Matrix projectionToView = osg::Matrix::inverse( projectionMatrix );
|
||||
|
||||
osg::Vec3 min =
|
||||
osg::Vec3( bb._min[0], bb._min[1], bb._min[2] ) * projectionToView;
|
||||
|
||||
osg::Vec3 max =
|
||||
osg::Vec3( bb._max[0], bb._max[1], bb._max[2] ) * projectionToView;
|
||||
|
||||
if( trimMask & 16 ) { // trim near
|
||||
if( !ortho ) { // recalc frustum corners on new near plane
|
||||
l *= -min[2] / n;
|
||||
r *= -min[2] / n;
|
||||
b *= -min[2] / n;
|
||||
t *= -min[2] / n;
|
||||
}
|
||||
n = -min[2];
|
||||
}
|
||||
|
||||
if( trimMask & 32 ) // trim far
|
||||
f = -max[2];
|
||||
|
||||
if( !ortho ) {
|
||||
min[0] *= -n / min[2];
|
||||
min[1] *= -n / min[2];
|
||||
max[0] *= -n / max[2];
|
||||
max[1] *= -n / max[2];
|
||||
}
|
||||
|
||||
if( l < r ) { // check for inverted X range
|
||||
if( l < min[0] && ( trimMask & 1 ) ) l = min[0];
|
||||
if( r > max[0] && ( trimMask & 2 ) ) r = max[0];
|
||||
} else {
|
||||
if( l > min[0] && ( trimMask & 1 ) ) l = min[0];
|
||||
if( r < max[0] && ( trimMask & 2 ) ) r = max[0];
|
||||
}
|
||||
|
||||
if( b < t ) { // check for inverted Y range
|
||||
if( b < min[1] && ( trimMask & 4 ) ) b = min[1];
|
||||
if( t > max[1] && ( trimMask & 8 ) ) t = max[1];
|
||||
} else {
|
||||
if( b > min[1] && ( trimMask & 4 ) ) b = min[1];
|
||||
if( t < max[1] && ( trimMask & 8 ) ) t = max[1];
|
||||
}
|
||||
|
||||
if( ortho )
|
||||
projectionMatrix.makeOrtho( l, r, b, t, n, f );
|
||||
else
|
||||
projectionMatrix.makeFrustum( l, r, b, t, n, f );
|
||||
#endif
|
||||
}
|
||||
|
||||
void MinimalShadowMap::ViewData::clampProjection
|
||||
( osg::Matrix & projection, float new_near, float new_far )
|
||||
{
|
||||
double r, l, t, b, n, f;
|
||||
bool perspective = projection.getFrustum( l, r, b, t, n, f );
|
||||
if( !perspective && !projection.getOrtho( l, r, b, t, n, f ) )
|
||||
{
|
||||
// What to do here ?
|
||||
osg::notify( osg::WARN )
|
||||
<< "MinimalShadowMap::clampProjectionFarPlane failed - non standard matrix"
|
||||
<< std::endl;
|
||||
|
||||
} else if( n < new_near || new_far < f ) {
|
||||
|
||||
if( n < new_near && new_near < f ) {
|
||||
if( perspective ) {
|
||||
l *= new_near / n;
|
||||
r *= new_near / n;
|
||||
b *= new_near / n;
|
||||
t *= new_near / n;
|
||||
}
|
||||
n = new_near;
|
||||
}
|
||||
|
||||
if( n < new_far && new_far < f ) {
|
||||
f = new_far;
|
||||
}
|
||||
|
||||
if( perspective )
|
||||
projection.makeFrustum( l, r, b, t, n, f );
|
||||
else
|
||||
projection.makeOrtho( l, r, b, t, n, f );
|
||||
}
|
||||
}
|
||||
|
||||
// Imagine following scenario:
|
||||
// We stand in the room and look through the window.
|
||||
// How should our view change if we were looking through larger window ?
|
||||
// In other words how should projection be adjusted if
|
||||
// window had grown by some margin ?
|
||||
// Method computes such new projection which maintains perpective/world ratio
|
||||
|
||||
void MinimalShadowMap::ViewData::extendProjection
|
||||
( osg::Matrix & projection, osg::Viewport * viewport, const osg::Vec2& margin )
|
||||
{
|
||||
double l,r,b,t,n,f;
|
||||
|
||||
//osg::Matrix projection = camera.getProjectionMatrix();
|
||||
|
||||
bool frustum = projection.getFrustum( l,r,b,t,n,f );
|
||||
|
||||
if( !frustum && !projection.getOrtho( l,r,b,t,n,f ) ) {
|
||||
osg::notify( osg::WARN )
|
||||
<< " Awkward projection matrix. ComputeExtendedProjection failed"
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
osg::Matrix window = viewport->computeWindowMatrix();
|
||||
|
||||
osg::Vec3 vMin( viewport->x() - margin.x(),
|
||||
viewport->y() - margin.y(),
|
||||
0.0 );
|
||||
|
||||
osg::Vec3 vMax( viewport->width() + margin.x() * 2 + vMin.x(),
|
||||
viewport->height() + margin.y() * 2 + vMin.y(),
|
||||
0.0 );
|
||||
|
||||
osg::Matrix inversePW = osg::Matrix::inverse( projection * window );
|
||||
|
||||
vMin = vMin * inversePW;
|
||||
vMax = vMax * inversePW;
|
||||
|
||||
l = vMin.x();
|
||||
r = vMax.x();
|
||||
b = vMin.y();
|
||||
t = vMax.y();
|
||||
|
||||
if( frustum )
|
||||
projection.makeFrustum( l,r,b,t,n,f );
|
||||
else
|
||||
projection.makeOrtho( l,r,b,t,n,f );
|
||||
}
|
||||
|
765
src/osgShadow/StandardShadowMap.cpp
Normal file
765
src/osgShadow/StandardShadowMap.cpp
Normal file
@ -0,0 +1,765 @@
|
||||
/* -*-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/StandardShadowMap>
|
||||
#include <osg/PolygonOffset>
|
||||
#include <osg/ComputeBoundsVisitor>
|
||||
#include <osgShadow/ShadowedScene>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/CullFace>
|
||||
#include <osg/Point>
|
||||
#include <sstream>
|
||||
|
||||
using namespace osgShadow;
|
||||
|
||||
#define DISPLAY_SHADOW_TEXEL_TO_PIXEL_ERROR 0
|
||||
|
||||
|
||||
StandardShadowMap::StandardShadowMap():
|
||||
BaseClass(),
|
||||
_polygonOffsetFactor( 1.1f ),
|
||||
_polygonOffsetUnits( 4.0f ),
|
||||
_textureSize( 1024, 1024 ),
|
||||
_baseTextureUnit( 0 ),
|
||||
_shadowTextureUnit( 1 ),
|
||||
_baseTextureCoordIndex( 0 ),
|
||||
_shadowTextureCoordIndex( 1 )
|
||||
|
||||
{
|
||||
_mainFragmentShader = new osg::Shader( osg::Shader::FRAGMENT,
|
||||
" // following expressions are auto modified - do not change them: \n"
|
||||
" // gl_TexCoord[0] 0 - can be subsituted with other index \n"
|
||||
" \n"
|
||||
"float DynamicShadow( ); \n"
|
||||
" \n"
|
||||
"varying vec4 colorAmbientEmissive; \n"
|
||||
" \n"
|
||||
"uniform sampler2D baseTexture; \n"
|
||||
" \n"
|
||||
"void main(void) \n"
|
||||
"{ \n"
|
||||
" vec4 color = texture2D( baseTexture, gl_TexCoord[0].xy ); \n"
|
||||
" color *= mix( colorAmbientEmissive, gl_Color, DynamicShadow() ); \n"
|
||||
#if DISPLAY_SHADOW_TEXEL_TO_PIXEL_ERROR
|
||||
" color.xy = abs( dFdy( gl_TexCoord[1].xy / gl_TexCoord[1].w ) )* 1024.0; \n"
|
||||
" color.z = color.y; \n"
|
||||
" color.x = color.z; \n"
|
||||
" color.y = color.z; \n"
|
||||
" color.a = 1.0; \n"
|
||||
#endif
|
||||
// " float fog = clamp((gl_Fog.end - gl_FogFragCoord)*gl_Fog.scale, 0.,1.);\n"
|
||||
// " color.rgb = mix( gl_Fog.color.rgb, color.rgb, fog ); \n"
|
||||
" gl_FragColor = color; \n"
|
||||
"} \n" );
|
||||
|
||||
|
||||
_shadowFragmentShader = new osg::Shader( osg::Shader::FRAGMENT,
|
||||
" // following expressions are auto modified - do not change them: \n"
|
||||
" // gl_TexCoord[1] 1 - can be subsituted with other index \n"
|
||||
" \n"
|
||||
"uniform sampler2DShadow shadowTexture; \n"
|
||||
" \n"
|
||||
"float DynamicShadow( ) \n"
|
||||
"{ \n"
|
||||
" return shadow2DProj( shadowTexture, gl_TexCoord[1] ).r; \n"
|
||||
"} \n" );
|
||||
|
||||
|
||||
|
||||
_shadowVertexShader = new osg::Shader( osg::Shader::VERTEX,
|
||||
" // following expressions are auto modified - do not change them: \n"
|
||||
" // gl_TexCoord[1] 1 - can be subsituted with other index \n"
|
||||
" // gl_EyePlaneS[1] 1 - can be subsituted with other index \n"
|
||||
" // gl_EyePlaneT[1] 1 - can be subsituted with other index \n"
|
||||
" // gl_EyePlaneR[1] 1 - can be subsituted with other index \n"
|
||||
" // gl_EyePlaneQ[1] 1 - can be subsituted with other index \n"
|
||||
" \n"
|
||||
"void DynamicShadow( in vec4 ecPosition ) \n"
|
||||
"{ \n"
|
||||
" // generate coords for shadow mapping \n"
|
||||
" gl_TexCoord[1].s = dot( ecPosition, gl_EyePlaneS[1] ); \n"
|
||||
" gl_TexCoord[1].t = dot( ecPosition, gl_EyePlaneT[1] ); \n"
|
||||
" gl_TexCoord[1].p = dot( ecPosition, gl_EyePlaneR[1] ); \n"
|
||||
" gl_TexCoord[1].q = dot( ecPosition, gl_EyePlaneQ[1] ); \n"
|
||||
"} \n" );
|
||||
|
||||
_mainVertexShader = new osg::Shader( osg::Shader::VERTEX,
|
||||
" // following expressions are auto modified - do not change them: \n"
|
||||
" // gl_TexCoord[0] 0 - can be subsituted with other index \n"
|
||||
" // gl_TextureMatrix[0] 0 - can be subsituted with other index \n"
|
||||
" // gl_MultiTexCoord0 0 - can be subsituted with other index \n"
|
||||
" \n"
|
||||
"const int NumEnabledLights = 1; \n"
|
||||
" \n"
|
||||
"void DynamicShadow( in vec4 ecPosition ); \n"
|
||||
" \n"
|
||||
"varying vec4 colorAmbientEmissive; \n"
|
||||
" \n"
|
||||
"void SpotLight(in int i, \n"
|
||||
" in vec3 eye, \n"
|
||||
" in vec3 ecPosition3, \n"
|
||||
" in vec3 normal, \n"
|
||||
" inout vec4 ambient, \n"
|
||||
" inout vec4 diffuse, \n"
|
||||
" inout vec4 specular) \n"
|
||||
"{ \n"
|
||||
" float nDotVP; // normal . light direction \n"
|
||||
" float nDotHV; // normal . light half vector \n"
|
||||
" float pf; // power factor \n"
|
||||
" float spotDot; // cosine of angle between spotlight \n"
|
||||
" float spotAttenuation; // spotlight attenuation factor \n"
|
||||
" float attenuation; // computed attenuation factor \n"
|
||||
" float d; // distance from surface to light source \n"
|
||||
" vec3 VP; // direction from surface to light position \n"
|
||||
" vec3 halfVector; // direction of maximum highlights \n"
|
||||
" \n"
|
||||
" // Compute vector from surface to light position \n"
|
||||
" VP = vec3(gl_LightSource[i].position) - ecPosition3; \n"
|
||||
" \n"
|
||||
" // Compute distance between surface and light position \n"
|
||||
" d = length(VP); \n"
|
||||
" \n"
|
||||
" // Normalize the vector from surface to light position \n"
|
||||
" VP = normalize(VP); \n"
|
||||
" \n"
|
||||
" // Compute attenuation \n"
|
||||
" attenuation = 1.0 / (gl_LightSource[i].constantAttenuation + \n"
|
||||
" gl_LightSource[i].linearAttenuation * d + \n"
|
||||
" gl_LightSource[i].quadraticAttenuation *d*d); \n"
|
||||
" \n"
|
||||
" // See if point on surface is inside cone of illumination \n"
|
||||
" spotDot = dot(-VP, normalize(gl_LightSource[i].spotDirection)); \n"
|
||||
" \n"
|
||||
" if (spotDot < gl_LightSource[i].spotCosCutoff) \n"
|
||||
" spotAttenuation = 0.0; // light adds no contribution \n"
|
||||
" else \n"
|
||||
" spotAttenuation = pow(spotDot, gl_LightSource[i].spotExponent);\n"
|
||||
" \n"
|
||||
" // Combine the spotlight and distance attenuation. \n"
|
||||
" attenuation *= spotAttenuation; \n"
|
||||
" \n"
|
||||
" halfVector = normalize(VP + eye); \n"
|
||||
" \n"
|
||||
" nDotVP = max(0.0, dot(normal, VP)); \n"
|
||||
" nDotHV = max(0.0, dot(normal, halfVector)); \n"
|
||||
" \n"
|
||||
" if (nDotVP == 0.0) \n"
|
||||
" pf = 0.0; \n"
|
||||
" else \n"
|
||||
" pf = pow(nDotHV, gl_FrontMaterial.shininess); \n"
|
||||
" \n"
|
||||
" ambient += gl_LightSource[i].ambient * attenuation; \n"
|
||||
" diffuse += gl_LightSource[i].diffuse * nDotVP * attenuation; \n"
|
||||
" specular += gl_LightSource[i].specular * pf * attenuation; \n"
|
||||
"} \n"
|
||||
" \n"
|
||||
"void PointLight(in int i, \n"
|
||||
" in vec3 eye, \n"
|
||||
" in vec3 ecPosition3, \n"
|
||||
" in vec3 normal, \n"
|
||||
" inout vec4 ambient, \n"
|
||||
" inout vec4 diffuse, \n"
|
||||
" inout vec4 specular) \n"
|
||||
"{ \n"
|
||||
" float nDotVP; // normal . light direction \n"
|
||||
" float nDotHV; // normal . light half vector \n"
|
||||
" float pf; // power factor \n"
|
||||
" float attenuation; // computed attenuation factor \n"
|
||||
" float d; // distance from surface to light source \n"
|
||||
" vec3 VP; // direction from surface to light position \n"
|
||||
" vec3 halfVector; // direction of maximum highlights \n"
|
||||
" \n"
|
||||
" // Compute vector from surface to light position \n"
|
||||
" VP = vec3(gl_LightSource[i].position) - ecPosition3; \n"
|
||||
" \n"
|
||||
" // Compute distance between surface and light position \n"
|
||||
" d = length(VP); \n"
|
||||
" \n"
|
||||
" // Normalize the vector from surface to light position \n"
|
||||
" VP = normalize(VP); \n"
|
||||
" \n"
|
||||
" // Compute attenuation \n"
|
||||
" attenuation = 1.0 / (gl_LightSource[i].constantAttenuation + \n"
|
||||
" gl_LightSource[i].linearAttenuation * d + \n"
|
||||
" gl_LightSource[i].quadraticAttenuation * d*d);\n"
|
||||
" \n"
|
||||
" halfVector = normalize(VP + eye); \n"
|
||||
" \n"
|
||||
" nDotVP = max(0.0, dot(normal, VP)); \n"
|
||||
" nDotHV = max(0.0, dot(normal, halfVector)); \n"
|
||||
" \n"
|
||||
" if (nDotVP == 0.0) \n"
|
||||
" pf = 0.0; \n"
|
||||
" else \n"
|
||||
" pf = pow(nDotHV, gl_FrontMaterial.shininess); \n"
|
||||
" \n"
|
||||
" ambient += gl_LightSource[i].ambient * attenuation; \n"
|
||||
" diffuse += gl_LightSource[i].diffuse * nDotVP * attenuation; \n"
|
||||
" specular += gl_LightSource[i].specular * pf * attenuation; \n"
|
||||
"} \n"
|
||||
" \n"
|
||||
"void DirectionalLight(in int i, \n"
|
||||
" in vec3 normal, \n"
|
||||
" inout vec4 ambient, \n"
|
||||
" inout vec4 diffuse, \n"
|
||||
" inout vec4 specular) \n"
|
||||
"{ \n"
|
||||
" float nDotVP; // normal . light direction \n"
|
||||
" float nDotHV; // normal . light half vector \n"
|
||||
" float pf; // power factor \n"
|
||||
" \n"
|
||||
" nDotVP = max(0.0, dot(normal, \n"
|
||||
" normalize(vec3(gl_LightSource[i].position)))); \n"
|
||||
" nDotHV = max(0.0, dot(normal, \n"
|
||||
" vec3(gl_LightSource[i].halfVector))); \n"
|
||||
" \n"
|
||||
" if (nDotVP == 0.0) \n"
|
||||
" pf = 0.0; \n"
|
||||
" else \n"
|
||||
" pf = pow(nDotHV, gl_FrontMaterial.shininess); \n"
|
||||
" \n"
|
||||
" ambient += gl_LightSource[i].ambient; \n"
|
||||
" diffuse += gl_LightSource[i].diffuse * nDotVP; \n"
|
||||
" specular += gl_LightSource[i].specular * pf; \n"
|
||||
"} \n"
|
||||
" \n"
|
||||
"void main( ) \n"
|
||||
"{ \n"
|
||||
" // Transform vertex to clip space \n"
|
||||
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n"
|
||||
" vec3 normal = normalize( gl_NormalMatrix * gl_Normal ); \n"
|
||||
" \n"
|
||||
" vec4 ecPos = gl_ModelViewMatrix * gl_Vertex; \n"
|
||||
" float ecLen = length( ecPos ); \n"
|
||||
" vec3 ecPosition3 = ecPos.xyz / ecPos.w; \n"
|
||||
" \n"
|
||||
" vec3 eye = vec3( 0.0, 0.0, 1.0 ); \n"
|
||||
" //vec3 eye = -normalize(ecPosition3); \n"
|
||||
" \n"
|
||||
" DynamicShadow( ecPos ); \n"
|
||||
" \n"
|
||||
" gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; \n"
|
||||
" \n"
|
||||
" // Front Face lighting \n"
|
||||
" \n"
|
||||
" // Clear the light intensity accumulators \n"
|
||||
" vec4 amb = vec4(0.0); \n"
|
||||
" vec4 diff = vec4(0.0); \n"
|
||||
" vec4 spec = vec4(0.0); \n"
|
||||
" \n"
|
||||
" // Loop through enabled lights, compute contribution from each \n"
|
||||
" for (int i = 0; i < NumEnabledLights; i++) \n"
|
||||
" { \n"
|
||||
" if (gl_LightSource[i].position.w == 0.0) \n"
|
||||
" DirectionalLight(i, normal, amb, diff, spec); \n"
|
||||
" else if (gl_LightSource[i].spotCutoff == 180.0) \n"
|
||||
" PointLight(i, eye, ecPosition3, normal, amb, diff, spec); \n"
|
||||
" else \n"
|
||||
" SpotLight(i, eye, ecPosition3, normal, amb, diff, spec); \n"
|
||||
" } \n"
|
||||
" \n"
|
||||
" colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor + \n"
|
||||
" amb * gl_FrontMaterial.ambient; \n"
|
||||
" \n"
|
||||
" gl_FrontColor = colorAmbientEmissive + \n"
|
||||
" diff * gl_FrontMaterial.diffuse; \n"
|
||||
" \n"
|
||||
" gl_FrontSecondaryColor = vec4(spec*gl_FrontMaterial.specular); \n"
|
||||
" \n"
|
||||
" gl_BackColor = gl_FrontColor; \n"
|
||||
" gl_BackSecondaryColor = gl_FrontSecondaryColor; \n"
|
||||
" \n"
|
||||
" gl_FogFragCoord = ecLen; \n"
|
||||
"} \n" );
|
||||
}
|
||||
|
||||
StandardShadowMap::StandardShadowMap
|
||||
(const StandardShadowMap& copy, const osg::CopyOp& copyop) :
|
||||
BaseClass(copy,copyop),
|
||||
_polygonOffsetFactor( copy._polygonOffsetFactor ),
|
||||
_polygonOffsetUnits( copy._polygonOffsetUnits ),
|
||||
_textureSize( copy._textureSize ),
|
||||
_baseTextureUnit( copy._baseTextureUnit ),
|
||||
_shadowTextureUnit( copy._shadowTextureUnit )
|
||||
{
|
||||
if( copy._mainVertexShader.valid() )
|
||||
_mainVertexShader = dynamic_cast<osg::Shader*>
|
||||
( copy._mainVertexShader->clone(copyop) );
|
||||
|
||||
if( copy._mainFragmentShader.valid() )
|
||||
_mainFragmentShader = dynamic_cast<osg::Shader*>
|
||||
( copy._mainFragmentShader->clone(copyop) );
|
||||
|
||||
if( copy._shadowVertexShader.valid() )
|
||||
_shadowVertexShader = dynamic_cast<osg::Shader*>
|
||||
( copy._shadowVertexShader->clone(copyop) );
|
||||
|
||||
if( copy._shadowFragmentShader.valid() )
|
||||
_shadowFragmentShader = dynamic_cast<osg::Shader*>
|
||||
( copy._shadowFragmentShader->clone(copyop) );
|
||||
}
|
||||
|
||||
StandardShadowMap::~StandardShadowMap(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void StandardShadowMap::updateTextureCoordIndices
|
||||
( unsigned int fromTextureCoordIndex, unsigned int toTextureCoordIndex )
|
||||
{
|
||||
if( fromTextureCoordIndex == toTextureCoordIndex ) return;
|
||||
|
||||
const char *expressions[] = {
|
||||
"gl_TexCoord[%d]",
|
||||
"gl_TextureMatrix[%d]",
|
||||
"gl_MultiTexCoord%d",
|
||||
"gl_EyePlaneS[%d]",
|
||||
"gl_EyePlaneT[%d]",
|
||||
"gl_EyePlaneR[%d]",
|
||||
"gl_EyePlaneQ[%d]",
|
||||
};
|
||||
|
||||
for( unsigned int i = 0;
|
||||
i < sizeof( expressions ) / sizeof( expressions[0] );
|
||||
i++ )
|
||||
{
|
||||
char acFrom[ 32 ], acTo[32];
|
||||
|
||||
// its not elegant to mix stdio & stl strings
|
||||
// but in this context I do an exception for cleaner code
|
||||
|
||||
std::sprintf( acFrom, expressions[i], fromTextureCoordIndex );
|
||||
std::sprintf( acTo, expressions[i], toTextureCoordIndex );
|
||||
|
||||
std::string from( acFrom ), to( acTo );
|
||||
|
||||
searchAndReplaceShaderSource( getShadowVertexShader(), from, to );
|
||||
searchAndReplaceShaderSource( getShadowFragmentShader(), from, to );
|
||||
searchAndReplaceShaderSource( getMainVertexShader(), from, to );
|
||||
searchAndReplaceShaderSource( getMainFragmentShader(), from, to );
|
||||
}
|
||||
|
||||
dirty();
|
||||
}
|
||||
|
||||
void StandardShadowMap::searchAndReplaceShaderSource
|
||||
( osg::Shader* shader, std::string fromString, std::string toString )
|
||||
{
|
||||
if( !shader || fromString == toString ) return;
|
||||
|
||||
const std::string & srceString = shader->getShaderSource();
|
||||
std::string destString;
|
||||
|
||||
std::string::size_type fromLength = fromString.length();
|
||||
std::string::size_type srceLength = srceString.length();
|
||||
|
||||
for( std::string::size_type pos = 0; pos < srceLength; )
|
||||
{
|
||||
std::string::size_type end = srceString.find( fromString, pos );
|
||||
|
||||
if( end == std::string::npos )
|
||||
end = srceLength;
|
||||
|
||||
destString.append( srceString, pos, end - pos );
|
||||
|
||||
if( end == srceLength )
|
||||
break;
|
||||
|
||||
destString.append( toString );
|
||||
pos = end + fromLength;
|
||||
}
|
||||
|
||||
shader->setShaderSource( destString );
|
||||
}
|
||||
|
||||
void StandardShadowMap::ViewData::cull()
|
||||
{
|
||||
// step 1:
|
||||
// cull shadowed scene ie put into render bins and states into stage graphs
|
||||
cullShadowReceivingScene( );
|
||||
|
||||
// step 2:
|
||||
// find the light casting our shadows
|
||||
osg::Vec4 lightPos;
|
||||
osg::Vec3 lightDir;
|
||||
osg::Vec3 lightUp( 0,0,0 ); // force computing most approprate dir
|
||||
const osg::Light *light = selectLight( lightPos, lightDir );
|
||||
|
||||
if ( !light )
|
||||
return;// bail out - no shadowing needed in darkest night
|
||||
|
||||
// step 3:
|
||||
// compute shadow casting matrices and apply them to shadow map camera
|
||||
aimShadowCastingCamera( light, lightPos, lightDir, lightUp );
|
||||
|
||||
// step 4:
|
||||
// cull scene casting shadow and generate render
|
||||
cullShadowCastingScene( );
|
||||
|
||||
// step 5:
|
||||
// setup texgen generating shadow map coords for the shadow receiving scene
|
||||
addShadowReceivingTexGen( );
|
||||
|
||||
BaseClass::ViewData::cull();
|
||||
}
|
||||
|
||||
void StandardShadowMap::ViewData::init( ThisClass *st, osgUtil::CullVisitor *cv )
|
||||
{
|
||||
BaseClass::ViewData::init( st, cv );
|
||||
|
||||
_lightPtr = &st->_light;
|
||||
_shadowTextureUnitPtr = &st->_shadowTextureUnit;
|
||||
_baseTextureUnitPtr = &st->_baseTextureUnit;
|
||||
|
||||
_texture = new osg::Texture2D;
|
||||
{ // Setup shadow texture
|
||||
_texture->setTextureSize( st->_textureSize.x(), st->_textureSize.y());
|
||||
_texture->setInternalFormat(GL_DEPTH_COMPONENT);
|
||||
_texture->setShadowComparison(true);
|
||||
_texture->setShadowTextureMode(osg::Texture2D::LUMINANCE);
|
||||
_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
|
||||
_texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
|
||||
|
||||
// the shadow comparison should fail if object is outside the texture
|
||||
_texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
|
||||
_texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
|
||||
_texture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
|
||||
}
|
||||
|
||||
_camera = new osg::Camera;
|
||||
{ // Setup shadow map camera
|
||||
_camera->setName( "ShadowCamera" );
|
||||
#if 0 // Absolute reference frame INHERIT_VIEWPOINT works better than this
|
||||
_camera->setCullingMode
|
||||
( _camera->getCullingMode() & ~osg::CullSettings::SMALL_FEATURE_CULLING );
|
||||
#endif
|
||||
_camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
||||
_camera->setCullCallback(new CameraCullCallback( st ));
|
||||
_camera->setClearMask(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
#if 0 // Left in case of some debug testing
|
||||
_camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||
_camera->setClearColor( osg::Vec4(1.0f,1.0f,1.0f,1.0f) );
|
||||
#endif
|
||||
_camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
_camera->setViewport(0,0, st->_textureSize.x(), st->_textureSize.y() );
|
||||
_camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
_camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
||||
_camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get());
|
||||
}
|
||||
|
||||
_texgen = new osg::TexGen;
|
||||
|
||||
_stateset = new osg::StateSet;
|
||||
{ // Create and add fake texture for use with nodes without any texture
|
||||
osg::Image * image = new osg::Image;
|
||||
image->allocateImage( 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE );
|
||||
*(osg::Vec4ub*)image->data() = osg::Vec4ub( 0xFF, 0xFF, 0xFF, 0xFF );
|
||||
|
||||
osg::Texture2D* fakeTex = new osg::Texture2D( image );
|
||||
fakeTex->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::REPEAT);
|
||||
fakeTex->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT);
|
||||
fakeTex->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);
|
||||
fakeTex->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);
|
||||
|
||||
_stateset->setTextureAttribute(st->_baseTextureUnit,fakeTex,osg::StateAttribute::ON);
|
||||
_stateset->setTextureMode(st->_baseTextureUnit,GL_TEXTURE_1D,osg::StateAttribute::OFF);
|
||||
_stateset->setTextureMode(st->_baseTextureUnit,GL_TEXTURE_2D,osg::StateAttribute::ON);
|
||||
_stateset->setTextureMode(st->_baseTextureUnit,GL_TEXTURE_3D,osg::StateAttribute::OFF);
|
||||
}
|
||||
|
||||
{ // Add shadow texture
|
||||
_stateset->setTextureAttributeAndModes(st->_shadowTextureUnit,_texture.get(),osg::StateAttribute::ON);
|
||||
_stateset->setTextureMode(st->_shadowTextureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
||||
_stateset->setTextureMode(st->_shadowTextureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
||||
_stateset->setTextureMode(st->_shadowTextureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
|
||||
_stateset->setTextureMode(st->_shadowTextureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
|
||||
}
|
||||
|
||||
{ // Setup shaders used in shadow casting
|
||||
osg::Program * program = new osg::Program();
|
||||
_stateset->setAttribute( program );
|
||||
|
||||
if( st->_shadowFragmentShader.valid() )
|
||||
program->addShader( st->_shadowFragmentShader.get() );
|
||||
|
||||
if( st->_mainFragmentShader.valid() )
|
||||
program->addShader( st->_mainFragmentShader.get() );
|
||||
|
||||
if( st->_shadowVertexShader.valid() )
|
||||
program->addShader( st->_shadowVertexShader.get() );
|
||||
|
||||
if( st->_mainVertexShader.valid() )
|
||||
program->addShader( st->_mainVertexShader.get() );
|
||||
|
||||
_stateset->addUniform
|
||||
( new osg::Uniform( "baseTexture", int( st->_baseTextureUnit ) ) );
|
||||
_stateset->addUniform
|
||||
( new osg::Uniform( "shadowTexture", int( st->_shadowTextureUnit ) ) );
|
||||
}
|
||||
|
||||
{ // Setup states used for shadow map generation
|
||||
osg::StateSet * stateset = _camera->getOrCreateStateSet();
|
||||
|
||||
stateset->setAttribute(
|
||||
new osg::PolygonOffset( st->_polygonOffsetFactor, st->_polygonOffsetUnits ),
|
||||
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
|
||||
|
||||
stateset->setMode( GL_POLYGON_OFFSET_FILL,
|
||||
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
|
||||
|
||||
// agressive optimization
|
||||
stateset->setRenderBinDetails( 0, "RenderBin",
|
||||
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS );
|
||||
|
||||
// agressive optimization
|
||||
stateset->setAttributeAndModes
|
||||
( new osg::ColorMask( false, false, false, false ),
|
||||
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
|
||||
|
||||
// note soft (attribute only no mode override) setting. When this works ?
|
||||
// 1. for objects prepared for backface culling
|
||||
// because they usually also set CullFace and CullMode on in their state
|
||||
// For them we override CullFace but CullMode remains set by them
|
||||
// 2. For one faced, trees, and similar objects which cannot use
|
||||
// backface nor front face so they usually use CullMode off set here.
|
||||
// In this case we will draw them in their entirety.
|
||||
|
||||
stateset->setAttribute( new osg::CullFace( osg::CullFace::FRONT ),
|
||||
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
|
||||
|
||||
// make sure GL_CULL_FACE is off by default
|
||||
// we assume that if object has cull face attribute set to back
|
||||
// it will also set cull face mode ON so no need for override
|
||||
stateset->setMode( GL_CULL_FACE, osg::StateAttribute::OFF );
|
||||
|
||||
// optimization attributes
|
||||
osg::Program* program = new osg::Program;
|
||||
stateset->setAttribute( program, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON );
|
||||
stateset->setMode
|
||||
( GL_LIGHTING, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
||||
stateset->setMode
|
||||
( GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
||||
|
||||
#if 0
|
||||
stateset->setMode
|
||||
( GL_ALPHA_TEST, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
||||
#endif
|
||||
|
||||
#if 0 // fixed pipeline seems faster (at least on my 7800)
|
||||
program->addShader( new osg::Shader( osg::Shader::FRAGMENT,
|
||||
"uniform sampler2D texture; \n"
|
||||
"void main(void) \n"
|
||||
"{ \n"
|
||||
" gl_FragColor = texture2D( texture, gl_TexCoord[0].xy ); \n"
|
||||
"} \n"
|
||||
) ); // program->addShader Fragment
|
||||
|
||||
program->addShader( new osg::Shader( osg::Shader::VERTEX,
|
||||
"void main(void) \n"
|
||||
"{ \n"
|
||||
" gl_Position = ftransform(); \n"
|
||||
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
|
||||
"} \n"
|
||||
) ); // program->addShader Vertex
|
||||
#endif
|
||||
|
||||
for( unsigned stage = 1; stage < 4; stage ++ )
|
||||
{
|
||||
stateset->setTextureMode( stage, GL_TEXTURE_1D, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
||||
stateset->setTextureMode( stage, GL_TEXTURE_2D, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
||||
stateset->setTextureMode( stage, GL_TEXTURE_3D, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const osg::Light* StandardShadowMap::ViewData::selectLight
|
||||
( osg::Vec4 & lightPos, osg::Vec3 & lightDir )
|
||||
{
|
||||
const osg::Light* light = 0;
|
||||
|
||||
//MR testing giving a specific light
|
||||
osgUtil::RenderStage * rs = _cv->getRenderStage();
|
||||
|
||||
osgUtil::PositionalStateContainer::AttrMatrixList& aml =
|
||||
rs->getPositionalStateContainer()->getAttrMatrixList();
|
||||
|
||||
osg::RefMatrix* matrix;
|
||||
|
||||
for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
|
||||
itr != aml.end();
|
||||
++itr)
|
||||
{
|
||||
const osg::Light* found = dynamic_cast<const osg::Light*>(itr->first.get());
|
||||
if( found )
|
||||
{
|
||||
if( _lightPtr->valid() && _lightPtr->get() != found )
|
||||
continue; // continue search for the right one
|
||||
|
||||
light = found;
|
||||
matrix = itr->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
if( light ) { // transform light to world space
|
||||
|
||||
osg::Matrix localToWorld = osg::Matrix::inverse( *_cv->getModelViewMatrix() );
|
||||
if( matrix ) localToWorld.preMult( *matrix );
|
||||
|
||||
lightPos = light->getPosition();
|
||||
|
||||
if( lightPos[3] == 0 )
|
||||
lightDir.set( -lightPos[0], -lightPos[1], -lightPos[2] );
|
||||
else
|
||||
lightDir = light->getDirection();
|
||||
|
||||
lightPos = lightPos * localToWorld;
|
||||
lightDir = osg::Matrix::transform3x3( lightDir, localToWorld );
|
||||
lightDir.normalize();
|
||||
}
|
||||
|
||||
return light;
|
||||
}
|
||||
|
||||
void StandardShadowMap::ViewData::aimShadowCastingCamera( const osg::Light *light,
|
||||
const osg::Vec4 &lightPos,
|
||||
const osg::Vec3 &lightDir,
|
||||
const osg::Vec3 &lightUp
|
||||
/* by default = osg::Vec3( 0, 1 0 )*/ )
|
||||
{
|
||||
#if 0 // less precise but faster
|
||||
osg::BoundingSphere bs =_st->getShadowedScene()->getBound();
|
||||
#else
|
||||
// get the bounds of the model.
|
||||
osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
|
||||
cbbv.setTraversalMask(_st->getShadowedScene()->getCastsShadowTraversalMask());
|
||||
_st->getShadowedScene()->osg::Group::traverse(cbbv);
|
||||
osg::BoundingSphere bs( cbbv.getBoundingBox() );
|
||||
#endif
|
||||
|
||||
aimShadowCastingCamera
|
||||
( bs, light, lightPos, lightDir, lightUp );
|
||||
}
|
||||
|
||||
void StandardShadowMap::ViewData::aimShadowCastingCamera(
|
||||
const osg::BoundingSphere &bs,
|
||||
const osg::Light *light,
|
||||
const osg::Vec4 &lightPos,
|
||||
const osg::Vec3 &lightDir,
|
||||
const osg::Vec3 &lightUpVector
|
||||
/* by default = osg::Vec3( 0, 1 0 )*/ )
|
||||
{
|
||||
osg::Matrix & view = _camera->getViewMatrix();
|
||||
osg::Matrix & projection = _camera->getProjectionMatrix();
|
||||
|
||||
osg::Vec3 up = lightUpVector;
|
||||
if( up.length2() <= 0 ) up.set( 0,1,0 );
|
||||
|
||||
if( light->getSpotCutoff() < 180.0f) // spotlight, no need for bounding box
|
||||
{
|
||||
osg::Vec3 position(lightPos.x(), lightPos.y(), lightPos.z());
|
||||
float spotAngle = light->getSpotCutoff();
|
||||
|
||||
projection.makePerspective( spotAngle, 1.0, 0.1, 1000.0);
|
||||
view.makeLookAt(position,position+lightDir,up);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lightPos[3]!=0.0) // point light
|
||||
{
|
||||
osg::Vec3 position(lightPos.x(), lightPos.y(), lightPos.z());
|
||||
|
||||
float centerDistance = (position-bs.center()).length();
|
||||
|
||||
float znear = centerDistance-bs.radius();
|
||||
float zfar = centerDistance+bs.radius();
|
||||
float zNearRatio = 0.001f;
|
||||
if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
|
||||
|
||||
float top = (bs.radius()/centerDistance)*znear;
|
||||
float right = top;
|
||||
|
||||
projection.makeFrustum(-right,right,-top,top,znear,zfar);
|
||||
view.makeLookAt(position,bs.center(),up );
|
||||
}
|
||||
else // directional light
|
||||
{
|
||||
// make an orthographic projection
|
||||
|
||||
// set the position far away along the light direction
|
||||
float radius = bs.radius();
|
||||
osg::Vec3 position = bs.center() - lightDir * radius * 2;
|
||||
|
||||
float centerDistance = (position-bs.center()).length();
|
||||
|
||||
float znear = centerDistance-radius;
|
||||
float zfar = centerDistance+radius;
|
||||
float zNearRatio = 0.001f;
|
||||
if (znear<zfar*zNearRatio)
|
||||
znear = zfar*zNearRatio;
|
||||
|
||||
float top = radius;
|
||||
float right = top;
|
||||
|
||||
projection.makeOrtho(-right, right, -top, top, znear, zfar);
|
||||
view.makeLookAt(position,bs.center(),up);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StandardShadowMap::ViewData::cullShadowReceivingScene( )
|
||||
{
|
||||
_cv->pushStateSet( _stateset.get() );
|
||||
|
||||
_st->getShadowedScene()->osg::Group::traverse( *_cv );
|
||||
|
||||
_cv->popStateSet();
|
||||
}
|
||||
|
||||
void StandardShadowMap::ViewData::cullShadowCastingScene( )
|
||||
{
|
||||
// record the traversal mask on entry so we can reapply it later.
|
||||
unsigned int traversalMask = _cv->getTraversalMask();
|
||||
|
||||
_cv->setTraversalMask( traversalMask &
|
||||
_st->getShadowedScene()->getCastsShadowTraversalMask() );
|
||||
|
||||
// do RTT camera traversal
|
||||
_camera->accept(*_cv);
|
||||
|
||||
// reapply the original traversal mask
|
||||
_cv->setTraversalMask( traversalMask );
|
||||
}
|
||||
|
||||
void StandardShadowMap::ViewData::addShadowReceivingTexGen( )
|
||||
{
|
||||
_texgen->setMode(osg::TexGen::EYE_LINEAR);
|
||||
|
||||
// compute the matrix which takes a vertex from view coords into tex coords
|
||||
_texgen->setPlanesFromMatrix(
|
||||
_camera->getProjectionMatrix() *
|
||||
osg::Matrix::translate(1.0,1.0,1.0) *
|
||||
osg::Matrix::scale(0.5f,0.5f,0.5f) );
|
||||
|
||||
osg::RefMatrix * refMatrix = new osg::RefMatrix
|
||||
( _camera->getInverseViewMatrix() * *_cv->getModelViewMatrix() );
|
||||
|
||||
_cv->getRenderStage()->getPositionalStateContainer()->
|
||||
addPositionedTextureAttribute
|
||||
( *_shadowTextureUnitPtr, refMatrix, _texgen.get() );
|
||||
}
|
||||
|
127
src/osgShadow/ViewDependentShadowTechnique.cpp
Normal file
127
src/osgShadow/ViewDependentShadowTechnique.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
/* -*-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/ViewDependentShadowTechnique>
|
||||
#include <osgShadow/ShadowedScene>
|
||||
|
||||
using namespace osgShadow;
|
||||
|
||||
|
||||
ViewDependentShadowTechnique::ViewDependentShadowTechnique()
|
||||
{
|
||||
dirty();
|
||||
}
|
||||
|
||||
ViewDependentShadowTechnique::ViewDependentShadowTechnique
|
||||
(const ViewDependentShadowTechnique& copy, const osg::CopyOp& copyop):
|
||||
ShadowTechnique(copy,copyop)
|
||||
{
|
||||
dirty();
|
||||
}
|
||||
|
||||
ViewDependentShadowTechnique::~ViewDependentShadowTechnique(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::traverse(osg::NodeVisitor& nv)
|
||||
{
|
||||
osgShadow::ShadowTechnique::traverse(nv);
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::dirty()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_viewDataMapMutex);
|
||||
|
||||
osgShadow::ShadowTechnique::_dirty = true;
|
||||
|
||||
for( ViewDataMap::iterator mitr = _viewDataMap.begin();
|
||||
mitr != _viewDataMap.end();
|
||||
++mitr )
|
||||
{
|
||||
mitr->second->dirty( true );
|
||||
}
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::init()
|
||||
{
|
||||
//osgShadow::ShadowTechnique::init( );
|
||||
osgShadow::ShadowTechnique::_dirty = false;
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::update(osg::NodeVisitor& nv)
|
||||
{
|
||||
//osgShadow::ShadowTechnique::update( nv );
|
||||
osgShadow::ShadowTechnique::_shadowedScene->osg::Group::traverse(nv);
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||
{
|
||||
//osgShadow::ShadowTechnique::cull( cv );
|
||||
|
||||
ViewData * vd = getViewDependentData( &cv );
|
||||
|
||||
if ( !vd || vd->_dirty || vd->_cv != &cv || vd->_st != this ) {
|
||||
vd = initViewDependentData( &cv, vd );
|
||||
setViewDependentData( &cv, vd );
|
||||
}
|
||||
|
||||
if( vd ) {
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(vd->_mutex);
|
||||
vd->cull();
|
||||
} else {
|
||||
osgShadow::ShadowTechnique::_shadowedScene->osg::Group::traverse(cv);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::cleanSceneGraph()
|
||||
{
|
||||
//osgShadow::ShadowTechnique::cleanSceneGraph( );
|
||||
}
|
||||
|
||||
ViewDependentShadowTechnique::ViewData *
|
||||
ViewDependentShadowTechnique::getViewDependentData( osgUtil::CullVisitor * cv )
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_viewDataMapMutex);
|
||||
return _viewDataMap[ cv ].get();
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::setViewDependentData
|
||||
( osgUtil::CullVisitor * cv, ViewData * data )
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_viewDataMapMutex);
|
||||
_viewDataMap[ cv ] = data;
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::ViewData::dirty( bool flag )
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
||||
_dirty = flag;
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::ViewData::init
|
||||
( ViewDependentShadowTechnique *st, osgUtil::CullVisitor * cv )
|
||||
{
|
||||
_cv = cv;
|
||||
_st = st;
|
||||
dirty( false );
|
||||
}
|
||||
|
||||
void ViewDependentShadowTechnique::ViewData::cull( void )
|
||||
{
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user