   Shadow Volumes in VB.Net using OpenGL
  Please Help! I am currently working on a research project whereby we want to do real time shadow projection on buildings. I am using VB.Net with OpenGL.I tried to implement the paper that was published "Practical and Robust Stenciled Shadow Volumes for Hardware-Accelerated Rendering" by Cass Everitt and Mark J. Kilgard 12 March 2002. http://developer.nvidia.com/object/robust_shadow_volumes.htmlI experienced the following problem. For simplicity i only used 3 objects (boxes) with a floor a did assign material properties to each object.When I implemented the routine as desribed by the Pseudo-code my shadows for the boxes was casted on the back side of the floor. Here is the steps i followed in my code.'================================================='1 Initialized my OpenGL Perspective as follow '=================================================  m_ScreenWidth = nWidth  m_ScreenHeight = nHeight  m_zNear = 0.01  m_zFar = (100.0 * nWidth / nHeight)  GL.glViewport(0, 0, nWidth, nHeight)  GL.glMatrixMode(GLFlags.GL_PROJECTION)  GL.glLoadIdentity()  GL.gluPerspective(45.0, nWidth / nHeight, m_zNear, m_zFar)  GL.glMatrixMode(GLFlags.GL_MODELVIEW)  GL.glLoadIdentity()  GL.glGetDoublev(GL.GL_PROJECTION_MATRIX, m_DefaultProjMatrix)'==============================================================================2. setup my connectivity (neighbour indices) for each face of of the box (each face is an triangle so for a box you would have 12 triangles) this is done for each object'==============================================================================Public Sub setConnectivity()    Dim iFA, iFB, iFAE, iFBE As Intege    Dim vertA1, vertA2, vertB1, vertB2 As Integer    Try  If bConnectivity = False ThenFor iFA = 0 To m_Faces.Count - 1 'FaceA    For iFAE = 0 To m_Faces(iFA).m_VertexIndices.Count - 1 'Edge A   For iFB = 0 To m_Faces.Count - 1 'FaceB If iFB <> iFA Then   For iFBE = 0 To m_Faces(iFB).m_VertexIndices.Count - 1 'Edge B  vertA1 = m_Faces(iFA).m_VertexIndices(iFAE)  vertA2 = m_Faces(iFA).m_VertexIndices((iFAE + 1) Mod 3)   vertB1 = m_Faces(iFB).m_VertexIndices(iFBE)   vertB2 = m_Faces(iFB).m_VertexIndices((iFBE + 1) Mod 3)   'Check if they are neighbours   If ((vertA1 = vertB1 And vertA2 = vertB2) Or _  (vertA1 = vertB2 And vertA2 = vertB1)) Then m_Faces(iFA).m_NeighbourIndices(iFAE) = iFB m_Faces(iFB).m_NeighbourIndices(iFBE) = iFA GoTo nextFaceB    End IfNextnextFaceB:   End If    Next    NextNextbConnectivity = True   End If   Catch ex As Exception  Debug.Print("setConnectivity - " & ex.Message)   End TryEnd Sub'===============================================================3. Implement my render routine where the Shadows are called from'===============================================================Public Sub Draw()   Try  GL.glClear(GLFlags.GL_COLOR_BUFFER_BIT Or   GLFlags.GL_DEPTH_BUFFER_BIT Or GLFlags.GL_STENCIL_BUFFER_BIT)  GL.glMatrixMode(GL.GL_MODELVIEW)  GL.glLoadIdentity()  GL.glPushMatrix()   GL.glTranslatef(0, 0, -0.2)  GL.glScalef(m_ViewSettings.m_ScaledFactor.X, m_ViewSettings.m_ScaledFactor.Y, m_ViewSettings.m_ScaledFactor.Z)  GL.glTranslatef(m_ViewSettings.m_TranslatedPos.X, m_ViewSettings.m_TranslatedPos.Y, m_ViewSettings.m_TranslatedPos.Z)  GL.glRotatef(m_ViewSettings.m_Rotation.X, 0.0#, 1.0#, 0.0#)  GL.glRotatef(m_ViewSettings.m_Rotation.Y, 1.0#, 0.0#, 0.0#)  'Setup Camara position  GL.gluLookAt(m_ViewSettings.m_DefaultTranslatedPos.X, m_ViewSettings.m_DefaultTranslatedPos.Y, 0, m_ViewSettings.m_DefaultTranslatedPos.X, m_ViewSettings.m_DefaultTranslatedPos.Y, m_ViewSettings.m_DefaultTranslatedPos.Z, 0.0#, 1.0#, 0.0#) m_ViewSettings.m_LightSettings.Initialize()   'Cast Shadows  m_Shadow.castShadow()  GL.glPopMatrix()  GL.glFlush()   Catch ex As Exception  Debug.Print("Draw - " & ex.Message)   End Try   m_DrawComplete = True    End Sub'====================================='4 Implement Shadow Routine '=====================================Public Sub castShadow()  Dim m_ShadowObj As clsShadowObject  If m_ShowShadow = False Then Exit SubDim light_ka() As Single = {0.3, 0.3, 0.3, 1}Dim light_pos() As Single = {0, 0, 0, 1}'Global Ambient Lightlight_ka(0) = 0.3light_ka(1) = 0.3light_ka(2) = 0.3light_ka(3) = 1Try   If m_OPGL.m_ViewSettings.m_LightSettings.LightPosChange = True Then    light_pos = m_OPGL.m_ViewSettings.m_LightSettings.Light (View.LightSettings.enumLight.Position)    GL.glDeleteLists(m_ShadowDisplayObject, 1)    m_ShadowDisplayObject = GL.glGenLists(1)    GL.glNewList(m_ShadowDisplayObject, GL.GL_COMPILE_AND_EXECUTE)    'Calculates for all the Objects which faces is visible to the light  position   CalculateFacesVisible(m_OPGL.m_ViewSettings.m_LightSettings.Light(View.LightSettings.enumLight.Position))    'Step 1    GL.glClear(GL.GL_DEPTH_BUFFER_BIT Or GL.GL_COLOR_BUFFER_BIT)    'Step4    GL.glEnable(GL.GL_DEPTH_TEST)    GL.glDepthFunc(GL.GL_LESS)    GL.glEnable(GL.GL_CULL_FACE)    GL.glCullFace(GL.GL_BACK)    GL.glEnable(GL.GL_LIGHTING)    GL.glDisable(GL.GL_LIGHT0)'// Draw the whole model with ambient lightGL.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, light_ka)For iShadowObjects As Integer = 0 To m_ShadowObjects.Count - 1m_ShadowObj = m_ShadowObjects(iShadowObjects)GL.glCallList(m_ShadowObj.m_DisplayList)m_ShadowObj = Nothing Next 'Step 5 GL.glDepthMask(0) GL.glEnable(GL.GL_BLEND) GL.glBlendFunc(GL.GL_ONE, GL.GL_ONE) light_ka(0) = 0.5 light_ka(1) = 0.5 light_ka(2) = 0.5 light_ka(3) = 0.1 GL.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, light_ka) 'Step 6 'A and B GL.glClear(GL.GL_STENCIL_BUFFER_BIT) GL.glColorMask(0, 0, 0, 0) GL.glEnable(GL.GL_STENCIL_TEST) GL.glStencilFunc(GL.GL_ALWAYS, 0, 1) GL.glStencilMask(1) 'Step 6 a b c 'For each occluder  For iShadowObjects As Integer = 0 To m_ShadowObjects.Count - 1    m_ShadowObj = m_ShadowObjects(iShadowObjects)    'b    GL.glCullFace(GL.GL_FRONT)    GL.glStencilOp(GL.GL_KEEP, GL.GL_INCR, GL.GL_KEEP)    'c    renderSilhouetteEdges(light_pos, m_ShadowObj, True)    'd    renderOccluderTri(light_pos, m_ShadowObj, True)    'e    GL.glCullFace(GL.GL_BACK)    GL.glStencilOp(GL.GL_KEEP, GL.GL_DECR, GL.GL_KEEP)    'frenderSilhouetteEdges(light_pos,m_ShadowObj, False)    renderOccluderTri(light_pos, m_ShadowObj, False)    m_ShadowObj = Nothing Next 'Step D 'Step E GL.glStencilFunc(GL.GL_EQUAL, 0, 1) GL.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_INCR) GL.glDepthFunc(GL.GL_EQUAL) GL.glColorMask(1, 1, 1, 1) 'Step F   For iShadowObjects As Integer = 0 To m_ShadowObjects.Count - 1    m_ShadowObj = m_ShadowObjects(iShadowObjects)    GL.glCallList(m_ShadowObj.m_DisplayList)    m_ShadowObj = Nothing  Next  'Step G  GL.glDepthFunc(GL.GL_LESS)  GL.glDisable(GL.GL_BLEND)  GL.glDisable(GL.GL_STENCIL_TEST)  GL.glDepthMask(1)  'GL.glDisable(GL.GL_CULL_FACE)  GL.glEndList()  m_OPGL.m_ViewSettings.m_LightSettings.LightPosChange = True   Else   GL.glCallList(m_ShadowDisplayObject)   End IfCatch ex As ExceptionDebug.Print("castShadow - " & ex.Message) End Try End Sub'----------------------------------------------------------------------Private Sub renderSilhouetteEdges(ByVal light_pos() As Single, ByVal m_ShadowObj As clsShadowObject, ByVal bFrontFacing As Boolean)   Dim nEdgeCount As Integer = 0   Dim m_FaceList As New List(Of Integer)   Dim Av As New ToolObject.clsPoint   Dim Bv As New ToolObject.clsPoint   Dim Cv As New ToolObject.clsPoint   Dim Dv As New ToolObject.clsPoint   Dim iNeighbourIndice As Integer   GL.glBegin(GL.GL_QUADS)    For iFaces As Integer = 0 To m_ShadowObj.m_Faces.Count - 1  'commented out wasn't sure how to implement this routine correctly  'If m_ShadowObj.m_Faces(iFaces).m_Visible = bFrontFacing Then   If m_ShadowObj.m_Faces(iFaces).m_Visible = True Then  nEdgeCount = m_ShadowObj.m_Faces(iFaces).m_VertexIndices.Count - 1   For iEdge As Integer = 0 To nEdgeCount iNeighbourIndice = m_ShadowObj.m_Faces(iFaces).m_NeighbourIndices(iEdge) 'commented out wasn't sure how to implement this routine 'If m_ShadowObj.m_Faces(iNeighbourIndice).m_Visible = (Not bFrontFacing) Then  If m_ShadowObj.m_Faces(iNeighbourIndice).m_Visible = False Then Av = m_ShadowObj.m_Vertices(m_ShadowObj.m_Faces(iFaces).m_VertexIndices(iEdge))    Bv = m_ShadowObj.m_Vertices(m_ShadowObj.m_Faces(iFaces).m_VertexIndices((iEdge + 1) Mod 3))  GL.glVertex3f((Bv.X * 1), (Bv.Y * 1), (Bv.Z * 1))  GL.glVertex3f((Av.X * 1), (Av.Y * 1), (Av.Z * 1))  GL.glVertex4f((Av.X  - light_pos(0)), (Av.Y - light_pos(1)), (Av.Z - light_pos(2)), 0) GL.glVertex4f((Bv.X - light_pos(0)), (Bv.Y - light_pos(1)), (Bv.Z  - light_pos(2)), 0)    End If  Next   End If  Next GL.glEnd()  End Sub'-------------------------------------------------------------------------Private Sub renderOccluderTri(ByVal light_pos() As Single, ByVal m_ShadowObj As clsShadowObject, ByVal bFrontFacing As Boolean)  Dim nEdgeCount As Integer = 0  Dim m_FaceList As New List(Of Integer)  Dim Av As New ToolObject.clsPoint  Dim Bv As New ToolObject.clsPoint  Dim Cv As New ToolObject.clsPoint  Dim Dv As New ToolObject.clsPoint  GL.glBegin(GL.GL_TRIANGLES)    For iFaces As Integer = 0 To m_ShadowObj.m_Faces.Count - 1    nEdgeCount = m_ShadowObj.m_Faces(iFaces).m_VertexIndices.Count - 1    For iEdge As Integer = 0 To nEdgeCount  Av = m_ShadowObj.m_Vertices(m_ShadowObj.m_Faces(iFaces).m_VertexIndices(iEdge))  'commented out wasn't sure how to implement this routine   'If m_ShadowObj.m_Faces(iFaces).m_Visible = (Not bFrontFacing) Then If m_ShadowObj.m_Faces(iFaces).m_Visible = False Then 'Back facing    GL.glVertex4f((Av.X - light_pos(0)), (Av.Y - light_pos(1)), (Av.Z - light_pos(2)), 0)    'commented out wasn't sure how to implement this routine    'ElseIf m_ShadowObj.m_Faces(iFaces).m_Visible = bFrontFacing Then    ElseIf m_ShadowObj.m_Faces(iFaces).m_Visible = True Then    'Front facing    GL.glVertex3f((Av.X * 1), (Av.Y * 1), (Av.Z * 1))End IfNext    Next    GL.glEnd() End Sub   
  You missed a very important point there.  If you are using normal perspective projection for rendering the shadow volume, you have to consider the capping explicitly.  The paper you read proposed a way to avoid capping that is using infinie shadow volume.  It can be achieved by using depth clamp extension on nvidia display card (GL_DEPTH_CLAMP_NV) or using a perspective project with far plane at infinite far.The way to prepare a perspective project with far plane at infinite far is documented in the paper.  Here is the function I used for my shadow volume project. void gluInfPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear ){  double t = 1/tan( fovy * G_PI / 180 / 2 );  double m[16];  memset( m, 0, 16 * sizeof(double) );  m[0] = t/aspect;  m[5] = t;  m[10] = -1;  m[11] = -1;  m[14] = -2*zNear;   glMultMatrixd( m );}When you render your shadow volume, call gluInfPerspective instead of gluPerspective.  And you will have a perspective project with far plane at infinite far.