///////////// Constructor/Destructor ////////////////////////
SunLight::SunLight()
{
vDir = Vector3f(0.125f, -0.75f, -0.125f);
vDir.normalize();
//lightView.DisableMatrixTransform();
SetAmbientColor(0.4f, 0.4f, 0.4f, 1.0f);
SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
SetSpecularColor(1.0f, 1.0f, 1.0f, 1.0f);
SetShininess(128.0f);
bIsEnabled = true;
nShadowMapSize = 512;
}
SunLight::SunLight(Vector3f direction)
{
vDir = direction;
vDir.normalize();
//lightView.DisableMatrixTransform();
SetAmbientColor(0.4f, 0.4f, 0.4f, 1.0f);
SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
SetSpecularColor(1.0f, 1.0f, 1.0f, 1.0f);
SetShininess(128.0f);
bIsEnabled = true;
nShadowMapSize = 512;
}
SunLight::~SunLight()
{
bIsEnabled = false;
if(texDepthMap.GetID() > 0)
texDepthMap.Free();
DestroyPBuffer();
}
///////////// Render the light source ///////////////////////
void SunLight::RenderAll(void)
{
if(bShadowMapping && eyeView)
{
CalcLightView();
BeginDepthMapPass();
//Render the tree
for(int i=0; i < nNumOfChildren; i++)
{
oChildren->RenderAll();
}
EndDepthMapPass();
BeginShadowPass();
}
//Enable lighting
if(bIsEnabled)
{
glEnable(GL_LIGHTING);
float fDir[4] = { vDir.x, vDir.y, vDir.z, 0.0f };
glLightfv(GL_LIGHT0, GL_POSITION, fDir);
glLightfv(GL_LIGHT0, GL_AMBIENT, fAmbientColor);
glLightfv(GL_LIGHT0, GL_DIFFUSE, fDiffuseColor);
glEnable(GL_LIGHT0);
glMaterialfv(GL_FRONT, GL_SPECULAR, fSpecularColor);
glMaterialf(GL_FRONT, GL_SHININESS, fShininess);
}
//Render the tree
for(int i=0; i < nNumOfChildren; i++)
{
oChildren->RenderAll();
}
//Disable lighting
if(bIsEnabled)
{
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
}
if(bShadowMapping && eyeView)
{
EndShadowPass();
}
}
///////////// Initialize the shadow mapping //////////////////
bool SunLight::CreatePBuffer(void)
{
//Create a P-Buffer
int nPBuffer[16] = { WGL_TEXTURE_FORMAT_ARB,
WGL_TEXTURE_RGBA_ARB,
WGL_TEXTURE_TARGET_ARB,
WGL_TEXTURE_2D_ARB,
0 };
hPBuffer = wglCreatePbufferARB(RenderManager::Instance()->GetTarget(),
RenderManager::Instance()->GetPixelFormatIndex(),
nShadowMapSize,
nShadowMapSize,
nPBuffer);
hDCPBuffer = wglGetPbufferDCARB(hPBuffer);
hRCPBuffer = RenderManager::Instance()->GetRenderingContext();
if(!hPBuffer)
{
MESSAGE("WARNING: PBuffer could not be created -> no shadows!");
return false;
}
return true;
}
void SunLight::DestroyPBuffer(void)
{
//Destroy the P-Buffer
if(hPBuffer)
{
wglReleasePbufferDCARB(hPBuffer, hDCPBuffer);
wglDestroyPbufferARB(hPBuffer);
}
hPBuffer = NULL;
//Release the device context
if(hDCPBuffer && gHWND)
ReleaseDC(gHWND, hDCPBuffer);
hDCPBuffer = NULL;
//Switch back to rendering into the frame buffer
wglMakeCurrent(RenderManager::Instance()->GetTarget(),
RenderManager::Instance()->GetRenderingContext());
}
bool SunLight::InitShadowMapping(void)
{
//Check for the extensions
if(!ExtensionManager::Instance()->IsSupported("GL_ARB_depth_texture"))
{
MESSAGE("WARNING: Hardware does not support shadows: lacking GL_ARB_depth_texture");
bShadowMapping = false;
return false;
}
if(!ExtensionManager::Instance()->IsSupported("GL_ARB_shadow"))
{
MESSAGE("WARNING: Hardware does not support shadows: lacking GL_ARB_shadow");
bShadowMapping = false;
return false;
}
if(!ExtensionManager::Instance()->IsSupported("WGL_ARB_pbuffer"))
{
MESSAGE("WARNING: Hardware does not support shadows: lacking WGL_ARB_pbuffer");
bShadowMapping = false;
return false;
}
//Create the depth map
float fAmbientColor[4] = { 0.65f, 0.65f, 0.65f, 1.0f }; //Shadow ambient color
bShadowMapping = texDepthMap.CreateShadowMap(nShadowMapSize, fAmbientColor);
if(!bShadowMapping)
{
MESSAGE("WARNING: Shadow depth map could not be created. Shadows are disabled.");
return false;
}
if(!CreatePBuffer())
{
texDepthMap.Free();
return false;
}
return true;
}
void SunLight::CalcLightView(void)
{
//Makes no sense, really. This is what I want to get working... --->
Matrix4x4 lightViewMatrix;
Vector3f *vFrustum = eyeView->GetFrustumHandle()->GetFrustumPoints();
Vector3f vFrustumLS[8];
memcpy(vFrustumLS, vFrustum, sizeof(Vector3f) * 8);
Vector3f vFrustumLSMin, vFrustumLSMax;
//Calculate the light view coordinate system
Vector3f vLightX = vDir.cross(Vector3f(0.0f, 1.0f, 0.0f));
Vector3f vLightY = vLightX.cross(vDir);
vLightX.normalize();
vLightY.normalize();
lightViewMatrix(0, 0) = vLightX.x; lightViewMatrix(0, 1) = vLightX.y; lightViewMatrix(0, 2) = vLightX.z;
lightViewMatrix(1, 0) = vLightY.x; lightViewMatrix(1, 1) = vLightY.y; lightViewMatrix(1, 2) = vLightY.z;
lightViewMatrix(2, 0) = -vDir.x; lightViewMatrix(2, 1) = -vDir.y; lightViewMatrix(2, 2) = -vDir.z;
//Position
lightViewMatrix.Translate(-(eyeView->GetPosition()));
//Set the light modelview matrix
Matrix4x4 *lightModelView = lightView.GetModelviewMatrixHandle();
//memcpy(lightModelView, &lightViewMatrix, sizeof(Matrix4x4));
//Set up the light like a normal camera (for testing only)
lightView.SetPerspective(90.0f, 1.0f, 1.0f, 1000.0f);
lightView.SetViewport(0, 0, nShadowMapSize, nShadowMapSize);
lightView.SetPosition(Vector3f(2500.0f, 400.0f, 3000.0f));
lightView.LookAt(Vector3f(2688.0f, 0.0f, 2688.0f));
}
void SunLight::BeginDepthMapPass(void)
{
//State: Shadow pass
State::Instance()->EnableShadowPass();
//Render into the P-Buffer
wglMakeCurrent(hDCPBuffer, hRCPBuffer);
//Clear the depth buffer
glClear(GL_DEPTH_BUFFER_BIT);
//Set the light view
eyeView->Disable(); //Disable the camera
lightView.Enable(); //Enable the lightview "camera"
glLoadIdentity(); //Make the modelview matrix the identity matrix
lightView.Update(0.0f); //Load the projection and modelview matrix of the lightview "camera"
//Disable unnecessary states
State::Instance()->SetColorMask(false, false, false, false);
State::Instance()->LockColorMask();
State::Instance()->DisableBlending();
State::Instance()->LockBlending();
State::Instance()->DisableTexturing();
State::Instance()->LockTexturing();
}
void SunLight::EndDepthMapPass(void)
{
//State: Render pass
State::Instance()->DisableShadowPass();
//Save the depth buffer to the depth map
glBindTexture(GL_TEXTURE_2D, texDepthMap.GetID());
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, nShadowMapSize, nShadowMapSize);
//Render into the frame buffer
wglMakeCurrent(RenderManager::Instance()->GetTarget(),
RenderManager::Instance()->GetRenderingContext());
//Enable states
State::Instance()->UnlockColorMask();
State::Instance()->SetColorMask(true, true, true, true);
State::Instance()->UnlockBlending();
State::Instance()->UnlockTexturing();
State::Instance()->EnableTexturing();
//Set the eye view
lightView.Disable(); //Disable the lightview "camera"
eyeView->Enable(); //Enable the camera
glLoadIdentity(); //Make the modelview matrix the identity matrix
eyeView->Update(0.0f); //Load the projection and modelview matrix of the camera
}
void SunLight::BeginShadowPass(void)
{
//Clear the depth buffer
glClear(GL_DEPTH_BUFFER_BIT);
const float x[4] = { 1.0f, 0.0f, 0.0f, 0.0f };
const float y[4] = { 0.0f, 1.0f, 0.0f, 0.0f };
const float z[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
const float w[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
const float bias[16] = { 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f };
//Bind the depth map to texture unit 4
texDepthMap.Bind(4);
//Setup texture coordinate generation for projective texture mapping
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);
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);
glTexGenfv(GL_S, GL_EYE_PLANE, x);
glTexGenfv(GL_T, GL_EYE_PLANE, y);
glTexGenfv(GL_R, GL_EYE_PLANE, z);
glTexGenfv(GL_Q, GL_EYE_PLANE, w);
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(bias);
lightView.GetProjectionMatrixHandle()->Apply();
lightView.GetModelviewMatrixHandle()->Apply();
glMatrixMode(GL_MODELVIEW);
//Compare the depth values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
}
void SunLight::EndShadowPass(void)
{
//Unbind the depth map
texDepthMap.Unbind();
//Do not compare anything
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
//Disable texture coordinate generation
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
glDisable(GL_TEXTURE_GEN_Q);
//Reset the texture matrix
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
}
Shadow Mapping Math
Hi,
I really need some help on implementing shadow mapping. I thought it would be easier :-).
I create a PBuffer, render my scene from the light's point of view into the buffer and copy it into a depth texture (GL_DEPTH_COMPONENT). In my second rendering pass I set up projective texture mapping on one texture layer, bind the depth texture to it and compare Z-Values using GL_ARB_shadow. That works actually quite fine, I mean shadows are drawn that way.
BUT: My problem is acutally the "rendering from the light's point of view". My light is directional, so it actually has no position. The light simulates sunlight on a relativly large outdoor terrain.
For testing reasons, I set up the lightview as a normal perspective-view camera. You can see what happens if you look at the following screenshot:
Screenshot 02
What I would like to do is: Take the dimensions of the camera's viewing frustum and calculate the position and perspective of the light, so that it always faces into the same direction, but still always has the camera's view frustum within it's own. I hope I could explain that precisly enough ;-)
If you look at another screenshot:
Screenshot 01 you can also see, that objects further away from the camera, seem to have some kind of depth precision problem. If I render the depth map onto a textured quad, I can just see a white primitive, no shades at all, so maybe z-values are very close to each other. But this might also solve with the correct light view.
Here's the shadow rendering code, if you want to have a look on it (CalcLightView() would be the function which sets up the lightview, but does only set up something like a light-camera for now.)
I hope someone can help me out with this...
Thanks in advance.
Quote:BUT: My problem is acutally the "rendering from the light's point of view". My light is directional, so it actually has no position. The light simulates sunlight on a relativly large outdoor terrain.
choose a position eg aim the light at the piece of ground 100meters in front of the camera
Quote:you can also see, that objects further away from the camera, seem to have some kind of depth precision problemi cant see what u mean, perhaps u mean zfighting (can be fixed somewhat with using polygonoffset)
also since u have a projection modelview matrix, each pixel in the shadowmap is gonna cover a larger screenarea thus will look worse. ways to fix this are one of the various methods of perspective shadowmapping (3/4 different methods, none are easy to do) or use multiple shadowmaps
Thanks for your ideas.
After days, I finally figured it out. Found some sources on the net, mainly DirectX but now I got it working with OpenGL in a similar way. Since the light position changes with the camera position and only the view frustum is on the depth map, I could squeeze a bit more precision out, compared to depth-mapping the whole terrain. A 2048x2048 shadow map looks quite good. Nevertheless, a disadvantage is, that since the virtual light position changes with the camera's view, you can see the shadow edges flickering whenever moving the camera. Having a low-res depth map even makes it worse.
But I have to live with this, maybe I can even change that a bit, didn't try yet.
This "z-fighting" problem, disappears when I move the far clipping plane of the camera further away from it. Having small light angles, makes the effect visible again, if I don't move the clipping plane away. I still think it's a precision or inacurracy problem between the depth values in the z-buffer and the depth map. However...
For everyone who's interested, here's the code:
After days, I finally figured it out. Found some sources on the net, mainly DirectX but now I got it working with OpenGL in a similar way. Since the light position changes with the camera position and only the view frustum is on the depth map, I could squeeze a bit more precision out, compared to depth-mapping the whole terrain. A 2048x2048 shadow map looks quite good. Nevertheless, a disadvantage is, that since the virtual light position changes with the camera's view, you can see the shadow edges flickering whenever moving the camera. Having a low-res depth map even makes it worse.
But I have to live with this, maybe I can even change that a bit, didn't try yet.
This "z-fighting" problem, disappears when I move the far clipping plane of the camera further away from it. Having small light angles, makes the effect visible again, if I don't move the clipping plane away. I still think it's a precision or inacurracy problem between the depth values in the z-buffer and the depth map. However...
For everyone who's interested, here's the code:
void SunLight::CalcLightView(void){ //Get the eye matrices Matrix4x4 eyeProjection = *(eyeView->GetProjectionMatrixHandle()); Matrix4x4 eyeModelView = *(eyeView->GetModelviewMatrixHandle()); //The lights modelview * projection matrix Matrix4x4 lightModelViewProjection; //Copy the frustum points to local memory Vector3f *vFrustumPtr = eyeView->GetFrustumHandle()->GetFrustumPoints(); Vector3f vFrustum[8]; memcpy(vFrustum, vFrustumPtr, sizeof(Vector3f) * 8); //Frustum AABB values Vector3f vMin, vMax; //Set the lights modelview and projection matrix to the identity matrix lightModelView.LoadIdentity(); lightProjection.LoadIdentity(); //Set the shadow map viewport glViewport(0, 0, nShadowMapSize, nShadowMapSize); //Look in the unit cube into the direction of the light lightModelView.LookAt(Vector3f(0.0f, 0.0f, 0.0f), -vDir, Vector3f(0.0f, 1.0f, 0.0f)); //Make the projection matrix the unit cube (Left handed coordinate system) lightProjection.OrthoLH(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); //Calculate the modelview * projection matrix lightModelViewProjection = lightModelView * lightProjection; //Transform the frustum coordinates from world space into the light space for(int i=0; i < 8; i++) vFrustum = lightModelViewProjection * vFrustum; //Obtain the min and max coordinate values for the AABB vMin = vFrustum[0]; for(int i=0; i < 8; i++) { if(vFrustum.x < vMin.x) vMin.x = vFrustum.x; if(vFrustum.y < vMin.y) vMin.y = vFrustum.y; if(vFrustum.z < vMin.z) vMin.z = vFrustum.z; } vMax = vMin; for(int i=0; i < 8; i++) { if(vFrustum.x > vMax.x) vMax.x = vFrustum.x; if(vFrustum.y > vMax.y) vMax.y = vFrustum.y; if(vFrustum.z > vMax.z) vMax.z = vFrustum.z; } //Now set up a new projection matrix that will just project the //exact dimensions of the AABB of the current view frustum //(left handed coordinate system) lightProjection.OrthoLH(vMin.x, vMax.x, vMin.y, vMax.y, vMin.z, vMax.z);}
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement