shadow mapping

BlitzMax Forums/BlitzMax OpenGL Programming/shadow mapping

I've been playing around with code taken from the More Opengl Programming book.

It mostly works but areas outside of the shadow map after projection are also shaded.

Is this to be expected ? I'd be lying if I said I understand everything the code is doing although I get the general principle

Are the extra shaded areas just the shadow map tiled?
Is the texture clamped?
Got a screeny? :)

heres the code. I warn you now its not pretty. I have been trying to get it to work before prettying it up. Mind you the source was pretty messed up with huge amounts of remmed out code and stuff. Not very helpful.

There are 3 main functions init, render and GenerateShadowmap. Render also includes some stuff for displaying the shadowmap(possibly not working correctly) and turning off shadows.

It seems to me that the error is in Render() as the shadow map seems to be generated ok.

I'm not expecting anyone to plough through the code and correct,although that would be nice, but some pointers would be helpful.

'shadowMap mark2


GLGraphics 1024,768,0,60,GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER

Global LIGHT_POS:Float[] = [ -60.0, 25.0, 0.0, 1.0 ]
Global CAMERA_POS:Float[] = [0.0, 100.0, 150.0, 1.0 ]

Global WHITE:Float[] =[ 1.0, 1.0, 1.0, 1.0]
Global BLACK:Float[] =[ 0.0, 0.0, 0.0, 0.0]
Global GREY:Float[] =[ 0.3, 0.3, 0.3, 0.3 ]
Global DARK_GREY:Float[] = [0.1, 0.1, 0.1, 0.1]
Global m_shadowMap:Int
Global   m_bounce:Float = 0.0
Global showShadowMap:Int = False
Global noShadows:Int = False
Global ambientShadowAvailable:Int = False
Global  m_windowHeight = 768
Global  m_windowWidth = 1024
Global sPlane:Float[] = [1.0, 0.0, 0.0, 0.0]
Global tPlane:Float[] = [0.0, 1.0, 0.0, 0.0]
Global rPlane:Float[] = [0.0, 0.0, 1.0, 0.0]
Global qPlane:Float[] = [0.0, 0.0, 0.0, 1.0]
Global m_quadric:Byte Ptr
m_quadric = gluNewQuadric()
gluQuadricNormals(m_quadric, GLU_SMOOTH)
Global modelview:Float[16], projection:Float[16]
Global texsize:Int = 128

'****************************************************
init()

'main loop
While Not AppTerminate() And Not KeyDown(KEY_ESCAPE)
update(0.5)
render()

Flip
If KeyHit(Key_N) showshadowmap = True
If KeyHit(KEY_m) showshadowmap = False
If KeyHit(KEY_O) noshadows = True
If KeyHit(KEY_P) noshadows = False
Wend

End

'*****************************************************

Function Init()
	
glewinit()
  

  glEnable(GL_DEPTH_TEST)
  glDepthFunc(GL_LEQUAL)

 
  glGenTextures(1, Varptr m_shadowMap)
  glBindTexture(GL_TEXTURE_2D, m_shadowMap)

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
  glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY)
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE)

  glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
  glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
  glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
  glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)

  glEnable(GL_LIGHT0)
  glEnable(GL_COLOR_MATERIAL)
  glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR)

  glPolygonOffset(4.0, 0.0)

 
End Function
'****************************************************************

Function SetupProjection(width:Int, height:Int)
	
	glViewport( 0, 0, width, height )

'	// set up a perspective projection
	glMatrixMode( GL_PROJECTION )
	glLoadIdentity()
	gluPerspective( 45.0, Float(width)/Float(height), 1.0, 1000.0 )
End Function
'*****************************************************************
Function Update( dt:Float )

  m_bounce :+ 10 * dt
End Function
'*****************************************************************

