Advertisement

Shadows volumes: what's wrong ??!

Started by November 09, 2004 08:33 AM
25 comments, last by RipTorn 20 years ago
it would be great if u post the implementation of that algorithm... i'm still trying to make my one work :P
Here's my implementation for shadow volumes. The code is not as clean as it should be but it works (or so it seems).
Please feel free to send feedback about it !

Header :

	class ShadowVolume {		struct triangle_t {			unsigned int vertex[3];			int edge[3];		};		struct edge_t {			unsigned int vertex[2];			char value;			bool operator <(const edge_t& e) const;			edge_t& operator =(const edge_t& e);		};		unsigned int	m_nb_triangles;		triangle_t*	m_triangles;		unsigned int	m_nb_edges;		edge_t*		m_edges;		static bool create_edge(edge_t& e,					const unsigned int v1,					const unsigned int v2);		static void quick_sort(const edge_t* edges,				       unsigned int* indices,				       const unsigned int l,				       const unsigned int r);		static void insertion_sort(const edge_t* edges,					   unsigned int* indices,					   const unsigned int l,					   const unsigned int r);		static void sort_edges(const edge_t* edges,				       unsigned int* indices,				       const unsigned int l,				       const unsigned int r);	public:		ShadowVolume(const Triangle* tris, const unsigned int nb_tris);		~ShadowVolume();		void draw(Vector3* vertices) const;		enum method_t {			z_pass, z_fail		};		static void begin(const method_t method = z_pass);		static void end_shadow();		static void end();	};


Implementation :
The interesting parts are the constructor and the draw method. The begin, end_shadow and end methods describe the "shadow volume flow and can be used as follow :
- draw the scene with ambient light
- for each light source
- ShadowVolume::begin()
- draw shadow volumes
- ShadowVolume::end_shadow()
- draw the scene with diffuse and specular lights
- ShadowVolume::end()

