Upcoming Events
VIEW Conference 2009
11/4 - 11/7 @ Turin, Italy

Project Horseshoe
11/5 - 11/8 @ Burnet, TX

Independent Game Conference West
11/5 - 11/6 @ Los Angeles, CA

IGDA Leadership Forum
11/12 - 11/13 @ San Francisco, CA

More events...


Quick Stats
6480 people currently visiting GDNet.
2337 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!



Link to us

Link to us

  search:   

Shadow Caster Volumes For The Culling Of Potential Shadow Casters


Listing 1

/** 
 * Gets the volume containing all objects that can cast a shadow 
 * into the view frustum.
 * @param frustum view frustum 
 * @return shadow caster volume 
 */ 
Polyhedron<double> PointLightSource::shadowCasterVolume( Polyhedron<double>const& frustum ) const 
{ 
	// If lightsource lies inside frustum, then simply return frustum.
	if ( frustum.pointInside( _position ) ) { 
		return frustum; 
	} 
	
	// Create a new polyhedron, using all frustum planes for which the light 
	// source lies on the positive side of (or lies exactly on the plane), 
	// and creating new planes for each edge that is shared by one plane that 
	// points towards the light and one that points away. 
	
	// First copy all planes pointing towards the light source 
	Polyhedron<double> shadowVol; 
	Polyhedron<double>::PlanesItors planesItors = frustum.getPlanes(); 
	for ( Polyhedron<double>::PlanesItor p=planesItors.first; p!=planesItors.second; ++p ) 
	{ 
		if ( p->dist(_position) >= 0 ) { 
			shadowVol.addPlane( *p ); 
		} 
	} 
	
	// Calculate the average of the frustum vertices. 
	// This is used for determining the direction of the 
	// normals for the new planes. 
	Polyhedron<double>::VerticesItors const verticesItors = frustum.getVertices(); 
	Polyhedron<double>::Vertex verticesAverage( 0.f ); 
	double numVertices = 0; 
	for ( Polyhedron<double>::VerticesItor v=verticesItors.first; v!=verticesItors.second; ++v ) 
	{ 
		verticesAverage += *v; 
		++numVertices; 
	} 
	verticesAverage /= numVertices; 
	
	// Now add new planes for all silloute edges 
	Polyhedron<double>::EdgesItors const edgesItors = frustum.getEdges(); 
	for ( Polyhedron<double>::EdgesItor e=edgesItors.first; e!=edgesItors.second; ++e ) 
	{ 
		// One plane faces light and other faces away (or is parallel) ? 
		bool const p0pos = e->plane[0]->dist(_position) >= 0; 
		bool const p1pos = e->plane[1]->dist(_position) >= 0; 
		if ( p0pos != p1pos ) { 
		
			// Create new plane 
			Vector<3, double> n( (e->end[0]-_position).cross(e->end[0]-e->end[1]).norm() ); 
			Plane<double> p( n, e->end[0] ); 
			// Ensure the normal is pointing in the correct direction. 
			if ( p.dist( verticesAverage ) < 0 ) { 
				p = -p; 
			} 
			
			// Save the plane 
			shadowVol.addPlane( p ); 
		} 
	} 
	
	return shadowVol; 
} 

Listing 2

/** 
 * Convex polyhedron. 
 * 
 * Note that there is no checking to enforce convexity. 
 * 
 * Polyhedron is defined as the intersection of the 
 * positive half-spaces for a list of planes. 
 * That is, plane normals must point inwards. 
 * 
 * Degenerate infinite polyhedra are supported, the 
 * simplest case of this being a half-space. 
 */ 
template < class T= double > 
class Polyhedron { 

	typedef std::vector< Plane<T> > Planes; 
	
public : 

	/** 
	 * Constructor 
	 */ 
	Polyhedron() 
	: _recalculateVertices( true ) 
	, _recalculateEdges( true ) 
	{ 
	} 
	
	/** 
	* Adds a plane to polyhedron. 
	* @param p plane 
	*/ 
	void addPlane( Plane<T> const& p ) { 
		_planes.push_back( p ); 
		_recalculateVertices = true ; 
	} 
	
	/// Const iterator over a collection of planes 
	typedef typename Planes::const_iterator PlanesItor; 
	
	/// Pair of PlanesItor's, where first is begin, and second is end. 
	typedef std::pair<PlanesItor,PlanesItor> PlanesItors; 
	
	/** 
	 * Get the begin and end const iterators over the planes. 
	 * @return plane begin and end iterators 
	 */ 
	PlanesItors getPlanes() const { 
		return PlanesItors( _planes.begin(), _planes.end() ); 
	} 
	
