FoxMcCloud
Newcomer
I'm trying to implement stencil shadowing and I'm running into a problem with shadow volume generation - I get the edge list and determine the shadow edges correctly, at least according to my visualizations (highlighting shadow edges / drawing shadow volumes to color buffer), however I can't seem to get the winding order right, messing up front / back facing - here's what it looks like right now:
http://www.flickr.com/photos/59098813@N06/5628070669/lightbox/
Here's my edge generation / shadow edge detection code:
And here's my shadow volume rendering code:
Any idea where I'm going wrong? I'm basing my work on this: http://www.gamasutra.com/view/feature/2942/the_mechanics_of_robust_stencil_.php?print=1 . Any help would be appreciated.
http://www.flickr.com/photos/59098813@N06/5628070669/lightbox/
Here's my edge generation / shadow edge detection code:
Code:
struct edge
{
unsigned int vertex_index[2];
unsigned int triangle_index[2];
};
#pragma pack(push)
#pragma pack(1)
struct triangle
{
ext::uint16 index[3];
};
#pragma pack(pop)
unsigned int BuildEdges(unsigned int VertexCount, unsigned int TriangleCount, const triangle* TriangleArray, edge* EdgeArray)
{
assert(sizeof(triangle) == (sizeof(ext::uint16) * 3));
long MaxEdgeCount = TriangleCount * 3;
unsigned short* FirstEdge = new unsigned short[VertexCount + MaxEdgeCount];
unsigned short* NextEdge = FirstEdge + VertexCount;
for(unsigned int a = 0; a < VertexCount; a++)
{
FirstEdge[a] = 0xFFFF;
}
// First pass over all triangles. This finds all the edges satisfying the
// condition that the first vertex index is less than the second vertex index
// when the direction from the first vertex to the second vertex represents
// a counterclockwise winding around the triangle to which the edge belongs.
// For each edge found, the edge index is stored in a linked list of edges
// belonging to the lower-numbered vertex index i. This allows us to quickly
// find an edge in the second pass whose higher-numbered vertex index is i.
unsigned int EdgeCount = 0;
const triangle* Triangle = TriangleArray;
for(unsigned int a = 0; a < TriangleCount; a++)
{
unsigned int i1 = Triangle->index[2];
for(unsigned int b = 0; b < 3; b++)
{
unsigned int i2 = Triangle->index[b];
if (i1 < i2)
{
edge* Edge = &EdgeArray[EdgeCount];
Edge->vertex_index[0] = (ext::uint16) i1;
Edge->vertex_index[1] = (ext::uint16) i2;
Edge->triangle_index[0] = (ext::uint16) a;
Edge->triangle_index[1] = (ext::uint16) a;
unsigned int EdgeIndex = FirstEdge[i1];
if (EdgeIndex == 0xFFFF)
{
FirstEdge[i1] = EdgeCount;
}
else
{
for (;;)
{
unsigned int Index = NextEdge[EdgeIndex];
if (Index == 0xFFFF)
{
NextEdge[EdgeIndex] = EdgeCount;
break;
}
EdgeIndex = Index;
}
}
NextEdge[EdgeCount] = 0xFFFF;
EdgeCount++;
}
i1 = i2;
}
Triangle++;
}
// Second pass over all triangles. This finds all the edges satisfying the
// condition that the first vertex index is greater than the second vertex index
// when the direction from the first vertex to the second vertex represents
// a counterclockwise winding around the triangle to which the edge belongs.
// For each of these edges, the same edge should have already been found in
// the first pass for a different triangle. So we search the list of edges
// for the higher-numbered vertex index for the matching edge and fill in the
// second triangle index. The maximum number of comparisons in this search for
// any vertex is the number of edges having that vertex as an endpoint.
Triangle = TriangleArray;
for(unsigned int a = 0; a < TriangleCount; a++)
{
unsigned int i1 = Triangle->index[2];
for(unsigned int b = 0; b < 3; b++)
{
unsigned int i2 = Triangle->index[b];
if(i1 > i2)
{
for(unsigned int EdgeIndex = FirstEdge[i2]; EdgeIndex != 0xFFFF; EdgeIndex = NextEdge[EdgeIndex])
{
edge* Edge = &EdgeArray[EdgeIndex];
if ((Edge->vertex_index[1] == i1) && (Edge->triangle_index[0] == Edge->triangle_index[1]))
{
Edge->triangle_index[1] = (unsigned short) a;
break;
}
}
}
i1 = i2;
}
Triangle++;
}
delete[] FirstEdge;
return EdgeCount;
}
vector<bool> CalculateShadowEdges(const float3 LightPosition, const vector<float3>& Vertices, const std::vector<float3>& Normals, unsigned int TriangleCount, const triangle* Triangles, vector<edge>& Edges)
{
vector<bool> Result;
Result.resize(Edges.size(), false);
vector<float3> TriCenters(TriangleCount);
vector<float3> TriNormals(TriangleCount);
vector<float3> LightVectors(TriangleCount);
for(unsigned int i = 0; i < TriangleCount; ++i)
{
float3 A = Vertices[Triangles[i].index[0]];
float3 B = Vertices[Triangles[i].index[1]];
float3 C = Vertices[Triangles[i].index[2]];
float3 Center = (A + B + C) * (1.0/3.0);
TriCenters[i] = Center;
float3 NormalA = Normals[Triangles[i].index[0]];
float3 NormalB = Normals[Triangles[i].index[1]];
float3 NormalC = Normals[Triangles[i].index[2]];
float3 Normal = normalize(NormalA + NormalB + NormalC);
TriNormals[i] = Normal;
float3 LightVector = Center - LightPosition;
LightVectors[i] = normalize(LightVector);
}
for(unsigned int i = 0; i < Edges.size(); ++i)
{
float CosA = dot(TriNormals[Edges[i].triangle_index[0]], LightVectors[Edges[i].triangle_index[0]]);
float CosB = dot(TriNormals[Edges[i].triangle_index[1]], LightVectors[Edges[i].triangle_index[1]]);
if(((CosA > 0) && (CosB < 0)) || ((CosA < 0) && (CosB > 0)))
{
Result[i] = true;
}
}
return Result;
}
And here's my shadow volume rendering code:
Code:
void mesh::DrawStencilVolumes(const float3 LightPosition)
{
vector<edge> Edges(Indices.size());
unsigned int EdgeCount = BuildEdges(Vertices.size(), Indices.size() / 3, (const triangle*)&Indices[0], &Edges[0]);
Edges.resize(EdgeCount);
//Triangles is just a different view of Indices.
const triangle* Triangles = (const triangle*)&Indices[0];
//ShadowEdges[i] is true if Edges[i] is a shadow edge
vector<bool> ShadowEdges = CalculateShadowEdges(LightPosition, Vertices, Normals, Indices.size() / 3, (const triangle*)&Indices[0], Edges);
//
unsigned int TriangleCount = Indices.size() / 3;
vector<float3> TriCenters(TriangleCount);
vector<float3> TriNormals(TriangleCount);
//Vector from the light to TriCenters[i]
vector<float3> LightVectors(TriangleCount);
for(unsigned int i = 0; i < TriangleCount; ++i)
{
float3 A = Vertices[Triangles[i].index[0]];
float3 B = Vertices[Triangles[i].index[1]];
float3 C = Vertices[Triangles[i].index[2]];
float3 Center = (A + B + C) * (1.0/3.0);
TriCenters[i] = Center;
float3 NormalA = Normals[Triangles[i].index[0]];
float3 NormalB = Normals[Triangles[i].index[1]];
float3 NormalC = Normals[Triangles[i].index[2]];
float3 Normal = normalize(NormalA + NormalB + NormalC);
TriNormals[i] = Normal;
float3 LightVector = Center - LightPosition;
LightVectors[i] = normalize(LightVector);
}
//
using namespace application;
using namespace programs;
cgGLBindProgram(SimpleVertexProgram);
cgGLBindProgram(StencilShadowVolumesDebugFragmentProgram);
float Color[] = { 1, 0, 1, 1 };
cgSetParameter4fv(cgGetNamedParameter(SimpleFragmentProgram, "Color"), Color);
update_matrices();
glPointSize(4.0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_CULL_FACE);
glBegin(GL_QUADS);
for(unsigned int i = 0; i < Edges.size(); ++i)
{
if(ShadowEdges[i])
{
unsigned int FirstVertexIndex = Edges[i].vertex_index[0];
unsigned int SecondVertexIndex = Edges[i].vertex_index[1];
float3 StartPoint = Vertices[Edges[i].vertex_index[0]];
float3 EndPoint = Vertices[Edges[i].vertex_index[1]];
triangle FirstTriangle = Triangles[Edges[i].triangle_index[0]];
triangle SecondTriangle = Triangles[Edges[i].triangle_index[1]];
float3 FirstNormal = TriNormals[Edges[i].triangle_index[0]];
float3 SecondNormal = TriNormals[Edges[i].triangle_index[1]];
float3 FirstLightVector = LightVectors[Edges[i].triangle_index[0]];
float3 SecondLightVector = LightVectors[Edges[i].triangle_index[1]];
unsigned int FirstIndex = Edges[i].vertex_index[0];
unsigned int SecondIndex = Edges[i].vertex_index[1];
//Begin experimental block
//This block tests to see if the second triangle comes before the first
//(based on winding order) and if so swaps the edge points.
bool Swap = false;
for(unsigned int i = 0; i < 3; ++i)
{
if(FirstTriangle.index[i] == FirstIndex)
{
if(FirstTriangle.index[(i+1)%3] == SecondIndex)
{
Swap = true;
}
}
}
//
if(Swap)
{
swap(StartPoint, EndPoint);
}
//End experimental block
float CosA = dot(FirstNormal, FirstLightVector);
float CosB = dot(SecondNormal, SecondLightVector);
//If the first triangle faces towards the light and the second away from the light
//Then swap the start / end points to swap winding for the extruded polygon.
if(((CosA > 0) && (CosB < 0)))
{
swap(StartPoint, EndPoint);
}
//This is a fudge factor, extrusion should be to infinity.
const float Scale = 1000.0f;
float3 StartExtruded = StartPoint + (normalize(StartPoint - LightPosition) * Scale);
float3 EndExtruded = EndPoint + (normalize(EndPoint - LightPosition) * Scale);
const float W = 1.0f;
glVertexAttrib4f(0, StartPoint.x, StartPoint.y, StartPoint.z, 1);
glVertexAttrib4f(0, EndPoint.x, EndPoint.y, EndPoint.z, 1);
glVertexAttrib4f(0, EndExtruded.x, EndExtruded.y, EndExtruded.z, W);
glVertexAttrib4f(0, StartExtruded.x, StartExtruded.y, StartExtruded.z, W);
}
}
glEnd();
///
glDisable(GL_CULL_FACE);
}
Any idea where I'm going wrong? I'm basing my work on this: http://www.gamasutra.com/view/feature/2942/the_mechanics_of_robust_stencil_.php?print=1 . Any help would be appreciated.