Note that the z-pass part is not finished yet.

	bool	ShadowVolume::edge_t::	operator <(const edge_t& e) const {		return (   (vertex[0] < e.vertex[0])			|| (vertex[0] == e.vertex[0] && vertex[1] < e.vertex[1]));	}	ShadowVolume::edge_t&	ShadowVolume::edge_t::	operator =(const edge_t& e) {		vertex[0] = e.vertex[0];		vertex[1] = e.vertex[1];		value = 0;		return *this;	}	bool	ShadowVolume::	create_edge(edge_t& e,		    const unsigned int v1,		    const unsigned int v2) {		if (v1 < v2) {			e.vertex[0] = v1;			e.vertex[1] = v2;			return true;		} else {			e.vertex[0] = v2;			e.vertex[1] = v1;			return false;		}	}	#define swap(i, j) { unsigned int tmp = indices; indices = indices[j]; indices[j] = tmp; }	void	ShadowVolume::	quick_sort(const edge_t* edges,		   unsigned int* indices,		   const unsigned int l,		   const unsigned int r) {		unsigned short M = 4;		unsigned short i, j;		edge_t v;		if (r-l > M) {			i = (r+l)>>1;			if (edges[indices] < edges[indices[l]]) swap(l, i);			if (edges[indices[r]] < edges[indices[l]]) swap(l, r);			if (edges[indices[r]] < edges[indices]) swap(i, r);			j = r-1;			swap(i, j);			i = l;			v = edges[indices[j]];			for (;;) {				while (edges[indices[++i]] < v);				while (v < edges[indices[--j]]);				if (j < i) break;				swap(i, j);			}			swap(i, r-1);			quick_sort(edges, indices, l, j);			quick_sort(edges, indices, i+1, r);		}	}	void	ShadowVolume::	insertion_sort(const edge_t* edges,		       unsigned int* indices,		       const unsigned int l,		       const unsigned int r) {		unsigned int i, j;		unsigned int v;		for (i = l+1; i <= r; ++i) {			v = indices;			j = i;			while (j > l && edges[v] < edges[indices[j-1]]) {				indices[j] = indices[j-1];				--j;			}			indices[j] = v;		}	}	void	ShadowVolume::	sort_edges(const edge_t* edges,		   unsigned int* indices,		   const unsigned int l,		   const unsigned int r) {		quick_sort(edges, indices, l, r);		insertion_sort(edges, indices, l, r);	}	#undef swap	ShadowVolume::	ShadowVolume(const Triangle* tris,		     const unsigned int nb_tris) : m_nb_triangles(nb_tris),       						   m_triangles(new triangle_t[nb_tris])	{		unsigned int i, j;				{	// Copy triangle array...			for (i = 0; i < nb_tris; ++i) {				m_triangles.vertex[0] = tris.a;				m_triangles.vertex[1] = tris.b;				m_triangles.vertex[2] = tris.c;			}		}		{	// Create edge array...			edge_t* edges = new edge_t[nb_tris*3];			for (i = 0; i < nb_tris; ++i) {				triangle_t& t = m_triangles;				if (create_edge(edges[i*3], t.vertex[0], t.vertex[1])) {					t.edge[0] = i*3;				} else {					t.edge[0] = -i*3-1;				}				if (create_edge(edges[i*3+1], t.vertex[1], t.vertex[2])) {					t.edge[1] = i*3+1;				} else {					t.edge[1] = -i*3-2;				}				if (create_edge(edges[i*3+2], t.vertex[2], t.vertex[0])) {					t.edge[2] = i*3+2;				} else {					t.edge[2] = -i*3-3;				}			}			// Sort edge array			unsigned int* indices = new unsigned int[nb_tris*3];						for (i = 0; i < nb_tris*3; ++i) {				indices = i;			}			sort_edges(edges, indices, 0, nb_tris*3-1);						// Count unique edges						m_nb_edges = 1;			for (i = 1; i < nb_tris*3; ++i) {				if (edges[indices[i-1]] < edges[indices]) {					m_nb_edges++;				}			}			m_edges = new edge_t[m_nb_edges];			unsigned int* new_indices = new unsigned int[nb_tris*3];			j = 0;			m_edges[0] = edges[indices[0]];			new_indices[indices[0]] = 0;			for (i = 1; i < nb_tris*3; ++i) {				if (edges[indices[i-1]] < edges[indices]) {					m_edges[++j] = edges[indices];				}				new_indices[indices] = j;			}			delete [] edges;			delete [] indices;			for (i = 0; i < nb_tris; ++i) {				triangle_t& t = m_triangles;				for (j = 0; j < 3; ++j) {					if (t.edge[j] >= 0) {						t.edge[j] = new_indices[t.edge[j]];					} else {						t.edge[j] = -1-new_indices[-1-t.edge[j]];					}				}			}			delete [] new_indices;		}	}	ShadowVolume::	~ShadowVolume() {		delete [] m_edges;		delete [] m_triangles;	}		namespace {		unsigned char status = 0;		ShadowVolume::method_t current_method;	}	void	ShadowVolume::	draw(Vector3* v) const {		const float shadow_length = 400;		Vector3 light_pos;		unsigned int i;		int j;		assert(status == 1);		{			float tmp[4];			Matrix4x4 m(GL_MODELVIEW_MATRIX);			glGetLightfv(GL_LIGHT0, GL_POSITION, tmp);			light_pos.x = tmp[0]*tmp[3];			light_pos.y = tmp[1]*tmp[3];			light_pos.z = tmp[2]*tmp[3];			light_pos = (m.inverse())*light_pos;		}		if (current_method == z_fail) {			glBegin(GL_TRIANGLES);			for (i = 0; i < m_nb_triangles; ++i) {				triangle_t& tri = m_triangles;				Vector3 n = (v[tri.vertex[1]]-v[tri.vertex[0]])^(v[tri.vertex[2]]-v[tri.vertex[0]]);				if (n*(light_pos-v[tri.vertex[0]]) < 0) {					Vector3 p1 = v[tri.vertex[0]]+shadow_length*((v[tri.vertex[0]]-light_pos).norm());					Vector3 p2 = v[tri.vertex[1]]+shadow_length*((v[tri.vertex[1]]-light_pos).norm());					Vector3 p3 = v[tri.vertex[2]]+shadow_length*((v[tri.vertex[2]]-light_pos).norm());					glVertex3f(v[tri.vertex[0]].x, v[tri.vertex[0]].y, v[tri.vertex[0]].z);					glVertex3f(v[tri.vertex[1]].x, v[tri.vertex[1]].y, v[tri.vertex[1]].z);					glVertex3f(v[tri.vertex[2]].x, v[tri.vertex[2]].y, v[tri.vertex[2]].z);					glVertex3f(p1.x, p1.y, p1.z);					glVertex3f(p3.x, p3.y, p3.z);					glVertex3f(p2.x, p2.y, p2.z);					for (j = 0; j < 3; ++j) {						if (tri.edge[j] >= 0) {							m_edges[tri.edge[j]].value--;						} else {							m_edges[-1-tri.edge[j]].value++;						}					}				}			}			glEnd();		} else {			for (i = 0; i < m_nb_triangles; ++i) {				triangle_t& tri = m_triangles;				Vector3 n = (v[tri.vertex[1]]-v[tri.vertex[0]])^(v[tri.vertex[2]]-v[tri.vertex[0]]);				if (n*(light_pos-v[tri.vertex[0]]) < 0) {					for (j = 0; j < 3; ++j) {						if (tri.edge[j] >= 0) {							m_edges[tri.edge[j]].value--;						} else {							m_edges[-1-tri.edge[j]].value++;						}					}				}			}		}		glBegin(GL_QUADS);		for (i = 0; i < m_nb_edges; ++i) {			edge_t& e = m_edges;			if (e.value != 0) {				Vector3 p1 = v[e.vertex[0]]+shadow_length*((v[e.vertex[0]]-light_pos).norm());				Vector3 p2 = v[e.vertex[1]]+shadow_length*((v[e.vertex[1]]-light_pos).norm());				if (e.value > 0) {					for (; e.value > 0; e.value--) {						glVertex3f(v[e.vertex[0]].x, v[e.vertex[0]].y, v[e.vertex[0]].z);						glVertex3f(v[e.vertex[1]].x, v[e.vertex[1]].y, v[e.vertex[1]].z);						glVertex3f(p2.x, p2.y, p2.z);						glVertex3f(p1.x, p1.y, p1.z);					}				} else {					for (; e.value < 0; e.value++) {						glVertex3f(v[e.vertex[0]].x, v[e.vertex[0]].y, v[e.vertex[0]].z);						glVertex3f(p1.x, p1.y, p1.z);						glVertex3f(p2.x, p2.y, p2.z);						glVertex3f(v[e.vertex[1]].x, v[e.vertex[1]].y, v[e.vertex[1]].z);					}				}				if (e.value != 0) printf("ca merde\n");			}		}		glEnd();	}	namespace {		bool dlist_defined = false;		GLuint dlist;	}	void	ShadowVolume::	begin(const method_t method) {		ASSERT(status == 0);		current_method = method;		Texture::none().bind();		GfxProgram::none.bind();		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);		glDepthFunc(GL_LESS);		glDepthMask(GL_FALSE);		glEnable(GL_STENCIL_TEST);		glClear(GL_STENCIL_BUFFER_BIT);		if (   OpenGL::has_stencil_two_side		    && OpenGL::has_stencil_wrap) {			glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);			glDisable(GL_CULL_FACE);			glActiveStencilFaceEXT(GL_FRONT);			glStencilOp(GL_KEEP, GL_INCR_WRAP_EXT, GL_KEEP);			glStencilMask(~0);			glStencilFunc(GL_ALWAYS, 0, ~0);			glActiveStencilFaceEXT(GL_BACK);			glStencilOp(GL_KEEP, GL_DECR_WRAP_EXT, GL_KEEP);			glStencilMask(~0);			glStencilFunc(GL_ALWAYS, 0, ~0);		} else {			if (!dlist_defined) {				dlist = glGenLists(1);				dlist_defined = true;			}			// Setup stencil for front faces.			glStencilMask(~0);			glStencilFunc(GL_ALWAYS, 0, ~0);			if (current_method == z_pass) {				glCullFace(GL_FRONT);				glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);			} else {				glCullFace(GL_BACK);				glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);			}			// Start display list.			glNewList(dlist, GL_COMPILE_AND_EXECUTE);		}		status = 1;	}	void	ShadowVolume::	end_shadow() {		ASSERT(status == 1);		if (   OpenGL::has_stencil_two_side		    && OpenGL::has_stencil_wrap) {			glEnable(GL_CULL_FACE);		} else {			// End display list.			glEndList();			// Setup stencil for back faces.			if (current_method == z_pass) {				glCullFace(GL_BACK);				glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);			} else {				glCullFace(GL_FRONT);				glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);			}			// Draw display list			glCallList(dlist);			glCullFace(GL_BACK);		}		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);		glColor4f(1, 1, 1, 1);		glStencilOp(GL_KEEP,GL_KEEP, GL_KEEP);		glStencilMask(~0);		if (current_method == z_pass) {			glStencilFunc(GL_EQUAL, 0, ~0);		} else {			glStencilFunc(GL_EQUAL, 0, ~0);		}		glEnable(GL_BLEND);		glBlendFunc(GL_ONE, GL_ONE);		status = 2;	}	void	ShadowVolume::	end() {		ASSERT(status == 1);		glDisable(GL_BLEND);		glDisable(GL_STENCIL_TEST);		status = 0;	}
SaM3d!, a cross-platform API for 3d based on SDL and OpenGL.The trouble is that things never get better, they just stay the same, only more so. -- (Terry Pratchett, Eric)
Advertisement
thank you :) i'll study it and post my results as i can :D
i've got many problems with your code... even i cannot make it compile :/

i know i'm probably asking you too much, but couldn't you post a small demo of sample usage, with source?
It's likely this code depends on other parts of the stuff I wrote. And it's also possible it won't compile under MSVC (I usually code under Linux and use gcc under Windows). Maybe I should take some time to port it...

Hmm I'll try to prepare something for you (but it will take time).
SaM3d!, a cross-platform API for 3d based on SDL and OpenGL.The trouble is that things never get better, they just stay the same, only more so. -- (Terry Pratchett, Eric)
don't worry 'bout the time, i'll wait :)
Advertisement
Quote:
You will probably want to read the following article: http://legion.gibbering.net/projectx/paper/paper.pdf
It describes an excellent algorithm that will work with *all* models.


BTW, the paper was written by RipTorn (forgot to mention it ; thanx RipTorn !).


cheers rodzilla [smile] awesome to know that the algorithm worked a charm - makes me feel all warm and fuzzy inside [wink]

This topic is closed to new replies.

Advertisement