   Stencil shadows with GL_TRIANGLE_STRIP : Strange artifacts
  Hello everybody,I've followed lesson 27 of nehe's tutorials on creating shadows and customized the code to my needs. Seems that the shadow calculation works fine. But now I am having some strange artifacts on the shadow, It seems that there are some very small gaps between the triangles of the shadow, since it's drawn as a GL_TRIANGLE_STRIP.Here are some screenshots:Does anyone have an idea, how to get rid of this lines? Is this an OpenGL specific issue, or should I inspect my code more precisely? The code is completely taken from Nehe's tutorial, I just changed some variable names to fit it into my project, so I think it should be ok.Any help is really appreciated.Thanks in advanceMatthias[Edited by - Pudutzki on September 10, 2007 10:56:13 AM]
  They look like tiny cracks, possibly because of subpixel precision artifacts.Is the strip indexed or not?   ;  What do you mean with "indexed"?   ;  What he means is: Do you send shared vertices (vertices that are used by multiple triangles that are not adjacent in the strip) multiple times or do you use indices into an array of vertices (where each vertex is contained once)?.Example:You have several triangles that are not directly connected by the strip.They share some vertices, say T1 (0, 1, 2), T2 (1, 4, 8), T3 (4, 8, 9).T1, T2, T3 are distributed over the strip and not directly connected!Note: the numbers in the vertex array represent the vertex number, in reality those have to be replaced by the real data. Numbers in the index array are the real data, i.e. indices into the vertex array.Non-indexed:Vertex array: 0, 1, 2, ... 1, 4, 8, ... 4, 8, 9IndexedVertex array: 0, 1, 2, ... 4, ... 8, 9, ...Index array : 0, 1, 2, ... 1, 4, 8, ... 4, 8, 9What could happen in the non-indexed case is that due to small imprecision errors in the vertex data calculation (e.g. when vertices are transformed) you could get slightly different positions for vertices that should be the same.Hope that helps.   ;Quote:Original post by Lord_EvilWhat could happen in the non-indexed case is that due to small imprecision errors in the vertex data calculation (e.g. when vertices are transformed) you could get slightly different positions for vertices that should be the same.Sort of.Take for example the following values:512.0f512.0f + 256.0f = 768.0f512.0f + 512.0f = 1024.0f1024.0f - 256.0f = 768.0fThis is just what you expect. There's a reason if everything is ^2.The trouble is when you add more esoteric numbers, for example:delta = 123.456fThis probably won't hold true:14.0f + delta - delta == 14.0f(if it works with 14.0f, try using 0.14f or 0.000014)This happens because floating point math is approximated.In general, when you "go forward" and then you have a path "going back" you won't match the same thing in 99% of the time. Xforming makes the thing simply worse.Indexing by itself does not solve the problem - if you build the index list by looking at the float values you'll just screw'em as well.Indexing however usually helps in understanding better the problem domain and eliminates the need for "non-shared" shared vertices.This doesn't mean Func(5.0f) returns slightly different numbers on the various calls butparam=5.0 (in the Real Set)fparam=5.0f (in your favorite Float Set)Generally,Func(fparam) != IdealFunc(param)But given an fparam the error will always be the same, in other words,floatError = abs( Func(fparam) - IdealFunc(param) )is constant as well asdeviation = abs( Func(fparam) / IdealFunc(param) ).So, what about you?Let's say you're doing:A -> B|   /|  /| /v/CA -> B|   /||  / || /  |v/   vCc1  DIf you go down from B (spawn vertex D) and then go back (spawn c1), the edge BC1 may match... or not, depending on how lucky you are.Indexing just fixes this by allowing to recognize c1 shouldn't be computed in the first place.[Edited by - Krohm on September 10, 2007 9:31:41 AM];  Ok, first of all, thanks for your effort, but I just don't get it.I am using a list of vertices, and a list of faces. Each face has an array of indices which points to the according vertices (face_vertex), so I think I am using indexing.typedef struct t_vertex {  float x;  float y;   float z;   vertex_n normal;} vertex;/* * A face consists 3 vertices  */typedef struct t_plane{GLfloat a, b, c, d;} Plane;typedef struct t_face {    int face_vertex[3];    vertex normal;    bool isVisible;    Plane planeEquation;    int neighbourIndices[3];    } face;The mesh data is loaded from a file which contains all vertices and all faces, it looks like this:v -0.650000 -0.179358 1.777216v -0.650000 1.127216 -0.470642v -0.650000 -1.120642 -1.777216v -0.650000 -2.427216 0.470642v 1.950000 -0.179358 1.777216v 1.950000 -2.427216 0.470642v 1.950000 -1.120642 -1.777216v 1.950000 1.127216 -0.470643f 5 1 4f 5 4 6f 3 7 6f 3 6 4f 2 8 3f 8 7 3f 1 5 2f 5 8 2f 5 6 7f 5 7 8f 1 2 3f 1 3 4All data is stored the following way:vector <vertex>   obj_vertices;vector <face>obj_faces;According to Nehe's tutorial, I do the following:1) Calculate face to face connectivity, as far as I unserstand, this function determines which face has which neighbours.void MeshObject::setConnectivity(){cout << "Setting face-to-face connectivity" << endl;int p1i, p2i, p1j, p2j;int P1i, P2i, P1j, P2j;int i,j,ki,kj;for(i=0;i<obj_faces.size()-1;i++)for(j=i+1;j<obj_faces.size();j++)for(ki=0;ki<3;ki++)if(!obj_faces.neighbourIndices[ki]){for(kj=0;kj<3;kj++){p1i=ki;p1j=kj;p2i=(ki+1)%3;p2j=(kj+1)%3;p1i=obj_faces.face_vertex[p1i];p2i=obj_faces.face_vertex[p2i];p1j=obj_faces[j].face_vertex[p1j];p2j=obj_faces[j].face_vertex[p2j];P1i=((p1i+p2i)-abs(p1i-p2i))/2;P2i=((p1i+p2i)+abs(p1i-p2i))/2;P1j=((p1j+p2j)-abs(p1j-p2j))/2;P2j=((p1j+p2j)+abs(p1j-p2j))/2;if((P1i==P1j) && (P2i==P2j)){  //they are neighboursobj_faces.neighbourIndices[ki] = j+1;  obj_faces[j].neighbourIndices[kj] = i+1;  }}}}2) Calculate the planes for each facevoid MeshObject::calculatePlanes() {vertex v[4];for(int n=0;n<obj_faces.size();n++) {for (int i=0;i<3;i++){v[i+1].x = obj_vertices[obj_faces[n].face_vertex].x;v[i+1].y = obj_vertices[obj_faces[n].face_vertex].y;v[i+1].z = obj_vertices[obj_faces[n].face_vertex].z;}obj_faces[n].planeEquation.a = v[1].y*(v[2].z-v[3].z) + v[2].y*(v[3].z-v[1].z) + v[3].y*(v[1].z-v[2].z);obj_faces[n].planeEquation.b = v[1].z*(v[2].x-v[3].x) + v[2].z*(v[3].x-v[1].x) + v[3].z*(v[1].x-v[2].x);obj_faces[n].planeEquation.c = v[1].x*(v[2].y-v[3].y) + v[2].x*(v[3].y-v[1].y) + v[3].x*(v[1].y-v[2].y);obj_faces[n].planeEquation.d =-( v[1].x*(v[2].y*v[3].z - v[3].y*v[2].z) +  v[2].x*(v[3].y*v[1].z - v[1].y*v[3].z) +  v[3].x*(v[1].y*v[2].z - v[2].y*v[1].z) );}}Later, inside my renderloop, the shadows are being casted with:void MeshObject::castShadow(GLfloat lp[4]){unsigned inti, j, k, jj;unsigned intp1, p2;vertexv1, v2;floatside;//set visual parameterfor (i=0;i<obj_faces.size();i++){// chech to see if light is in front or behind the plane (face plane)side =obj_faces.planeEquation.a*lp[0]+obj_faces.planeEquation.b*lp[1]+obj_faces.planeEquation.c*lp[2]+obj_faces.planeEquation.d*lp[3];if (side > 0) obj_faces.isVisible = true;else  obj_faces.isVisible = false;} glDisable(GL_LIGHTING);glDepthMask(GL_FALSE);glDepthFunc(GL_LEQUAL);glEnable(GL_STENCIL_TEST);glColorMask(0, 0, 0, 0);glStencilFunc(GL_ALWAYS, 1, 0xffffffff);// first pass, stencil operation decreases stencil valueglFrontFace(GL_CCW);glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);for (i=0; i<obj_faces.size();i++){if (obj_faces.isVisible)for (j=0;j<3;j++){k = obj_faces.neighbourIndices[j];if ((!k) || (!obj_faces[k-1].isVisible)){// here we have an edge, we must draw a polygonp1 = obj_faces.face_vertex[j];jj = (j+1)%3;p2 = obj_faces.face_vertex[jj];//calculate the length of the vectorv1.x = (obj_vertices[p1].x - lp[0])*100;v1.y = (obj_vertices[p1].y - lp[1])*100;v1.z = (obj_vertices[p1].z - lp[2])*100;v2.x = (obj_vertices[p2].x - lp[0])*100;v2.y = (obj_vertices[p2].y - lp[1])*100;v2.z = (obj_vertices[p2].z - lp[2])*100;//draw the polygonglBegin(GL_TRIANGLE_STRIP);glVertex3f(obj_vertices[p1].x,   obj_vertices[p1].y,   obj_vertices[p1].z);glVertex3f(obj_vertices[p1].x + v1.x,   obj_vertices[p1].y + v1.y,   obj_vertices[p1].z + v1.z);glVertex3f(obj_vertices[p2].x,   obj_vertices[p2].y,   obj_vertices[p2].z);glVertex3f(obj_vertices[p2].x + v2.x,   obj_vertices[p2].y + v2.y,   obj_vertices[p2].z + v2.z);glEnd();}}}// second pass, stencil operation increases stencil valueglFrontFace(GL_CW);glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);for (i=0; i<obj_faces.size();i++){if (obj_faces.isVisible)for (j=0;j<3;j++){k = obj_faces.neighbourIndices[j];if ((!k) || (!obj_faces[k-1].isVisible)){// here we have an edge, we must draw a polygonp1 = obj_faces.face_vertex[j];jj = (j+1)%3;p2 = obj_faces.face_vertex[jj];//calculate the length of the vectorv1.x = (obj_vertices[p1].x - lp[0])*INFINITY;v1.y = (obj_vertices[p1].y - lp[1])*INFINITY;v1.z = (obj_vertices[p1].z - lp[2])*INFINITY;v2.x = (obj_vertices[p2].x - lp[0])*INFINITY;v2.y = (obj_vertices[p2].y - lp[1])*INFINITY;v2.z = (obj_vertices[p2].z - lp[2])*INFINITY;//draw the polygonglBegin(GL_TRIANGLE_STRIP);glVertex3f(obj_vertices[p1].x,   obj_vertices[p1].y,   obj_vertices[p1].z);glVertex3f(obj_vertices[p1].x + v1.x,   obj_vertices[p1].y + v1.y,   obj_vertices[p1].z + v1.z);glVertex3f(obj_vertices[p2].x,   obj_vertices[p2].y,   obj_vertices[p2].z);glVertex3f(obj_vertices[p2].x + v2.x,   obj_vertices[p2].y + v2.y,   obj_vertices[p2].z + v2.z);glEnd();}}}glFrontFace(GL_CCW);glColorMask(1, 1, 1, 1);//draw a shadowing rectangle covering the entire screenglColor4f(0.0f, 0.0f, 0.0f, 0.4f);glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);glStencilFunc(GL_NOTEQUAL, 0, 0xffffffff);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();glDisable(GL_BLEND);glDepthFunc(GL_LEQUAL);glDepthMask(GL_TRUE);glEnable(GL_LIGHTING);glDisable(GL_STENCIL_TEST);glShadeModel(GL_SMOOTH);}Obviously, the shadow volume is correctly calculated and I don't use double vertices.So what is going wrong here???   ;Quote:Original post by PudutzkiObviously, the shadow volume is correctly calculated and I don't use double vertices.Look, I'll take that for granted, but consider the problem here is not on the "hardware vertex" (opos, onorm and such) but on the vertex opos only.Take for example a cube: each face will have different indices because of normals but the vertices will match.FP may be a bit approximative, but it's not SO bad.Try a check: run a scan and collapse all the points near each other by a Eps to a single vertex.For example: if vertex B is near vertex A, then B=A.