	/** 
	 * Test if a point lies in (or on) the polyhedron. 
	 * @param point point 
	 * @param error each "point" is given a radius of 
	 * max(error, error*plane's distance to origin) 
	 * @return true iff point lies in or on the polyhedron 
	 */ 
	bool pointInside( Vector<3,T> const& point, T error=1e-3 ) const { 
		for ( PlanesItor i=_planes.begin(); i!=_planes.end(); ++i ) { 
			if ( i->dist(point) < -std::max( error, std::fabs(error*i->d()) ) ) 
			{ 
				return false ; 
			} 
		} 
		return true ; 
	} 
	
private : 
	
	/// Vertex of the polyhedron 
	struct PlanesVertex { 
		PlanesItor 	plane[3]; 	///< planes that meet at the vertex 
		Vector<3,T> 	pos; 		///< vertex position 
	}; 
	
	/// Collection of PlanesVertex's 
	typedef std::vector<PlanesVertex> PlanesVertices; 
	
public : 

	/// Vertex of the polyhedron 
	typedef Vector<3,T> Vertex; 

private : 

	struct VertexCmp { 
		/** 
		 * Vertex comparison operator for use in std::set. 
		 * @param v0 first vertex 
		 * @param v1 second vertex 
		 * @return true iff v0 "is less than" v1 
		 */ 
		bool operator ()( Vertex const & v0, Vertex const & v1 ) const { 
			if ( v0[0] < v1[0] ) return true ; 
			if ( v0[0] > v1[0] ) return false ; 
			if ( v0[1] < v1[1] ) return true ; 
			if ( v0[1] > v1[1] ) return false ; 
			return v0[2] < v1[2]; 
		} 
	}; 
	
	/// Collection of Vertex's 
	typedef std::set<Vertex,VertexCmp> Vertices; 
	
public : 
	
	/// Const iterator over a collection of vertices 
	typedef typename Vertices::const_iterator VerticesItor; 
	
	/// Pair of VerticesItor's, where first is begin, and second is end. 
	typedef std::pair<VerticesItor,VerticesItor> VerticesItors; 
	
	/** 
	 * Gets the begin and end const iterators overt the vertices 
	 * @return vertex begin and end iterators 
	 */ 
	VerticesItors getVertices() const { 
		if ( _recalculateVertices ) { 
			_recalculateVertices = false ; 
			_recalculateEdges = true ; 
			
			// First calculate _planesVertices 
			_planesVertices.clear(); 
			for ( PlanesItor p0=_planes.begin(); p0!=_planes.end(); ++p0 ) { 
				PlanesItor p1 = p0; 
				for ( ++p1; p1!=_planes.end(); ++p1 ) { 
					PlanesItor p2 = p1; 
					for ( ++p2; p2!=_planes.end(); ++p2 ) { 
						// Test if the planes intersect at a point 
						Matrix<3,3,T> m; 
						Vector<3,T> const& n0( p0->n() ); 
						Vector<3,T> const& n1( p1->n() ); 
						Vector<3,T> const& n2( p2->n() ); 
						m[0][0] = n0[0]; m[0][1] = n0[1]; m[0][2] = n0[2]; 
						m[1][0] = n1[0]; m[1][1] = n1[1]; m[1][2] = n1[2]; 
						m[2][0] = n2[0]; m[2][1] = n2[1]; m[2][2] = n2[2]; 
						
						// Find the point of intersection 
						typename Matrix<3,3,T>::Lup const lup( m, false ); 
						if ( !lup.failed() ) { 
							Vector<3,T> const d(-p0->d(),-p1->d(),-p2->d()); 
							Vector<3,T> const pos( lup.solve(d) ); 
							
							// Now check that the point is in or on 
							// the polyhedron 
							if ( pointInside( pos ) ) { 
								// Save the vertex 
								PlanesVertex const pv = { {p0,p1,p2}, pos }; 
								_planesVertices.push_back( pv ); 
							} 
						} 
					} 
				} 
			} 
			
			// Now calculate unique set of vertex positions 
			_vertices.clear(); 
			for ( typename PlanesVertices::const_iterator pv=_planesVertices.begin(); pv!=_planesVertices.end(); ++pv ) 
			{ 
				_vertices.insert( Vertex(pv->pos) ); 
			} 
		} 
		return VerticesItors( _vertices.begin(), _vertices.end() ); 
	} 
	
