| DevMaster.net |
| Home | Forums | 3D Engines Database | Wiki | Articles/Tutorials | Game Dev Jobs | IRC Chat Network | Contact Us | |
| Projective Shadows using Stencil Buffer |
|
|
|
09/08/2003
|
||
IntroductionIn this article, I will explain how to render projective shadows in OpenGL using the stencil buffer. I will also talk about the advantages and disadvantages of this method. I do not guarantee that everything in this article is right and if you find anything wrong, please e-mail me. Advantages
Disadvantages
The PrinciplesJust as a reminder, Webster’s definition of a shadow is: “shade cast upon a surface by something blocking light” if you didn’t know :) The stencil buffer is not the same buffer as the Z buffer and depth buffer. The stencil buffer is used to restrict drawing to certain portions of the screen. The following are the steps to take when implementing projective shadows with stencil buffer:
ImplementationStep 1: Limiting the rendering of the shadowsWe need to limit the area the shadow is cast on by enabling the stencil test. We don’t want the shadow to come outside the surface the shadow will be cast on.
Figure 1 – The shadow has been made red to show that it also is drawn outside the surface that it should
Figure 2 – A proper shadow that’s only cast on the surface To only cast the shadow on the surface, we draw the surface in the stencil buffer and put a 1 every were we draw. After step one, we will only write to areas in the color buffer were the stencil buffer has a one until we disable the stencil test. // turning off writing to the color buffer and depth buffer so we only // write to stencil buffer glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); // enable stencil buffer glEnable(GL_STENCIL_TEST); // write a one to the stencil buffer everywhere we are about to draw glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF); // this is to always pass a one to the stencil buffer where we draw glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); // render the plane which the shadow will be on // color and depth buffer are disabled, only the stencil buffer // will be modified DrawFloor(0,0,0); // turn the color and depth buffers back on glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); // until stencil test is diabled, only write to areas where the // stencil buffer has a one. This is to draw the shadow only on // the floor. glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); // don't modify the contents of the stencil buffer glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); Step 2: Create a shadow-projection matrix basedThe idea behind the algorithm is to cast a shadow from the object we want to make a shadow from, onto a plane from the lights perspective. The 4x4 matrix we get from this algorithm must be multiplied with the 'model view' matrix before we render the object. The code for the algorithm: void SetShadowMatrix(float fDestMat[16], float fLightPos[4], float fPlane[4]) { float dot; // dot product of plane and light position dot = fPlane[0] * fLightPos[0] + fPlane[1] * fLightPos[1] + fPlane[2] * fLightPos[2] + fPlane[3] * fLightPos[3]; // first column fDestMat[0] = dot - fLightPos[0] * fPlane[0]; fDestMat[4] = 0.0f - fLightPos[0] * fPlane[1]; fDestMat[8] = 0.0f - fLightPos[0] * fPlane[2]; fDestMat[12] = 0.0f - fLightPos[0] * fPlane[3]; // second column fDestMat[1] = 0.0f - fLightPos[1] * fPlane[0]; fDestMat[5] = dot - fLightPos[1] * fPlane[1]; fDestMat[9] = 0.0f - fLightPos[1] * fPlane[2]; fDestMat[13] = 0.0f - fLightPos[1] * fPlane[3]; // third column fDestMat[2] = 0.0f - fLightPos[2] * fPlane[0]; fDestMat[6] = 0.0f - fLightPos[2] * fPlane[1]; fDestMat[10] = dot - fLightPos[2] * fPlane[2]; fDestMat[14] = 0.0f - fLightPos[2] * fPlane[3]; // fourth column fDestMat[3] = 0.0f - fLightPos[3] * fPlane[0]; fDestMat[7] = 0.0f - fLightPos[3] * fPlane[1]; fDestMat[11] = 0.0f - fLightPos[3] * fPlane[2]; fDestMat[15] = dot - fLightPos[3] * fPlane[3]; } Step 3: Turn off lights and texture mappingWe disable texture mapping so we don’t see the shadow with textures on it. We disable lights so the shadow is not lit. Because the shadow is supposed to be drawn at the same level as the surface it is on, something that’s called “Z fighting” can occur, where the shadow can partially be behind the surface. This is because the depth values don’t have infinite precision. Solutions to this include the use of polygon offsets, stencil test and disabling the depth buffer. We have here disabled the depth buffer which will draw the shadow above everything else. Step 4: Turn off depth testIf depth test is enabled, you can test if a new color that arrives for a pixel is closer to the window than the one already in the depth buffer. If it passes, it then replaces the value already in the depth buffer. For projective shadows, depth test must be off. Step 5: Enable blendingWe now enable blending so the shadow is blended with the texture of the surface. Blending in OpenGL allow you to have effects such as transparency into the scene. With transparency, you can simulate water, window and other objects that you can see through. Step 6: Render all objects that you want to cast shadowsWe simply draw the object that is casting the shadow. Step 7: Restore old settingsRestore any changes made to draw the shadow. After these seven steps, the object is then drawn normally and this is what the results look like:
Figure 3 – the final program SummarizeI’ve used a lot of time on this article and it has helped me understand how to do one of the many types of implementations of shadows in OpenGL and I hope it has helped you too. Please e-mail me at arne9184@yahoo.com and tell me if this was helpful to you or if you have any comments or questions. References
|
|
|
| © 2003-2004 DevMaster.net. All Rights Reserved. Terms of Use & Privacy Policy | Want to write for us? Click here |