December 12, 2021

SHADOW MAPPING WITH OPENGL

I present a graphical scene with shadows from at least one light source using OpenGL.


Project Source Code (My Github Repo)

In this project, I sought to add a sense of realism to our digital world by the use of shadows. In a previous project, I created a scene and added lighting to that scene. This in turn made a dynamic and realistic frame that was comparable to a natural setting with lights reflecting off different objects and their geometries.

The main thing it lacked was shadows. Shadows add depth and let the viewer immediately see where the light source comes from. Mix in the lighting and Blinn–Phong reflection model, and you inch closer to a scene that emulates reality. With this in mind, my goal was to build upon my previous projects source code to include Shadow Mapping.

Shadow Mapping is a shadowing technique that takes two rendering passes to complete. The first pass is a rendering of the scene from the point of view of the light, we put the camera at the position of the light source that is what will be casting the shadows. The image produced by this first pass will have a pixel value that is the depth of the scene. Basically it keeps record of the distances from the objects to the light source. Everything that can’t be seen from this view is going to be the shadows, and what we can see will be the objects lit. The second pass is a bit familiar, we are going to render basically the same scene as my previous project which is geometries with the Blinn-Phong lighting. Except we have the addition now of the shadowing computations. In these computations, we want to check if the current fragment is in the shadow or not. To do this, we will be checking if the distance between the light and the current fragment is at a greater distance than the distance between the light and the occluding geometry (The sampled depth that's been recorded in the texture). If it is the case that the sampled depth is at a location closer to the light source than the fragment is, then we make a shadow, if not it is where the light touches.

This project was broken up into 4 different tasks. In the first task, I needed to get the depth information that will be used for the shadow calculation. To do this, I had to create a depth shader. I created the files depth.frag and depth.vert that are used to output the depth info using the z component of the fragment coordinates.

In the second task, I had to do some overloading of the draw method in order to accommodate both the depth shader and the previously implemented (in previous project) surface shader. After this was done, I was able to run the program and see the geometries from the view of the camera that output a gradient image of the depth that each fragment has. In my implementation, the image showed further pixels from the camera/light source as whiter and the closer pixels as darker..

In the third task, (this is the furthest I was able to achieve in my implementation) we need to adjust the Light.h file to “generate, initialize, bind, and configure the depth map texture and depth map FBO.” Then in the display() function of main.cpp, we have to set up the two passes mentioned above. These two passes will be calling the draw methods created in our Scene.cpp. The last task is to do the shadow computations. We check if the light-space depth is different from the camera to the fragment depth distance. If it's less, we know that the fragment is being occluded (because light-space depth is closer to the light source than the fragment depth) and there is a shadow.

What the final result should look like:

You can see the shadows of the objects caused by the light source to bounce off other objects as if a realistic scene.