	/// Edge of polyhedron 
	struct Edge { 
		PlanesItor 	plane[2]; 	///< planes that meet to form the edge 
		Vector<3,T> 	end[2]; 	///< vertex positions 
	}; 
	
private : 

	struct EdgeCmp { 
		/** 
		 * Edge comparison operator for use in std::set. 
		 * @param e0 first edge 
		 * @param e1 second edge 
		 * @return true iff e0 "is less than" e1 
		 */ 
		bool operator ()( Edge const & e0, Edge const & e1 ) const { 
			for ( unsigned i=0; i<2; ++i ) { 
				for ( unsigned j=0; j<3; ++j ) { 
					if ( e0.plane[i]->n()[j] < e1.plane[i]->n()[j] ) 
						return true ; 
					if ( e0.plane[i]->n()[j] > e1.plane[i]->n()[j] ) 
						return false ; 
				} 
				if ( e0.plane[i]->d() < e1.plane[i]->d() ) return true ; 
				if ( e0.plane[i]->d() > e1.plane[i]->d() ) return false ; 
			} 
			for ( unsigned i=0; i<2; ++i ) { 
				for ( unsigned j=0; j<3; ++j ) { 
					if ( e0.end[i][j] < e1.end[i][j] ) return true ; 
					if ( e0.end[i][j] > e1.end[i][j] ) return false ; 
				} 
			} 
			return false ; 
		} 
	}; 
	
	/// Collection of Edge's 
	typedef std::set<Edge,EdgeCmp> Edges; 
	
public : 

	/// Const iterator over collection of edges 
	typedef typename Edges::const_iterator EdgesItor; 
	
	/// Pair of EdgesItor's, where first is begin, and second is end. 
	typedef std::pair<EdgesItor,EdgesItor> EdgesItors; 
	
	/** 
	 * Gets the begin and end const iterators overt the edges 
	 * @return edge begin and end iterators 
	 */ 
	EdgesItors getEdges() const { 
		getVertices(); 
		if ( _recalculateEdges ) { 
			_recalculateEdges = false ; 
			
			_edges.clear(); 
			
			for ( typename PlanesVertices::const_iterator v0=_planesVertices.begin(); v0!=_planesVertices.end(); ++v0 ) 
			{ 
				typename PlanesVertices::const_iterator v1 = v0; 
				for ( ++v1; v1!=_planesVertices.end(); ++v1 ) { 

					// Check that the two vertices are not the same point 
					// (which will happen if more than three planes meet 
					// at the same point) 
					if ( v0->pos != v1->pos ) { 
						
						// For every common pair of planes, add an edge 
						#define _POLYHEDRON_CMP_PLANE(n) \ 
							( v0->plane[n] == v1->plane[0] || \ 
							v0->plane[n] == v1->plane[1] || \ 
							v0->plane[n] == v1->plane[2] ) 
						#define _POLYHEDRON_ADD_EDGE(i,j) \ 
							do { \ 
								Edge const e = { { v0->plane[i], \ 
										   v0->plane[j] }, \ 
										 { v0->pos, v1->pos } }; \ 
								_edges.insert( e ); \ 
							} while (0) 
						if ( _POLYHEDRON_CMP_PLANE(0) ) { 
							if ( _POLYHEDRON_CMP_PLANE(1) ) { 
								_POLYHEDRON_ADD_EDGE(0,1); 
							} 
							else if ( _POLYHEDRON_CMP_PLANE(2) ) { 
								_POLYHEDRON_ADD_EDGE(0,2); 
							} 
						} 
						else if ( _POLYHEDRON_CMP_PLANE(1) ) { 
							if ( _POLYHEDRON_CMP_PLANE(2) ) { 
								_POLYHEDRON_ADD_EDGE(1,2); 
							} 
						} 
						#undef _POLYHEDRON_ADD_EDGE 
						#undef _POLYHEDRON_CMP_PLANE 
					} 
				} 
			} 
		} 
		return EdgesItors( _edges.begin(), _edges.end() ); 
	} 
private : 

	Planes 			_planes; 
	mutable bool 		_recalculateVertices; 
	mutable PlanesVertices 	_planesVertices; 
	mutable Vertices 	_vertices; 
	mutable bool 		_recalculateEdges; 
	mutable Edges 		_edges; 
}; 

Listing 3

/** 
 * LUP decomposition of a matrix. 
 * For a matrix non-singular square matrix M, it's decomposition satisfies 
 * PM = LU 
 */ 
template < int ROWS, int COLS, class T > 
class Matrix<ROWS,COLS,T>::Lup { 
BOOST_STATIC_ASSERT( ROWS == COLS ); 

public : 