Function Render()

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  GenerateShadowMap()

  '// Track camera angle
  glMatrixMode(GL_PROJECTION)
  glLoadIdentity()
  gluPerspective(45.0, 1.0, 1.0, 1000.0)
  glMatrixMode(GL_MODELVIEW)
  glLoadIdentity()
  gluLookAt(CAMERA_POS[0], CAMERA_POS[1], CAMERA_POS[2],0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
  glViewport(0, 0, 1024,768)

  '// Track light position
  glLightfv(GL_LIGHT0, GL_POSITION, LIGHT_POS)

  '// Clear the window with current clearing color
  glClear(GL_DEPTH_BUFFER_BIT)

  If showShadowMap = True Then
  
   ' // Display shadow map For educational purposes
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    glMatrixMode(GL_TEXTURE)
    glPushMatrix()
    glLoadIdentity()
    glEnable(GL_TEXTURE_2D)
    glDisable(GL_LIGHTING)
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    '// Show the shadowMap at its actual size relative To window
    glBegin(GL_QUADS)
    glTexCoord2f(0.0, 0.0)
    glVertex2f(-1.0, -1.0)
    glTexCoord2f(1.0, 0.0)
    glVertex2f((Float(texsize)/Float(1024))*2.0-1.0,-1.0)
    glTexCoord2f(1.0, 1.0)
    glVertex2f((Float(texsize)/Float(1024))*2.0-1.0,Float(texsize)/Float(768)*2.0-1.0)
    glTexCoord2f(0.0, 1.0)
    glVertex2f(-1.0,Float(texsize)/Float(768)*2.0-1.0)
    glEnd()
    glDisable(GL_TEXTURE_2D)
    glEnable(GL_LIGHTING)
    glPopMatrix()
    glMatrixMode(GL_PROJECTION)
    gluPerspective(45.0, 1.0, 1.0, 1000.0)
    glMatrixMode(GL_MODELVIEW)
  
  Else If noShadows = True
  
   ' // Set up some simple lighting
    glLightfv(GL_LIGHT0, GL_AMBIENT, GREY)
   glLightfv(GL_LIGHT0, GL_DIFFUSE, WHITE)

    '// Draw objects in the scene
   RenderObjects(False)
  
  Else
  
      If ambientShadowAvailable = False Then
    
    
      '// Because there is no support For an "ambient"
      '// shadow compare fail value, we'll have to
      '// draw an ambient pass first...
      glLightfv(GL_LIGHT0, GL_AMBIENT, GREY)
      glLightfv(GL_LIGHT0, GL_DIFFUSE, DARK_GREY)

      '// Draw objects in the scene
      RenderObjects(False)
      '// Enable alpha test so that shadowed fragments are discarded
      glAlphaFunc(GL_GREATER, 0.9)
      glEnable(GL_ALPHA_TEST)
    EndIf

    glLightfv(GL_LIGHT0, GL_AMBIENT, grey)
    glLightfv(GL_LIGHT0, GL_DIFFUSE, WHITE)

    '// Set up shadow comparison
    glEnable(GL_TEXTURE_2D)
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,GL_COMPARE_R_TO_TEXTURE)

  
    '// Set up the eye plane For projecting the shadow map on the scene
    glEnable(GL_TEXTURE_GEN_S)
    glEnable(GL_TEXTURE_GEN_T)
    glEnable(GL_TEXTURE_GEN_R)
    glEnable(GL_TEXTURE_GEN_Q)
    glTexGenfv(GL_S, GL_EYE_PLANE, sPlane)
    glTexGenfv(GL_T, GL_EYE_PLANE, tPlane)
    glTexGenfv(GL_R, GL_EYE_PLANE, rPlane)
    glTexGenfv(GL_Q, GL_EYE_PLANE, qPlane)

    '// Draw objects in the scene
    RenderObjects(False)
    glDisable(GL_ALPHA_TEST)
    glDisable(GL_TEXTURE_2D)
    glDisable(GL_TEXTURE_GEN_S)
    glDisable(GL_TEXTURE_GEN_T)
    glDisable(GL_TEXTURE_GEN_R)
    glDisable(GL_TEXTURE_GEN_Q)
  
EndIf
End Function
'********************************************************************
Function RenderObjects(isShadowPass:Int)

  glPushMatrix()
  glColor3f(0.2, 0.9, 0.3)
  glTranslatef(0.0, 5 + 5 * Sin(m_bounce), 0.0)
  glRotatef(-90.0, 1.0, 0.0, 0.0)
  gluCylinder(m_quadric, 10, 0, 20, 20, 20)

  glPopMatrix()

  glPushMatrix()
  glColor3f(0.75, 0.2, 0.1)
  glTranslatef(15.0, 5.0, 12 * Cos(m_bounce/3.0))
  glusphere(M_quadric,5,20,20)

  glPopMatrix()

  glPushMatrix()
  glColor3f(0.5, 0.1, 0.9)
  glTranslatef(28.0, 6.0, -24.0)
  glRotatef(30.0, 0.0, 1.0, 0.0)
  glucylinder(m_quadric,6, 6, 20, 20, 20)
  glPopMatrix()

glDisable(GL_LIGHTING)
	glPushMatrix()
	glColor3f(1,1,1)	
	glTranslatef(light_pos[0],light_pos[1],light_pos[2])
	glusphere(m_quadric,1,20,20)
	glPopMatrix()
	
