Shadows volumes: what's wrong ??!

Started by
25 comments, last by RipTorn 19 years, 5 months ago
it would be great if u post the implementation of that algorithm... i'm still trying to make my one work :P
Advertisement
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)
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 :)
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