	/** 
	 * Constructor 
	 * @param m matrix to decompose 
	 * @param canThrow if set, MatrixSingularException will be thrown if 
	 * LUP decomposition fails (the default), otherwise, 
	 * use the failed() method to test whether or not 
	 * the decomposition was successful 
	 */ 
	explicit Lup( Matrix const & m, bool canThrow= true ) 
	: _failed( true ) 
	, _lu( m ) 
	{ 
		// This is an implementation of the LUP-DECOMPOSITION algorithm 
		// presented in Cohen, Leiserson, Rivest and Stein. "Introduction To 
		// Algorithms", 2nd Ed. MIT Press. 2001. 
		
		// Initialise _p to identity transform 
		for ( unsigned i=0; i<ROWS; ++i ) { 
			_p[i] = i; 
		} 

		// Loop through each column 
		// While the last iteration does not modify the matrix _lu in anyway, 
		// it is required to check for singularity. 
		for ( int k=0; k<COLS; ++k ) { 
		
			// Find the row with the largest absolute value element 
			T maxElem = 0; 
			int maxRow; 
			for ( int i=k; i<ROWS; ++i ) { 
				T const absmik = std::fabs( _lu[i][k] ); 
				if ( absmik > maxElem ) { 
					maxElem = absmik; 
					maxRow = i; 
				} 
			} 
			if ( equals(maxElem,0) ) { 	// maxElem approx equal to zero 
				if ( canThrow ) { 
					throw MatrixSingularException(); 
				} 
				else { 
					return ; 
				} 
			} 
			
			// Swap rows k and maxRow 
			std::swap( _p[k], _p[maxRow] ); 
			_lu.swapRows( k, maxRow ); 
			
			// Perform LU decomposition 
			T const mkk = _lu[k][k]; 
			for ( int i=k+1; i<ROWS; ++i ) { 
				_lu[i][k] /= mkk; 
				T const mik = _lu[i][k]; 
				for ( int j=k+1; j<COLS; ++j ) { 
					_lu[i][j] -= mik * _lu[k][j]; 
				} 
			} 
		} 
		_failed = false ; 
	} 
	
	/** 
	 * Test whether or not the LUP decomposition was successful. 
	 * Note that the constructor was not called with canThrow=false, 
	 * then failed() will always return false. 
	 * @return false iff LUP decomposition was successful 
	 */ 
	bool failed() const { 
		return _failed; 
	} 
	
	/** 
	 * Solve decomposed set of linear equations. 
	 * @param c vector containing constants 
	 * @return solution 
	 */ 
	Vector<COLS,T> solve( Vector<COLS,T> const & c=(Vector<COLS,T>((T)0)) ) const { 
	
		// This is an implementation of the LUP-SOLVE algorithm presented in 
		// Cohen, Leiserson, Rivest and Stein. "Introduction To Algorithms", 
		// 2nd Ed. MIT Press. 2001. 
		
		Vector<COLS,T> x; 
		
		// Forward substitution to solve Ly = Pc, where y = Ux 
		// Bit confusing, but we are actually storing y in x, to save 
		// creating a second vector! 
		for ( int i=0; i<ROWS; ++i ) { 
			T sum = 0; 
			for ( int j=0; j<i; ++j ) { 
				sum += _lu[i][j] * x[j]; 
			} 
			x[i] = c[_p[i]] - sum; 
		} 
		
		// Backwards substitution to solve Ux = y 
		for ( int i=ROWS-1; i>=0; --i ) { 
			T sum = 0; 
			for ( int j=i+1; j<ROWS; ++j ) { 
				sum += _lu[i][j] * x[j]; 
			} 
			x[i] = (x[i] - sum) / _lu[i][i]; 
		} 
		
		return x; 
	} 
	
private : 

	bool 	_failed; 	///< set iff LUP decomposition failed 
				///< (input matrix was singular) 
	Matrix 	_lu; 		///< LU matrices combined (since L is 
				///< unit lower-triangular, the 1's on it's diagonal 
				///< are implied) 
	int 	_p[ROWS]; 	///< P matrix converted into array form, 
				///< where P[i][j] = (j==_p[i]) 
}; 



Contents
  1.0 Introduction
  2.0 Point Light Sources
  2.1 LUP Decomposition
  2.2 Calculating the New Shadow Caster Volume Planes
  3.0 Directional Light Sources
  4.0 Semi-Transparent Objects
  5.0 Summary
  Listings

  Printable version
  Discuss this article