Sign in to follow this  
b3rs3rk

Shadows volumes: what's wrong ??!

Recommended Posts

i'm playng a bit with shadows volumes. i've wrote a small class for an object, with faces and vertexes and i've created the relative shadow volume. in my render function i clear all the biffers, then render my scene (it's a simple high tessellated floor with a quad on it that should cast the shadow, nothing special) with a wireframe version of the casted volume this is perfect) , then i set the opengl states for the stencil, render the shadow and flush. Are these the correct passes or should i do some other things? the real problem is that except the floor , the quad and the wireframe volume, nothing more is rendered. The shadows doesn't appear. It's like if the stencil buffer is not filled. This is my render function:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glLoadIdentity();	
gluLookAt(0,3,10, 0,0,0, 0,1,0);
Point3 lpos(LightPosition[0],LightPosition[1],LightPosition[2]);
glLightfv( GL_LIGHT1, GL_POSITION, LightPosition );

RenderScene();	// the floor and the shadow caster quad
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
Quad.DrawShadowVolume(lpos);	
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );


glPushAttrib( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_POLYGON_BIT | GL_STENCIL_BUFFER_BIT );
glDisable( GL_LIGHTING );	// Turn Off Lighting
glDepthMask( GL_FALSE );	// Turn Off Writing To The Depth-Buffer
glDepthFunc( GL_LEQUAL );
glEnable( GL_STENCIL_TEST );	// Turn On Stencil Buffer Testing
glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );	// Don't Draw Into The Colour Buffer
glStencilFunc( GL_ALWAYS, 1, 0xFFFFFFFFL );

glFrontFace(GL_CCW);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
Quad.DrawShadowVolume(lpos);

glFrontFace(GL_CW);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
Quad.DrawShadowVolume(lpos);

glFrontFace( GL_CCW );
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );	

//draw a shadowing rectangle covering the entire screen
glColor4f( 0.0f, 0.0f, 0.0f, 0.4f );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glStencilFunc( GL_NOTEQUAL, 0, 0xFFFFFFFFL );
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
glPushMatrix();
glLoadIdentity();
glBegin( GL_TRIANGLE_STRIP );
	glVertex3f(-0.1f, 0.1f,-0.10f);
	glVertex3f(-0.1f,-0.1f,-0.10f);
	glVertex3f( 0.1f, 0.1f,-0.10f);
	glVertex3f( 0.1f,-0.1f,-0.10f);
glEnd();
glPopMatrix();
glPopAttrib();


glColor4f(0.7f, 0.4f, 0.0f, 1.0f);	// Set Color To An Orange
glDisable(GL_LIGHTING);		        // Disable Lighting
glDepthMask(GL_FALSE);			// Disable Depth Mask
glColor4f(0.75f, 0.0f, 0.0f, 1.0f);
glPushMatrix();
glTranslatef(LightPosition[0],LightPosition[1],LightPosition[2]);
gluSphere(sphere, 0.1f, 30, 30);        //draw a sphere with the light position
glPopMatrix();
glEnable(GL_LIGHTING);			// Enable Lighting
glDepthMask(GL_TRUE);		

glFlush();	



this is the code from the tutorial #27 on NeHe. Any advices?

Share this post


Link to post
Share on other sites
Is backface culling enabled ?
Do you set the stencil mask somewhere ?

BTW, instead of drawing a shadow quad, you should first render the scene with only ambient light draw shadow volumes, and then redraw the scene with diffuse and specular light (and this for all point lights). This will look much better.

Share this post


Link to post
Share on other sites
this is my InitGl function:


bool InitGL(void)
{
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glClearStencil(0); // Stencil Buffer Setup

glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
// glCullFace(GL_BACK); // Set Culling Face To Back Face
// glEnable(GL_CULL_FACE); // Enable Culling

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations

float LightAmb[] = { 0.2f, 0.2f, 0.2f, 1.0f};
float LightDif[] = { 0.6f, 0.6f, 0.6f, 1.0f};
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmb); // Set Light1 Ambience
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDif); // Set Light1 Diffuse
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT1);

glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

glClearColor(0.1f, 1.0f, 0.5f, 1.0f); // Set Clear Color (Greenish Color)

return true;
}




if i enable backface culling nothing is rendered o_O

Share this post


Link to post
Share on other sites
i don't know if your using z-fail or not(you should). so here is the code for that

glFrontFace(GL_CCW);
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
Quad.DrawShadowVolume(lpos);

glFrontFace(GL_CW);
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
Quad.DrawShadowVolume(lpos);


other than that, have you tried fiddeling with this line
glStencilFunc( GL_NOTEQUAL, 0, 0xFFFFFFFFL );