glEnable(GL_LIGHTING)	

	
  glPushMatrix()
glColor3f(.75,.75,.95)

For Local i = -50 To 50 Step 5
For Local j = -50 To 50 Step 5
glBegin(GL_QUADS)
glNormal3f(0,1,0)
glVertex3f(i+10,0,j+10)
glVertex3f(i-10,0,j+10)
glVertex3f(i-10,0,j-10)
glVertex3f(i+10,0,j-10)
glEnd()
Next
Next
glPopMatrix()

End Function
'*************************************************************
Function GenerateShadowMap()

  Local lightToSceneDistance:Float, nearPlane:Float, fieldOfView:Float
  Local lightModelview:Float[16], lightProjection:Float [16]

  '// Save the depth precision For where it's useful
  lightToSceneDistance = Sqr(LIGHT_POS[0]*LIGHT_POS[0]+LIGHT_POS[1]*LIGHT_POS[1]+LIGHT_POS[2]*LIGHT_POS[2])
  nearPlane = lightToSceneDistance - 150.0
  If nearPlane < 50.0  nearPlane = 50.0
  '// Keep the scene filling the depth texture
  fieldOfView = 17000.0 / lightToSceneDistance



 glViewport(0, 0, texsize, texsize)
  glMatrixMode(GL_PROJECTION)
  glLoadIdentity()
  'glFrustum(-12.0, 12.0, -20.0, 5.0, 13.0, 150.0)
 gluPerspective( 60,1, 13, 1000 )
	glGetFloatv(GL_PROJECTION_MATRIX, lightProjection)
  '// Switch To light's point of view
  glMatrixMode(GL_MODELVIEW)
  glLoadIdentity()
  gluLookAt(LIGHT_POS[0], LIGHT_POS[1], LIGHT_POS[2],0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
  glGetFloatv(GL_MODELVIEW_MATRIX, lightModelview)

  '// Clear the window with current clearing color
  glClear(GL_DEPTH_BUFFER_BIT)

  '// All we care about here is resulting depth values
  glShadeModel(GL_FLAT)
  glDisable(GL_LIGHTING)
  glDisable(GL_COLOR_MATERIAL)
  glDisable(GL_NORMALIZE)
  glColorMask(0, 0, 0, 0)

  '// Overcome imprecision
  glEnable(GL_POLYGON_OFFSET_FILL)

  '// Draw objects in the scene
  RenderObjects(True)

  '// Copy depth values into depth texture
  glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,0, 0, texsize, texsize, 0)

  '// Restore normal drawing state
  glShadeModel(GL_SMOOTH)
  glEnable(GL_LIGHTING)
  glEnable(GL_COLOR_MATERIAL)
  glEnable(GL_NORMALIZE)
  glColorMask(1, 1, 1, 1)
  glDisable(GL_POLYGON_OFFSET_FILL)

  '// Set up texture matrix For shadow map projection
  glMatrixMode(GL_TEXTURE)
  glLoadIdentity()
  glTranslatef(0.5, 0.5, 0.5)
  glScalef(0.5, 0.5, 0.5)
  glMultMatrixf(lightProjection)
  glMultMatrixf(lightModelview)

End Function
'********************************************************








Hi,

You need to use GL_CLAMP_TO_EDGE like this:
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
GL_CLAMP doesn't use the edge color on most graphics cards.

Oh, and it looks cool :) Any way to smooth the edges of the shadow, without shaders?

ahah thanks,so simple.

you can increase the shadowmaptexture size but this will kill the framerate. Storing the texture on the card itself might help this I guess.

Reducing the viewing frustum size in the shadow generation helps as well .

Its a pity this runs so slow. Doesn't seem like it would be practical to implement in a game or whatever.

didnt seem slow at all on my machine, nice work Kronos!

@fredborg if you make the shadow map 512 instead of 128 its a lot smoother...

And 16 times larger

curious this seems to run quicker on my laptop than my pc

The reason it's dreadfully slow is because you draw 16 times more quads for the ground than you need :) Also pushing quads to the graphics card like this is not exactly a fast solution.

Use this instead:
For Local i = -50 To 50 Step 20
For Local j = -50 To 50 Step 20
glBegin(GL_QUADS)
glNormal3f(0,1,0)
glVertex3f(i+10,0,j+10)
glVertex3f(i-10,0,j+10)
glVertex3f(i-10,0,j-10)
glVertex3f(i+10,0,j-10)
glEnd()
Next
Next
You used Step 5 instead of 20, meaning you had massive overdraws.

Yes, I know you can use a higher texture resolution, but the shadow edges are still very blocky.