try
glStencilFunc( GL_EQUAL, 0, 0xFFFFFFFFL );
IF the entire screen isn't shadowed with this, then it's a problem with the rendering of the black shadow polygon

allso i see that the shadow polygon is renderd at z=-0.10f
what is your z near?
try to put glDisable(GL_DEPTH_TEST);
right before glBegin( GL_TRIANGLE_STRIP );

Share this post


Link to post
Share on other sites
but i've noticed that if i comment these lines:
/*glFrontFace(GL_CCW);
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
Quad.DrawShadow(LightPos);*/
and do only this rendering pass:
glFrontFace(GL_CW);
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
Quad.DrawShadow(LightPos);
nothing changes. but if i do the inverse, the shadow isn't rendered..

Share this post


Link to post
Share on other sites
Quote:
Original post by b3rs3rk
it's happening something really wierd: instead of drawing the shadow rectangle, appears its CONTOUR o_O


Could be because you're drawing the shadow volume in line mode with depth write enabled.
Hmmm did you try the z-fail way (the z-pass way can give bad results when the camera is inside the shadow volume) ?

Share this post


Link to post
Share on other sites
Hmmm could you try you shadows step by step ?
- actually draw the shadow volume without the stencil stuff (and with blending) and see if its orientation is OK (draw front and back sides with different colors if necessary) ;
- test the incrementing pass alone for the shadow volume ;
- test the decrementing pass alone (initialize the stencil to 1).

This should help you figure what's wrong.

Share this post


Link to post
Share on other sites
the incrementing pass draws the contour of the shadow.

the decrementing pass instead, draws nothing:

glStencilFunc( GL_ALWAYS, 1, 0xFFFFFFFFL );
glFrontFace(GL_CCW);
glStencilOp(GL_KEEP, GL_DECR , GL_KEEP);
Quad.DrawShadow(LightPos);

i'm slightly becoming mad...

Share this post


Link to post
Share on other sites
Hmmm could you post screenshots of:
- shadow volumes drawn (no stencil) frontface in a color, backface in another color (with blending enabled so we can see everything).
- GL_INCR pass.
- GL_DECR pass (did you intialize the stencil buffer with 1 ? The result you obtain is weird).

Share this post


Link to post
Share on other sites
i've done something better: i've downloaded the shadows volumes code from codesampler and inserted my object class.

i've uploaded code and bin here:
http://www.neoborn.com/users/b3rs3rk/shadow%20demo.rar

F1 shows the volume.

something strange happens: the shadow is rendered only from a point of view, if you rotate the camera it disappear :/

Share this post


Link to post
Share on other sites
maybe the shadow volumes you render are incorrect..
I can imagine if you render each shadow volume triangle both front and back faced you could end up with lines because of slight differences between the triangles?
just a guess.
i'd run your demo if my computer at work would be capable at actually running your demo..

Share this post


Link to post
Share on other sites
Quote:
Original post by rodzilla
I looked at your demo. The shadow volume for the quad is wrong.
Do you want me to try and fix it ?

BTW the shadow volume for the teapot is wrong as well :D


yes!thanks!

Share this post


Link to post
Share on other sites
OK. To get a quick fix for your example, you'll just have to reverse the orientation of the first triangle in your model (swap 2 vertices).

Your demo will work (kind of) but basically your shadow volume generation algorithm is broken (will only work for *some* models).
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.

Another point is the floor. Look below the floor and you'll see the shadow as well. To avoid this, the floor should cast a shadow as well.

Hope it helps (sorry, I wish I'd have time to help you more).

Share this post


Link to post
Share on other sites
I'll post my implementation of the paper's algorithm if you wish (as soon as I reboot to Linux).

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

Hmmm and thanx ! I had only coded z-fail method until you mentionned your problems. I have z-pass as well now. :D

Share this post


Link to post
Share on other sites
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[i]; indices[i] = 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[i]] < edges[indices[l]]) swap(l, i);
if (edges[indices[r]] < edges[indices[l]]) swap(l, r);
if (edges[indices[r]] < edges[indices[i]]) 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[i];
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[i].vertex[0] = tris[i].a;
m_triangles[i].vertex[1] = tris[i].b;
m_triangles[i].vertex[2] = tris[i].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[i];

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] = 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[i]]) {
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[i]]) {
m_edges[++j] = edges[indices[i]];
}
new_indices[indices[i]] = j;
}

delete [] edges;
delete [] indices;

for (i = 0; i < nb_tris; ++i) {
triangle_t& t = m_triangles[i];

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[i];
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[i];
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[i];

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;
}


Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this