Chapter XII Shadow Mapping

Computer Graphics with OpenGL ES (J. Han) Why Shadows?

. Shadows help us understand the spatial relationships among objects in a scene.

Computer Graphics with OpenGL ES (J. Han) 12-2 Shadow Mapping

. Two-pass algorithm . Pass 1 – not real rendering . Render the scene from the position of the light source. . Store only the depths into the shadow map, which is a depth map with respect to the light source.

visualized in gray scale

Computer Graphics with OpenGL ES (J. Han) 12-3 Shadow Mapping (cont’d)

. Pass 2 . Render the scene from the camera position. It’s real rendering. . For each pixel, compare its distance d to the light source with the depth value z in the shadow map. . If d > z, the pixel is in shadows. . Otherwise, the pixel is lit.

Computer Graphics with OpenGL ES (J. Han) 12-4 Frame Buffer Object

. A framebuffer object (often referred to as an FBO) is a collection of color, depth, and attachment points. (Don’t care the stencil buffer for now.) . Various 2D images can be attached to the color attachment point in the FBO. . Similarly, various 2D images containing depth values can be attached to the depth attachment point of an FBO.

. We are going to create a 2D texture for depth map/buffer and attach it to the FBO.

Computer Graphics with OpenGL ES (J. Han) 12-5 Frame Buffer Object (cont’d)

. glGenFramebuffers(GLsizei n, GLuint *ids) returns n FBO names in ids, which are unsigned integers. . glBindFramebuffer(GLenum target, GLuint framebuffer), where target must be set to GL_FRAMEBUFFER and framebuffer is the name returned by glGenFramebuffers. . Once an FBO has been bound, the depth attachment of the current FBO can be set to a texture. . glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level): . target must be set to GL_FRAMEBUFFER . attachment can be GL_DEPTH_ATTACHMENT . textarget specifies the texture target. This is the value specified in the target argument in glTexImage2D . texture specifies the texture object level specifies the mip-level of texture image

Computer Graphics with OpenGL ES (J. Han) 12-6 Shadow Mapping – GL Program

. First of all, let’s generate the depth map.

// Generate a texture for a depth map glGenTextures(1, &mTexShadow); glBindTexture(GL_TEXTURE_2D, mTexShadow);

// mode glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

// Setup comparison glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE ); // depth comparison glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);

// Reserve the memory space in GPU memory glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, mShadowWidth, mShadowHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL); // e.g., not GL_RGBA

Computer Graphics with OpenGL ES (J. Han) 12-7 Shadow Mapping – GL Program (cont’d)

. Then, let’s create an FBO and attach the depth map to it.

// Generate an FBO glGenFramebuffers(1, &mShadowFbo); glBindFramebuffer(GL_FRAMEBUFFER, mShadowFbo);

// Setup for depth only FBOs glReadBuffer(none); glDrawBuffers(1, &none);

// Attach the depth map to the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, mTexShadow, 0);

Computer Graphics with OpenGL ES (J. Han) 12-8 Shadow Map Creation

. For the 1st pass, the view parameters are specified “with respect to the light source” and the so-called light space is defined.

projection perspective view transform light transform homogeneous division light space space light space (in NDC)

Computer Graphics with OpenGL ES (J. Han) 12-9 Shadow Mapping – (1st Pass)

// Vertex

#version 300 es projection perspective view transform light transform homogeneous division light space space light space (in NDC) layout(location = 0) in vec3 position;

uniform mat4 lightVpMat; uniform mat4 worldMat;

void main() { // Render the scene from the position of the light source gl_Position = lightVpMat * worldMat * vec4(position, 1.0); }

// Fragment Shader #version 300 es

precision mediump float;

void main() { // empty function: not color buffer but depth buffer is filled. }

Computer Graphics with OpenGL ES (J. Han) 12-10 Shadow Mapping Artifact – Surface Acne

. A brute-force implementation of shadow mapping suffers from two major problems. . Surface acne - a mixture of shadowed and lit areas . Shadow-map resolution sensitivity

Computer Graphics with OpenGL ES (J. Han) 12-11 Shadow Mapping Artifact – Surface Acne (cont’d)

. What’s the problem? The shadowed and lit pixels coexist on a surface area that should be entirely lit. . Note that the scene points sampled at the second pass are usually different from the scene points sampled at the first pass. . Suppose the nearest point sampling.

. In the example, p1 is to be lit, but judged to be in shadows because d1 > z1.

Computer Graphics with OpenGL ES (J. Han) 12-12 Shadow Mapping Artifact – Surface Acne (cont’d)

nd . At the 2 pass, add a small bias value to z1 such that d1 < z1´.

. Inversely, we can subtract some bias from d1.

Computer Graphics with OpenGL ES (J. Han) 12-13 Shadow Mapping Artifact – Surface Acne (cont’d)

. The bias value is usually fixed after a few trials.

. Anyway, the surface acne problem has been largely resolved.

Computer Graphics with OpenGL ES (J. Han) 12-14 Shadow Mapping Artifact – Resolution Sensitivity

. If the resolution of a shadow map is not high enough, multiple pixels may be mapped to a single texel of the shadow map. . This is a magnification case. Suppose you choose the nearest point sampling.

Computer Graphics with OpenGL ES (J. Han) 12-15 Shadow Mapping Artifact – Resolution Sensitivity (cont’d)

. Nearest point sampling leads to the well-known artifact. . Unfortunately, bilinear interpolation doesn’t help. You will see. . A simple but expensive solution is to increase the shadow map resolution.

. What else?

Computer Graphics with OpenGL ES (J. Han) 12-16 Shadow Mapping – Filtering

. Consider bilinear interpolation. . It is not good to have completely different results depending on the filtering options. . The shadow quality is not improved at all and the shadow still reveals the jagged edges.

Computer Graphics with OpenGL ES (J. Han) 12-17 Shadow Mapping – Filtering (cont’d)

. A solution to this problem is to first determine the visibilities of a pixel with respect to the four texels, and then interpolate the visibilities. This value is taken as the “degree of being lit.” . The technique of taking multiple texels from the shadow map and blending the pixel's visibilities against the texels is named percentage closer filtering (PCF).

. In GL, sampler2DShadow (combined with GL_LINEAR) supports PCF.

Computer Graphics with OpenGL ES (J. Han) 12-18 Shadow Map Reference

. For the 2nd pass, how do we reference the shadow map? . As the shadow map is generated “in the light space,” the scene points (vertices in VS) should be transformed to the light space too.

projection perspective view transform light transform homogeneous division light space space light space (in NDC)

. Then, the NDC points in the range [-1,1] should be converted into the texture coordinates in [0,1]. . The light-space vertices are not used for rendering, but just for referencing the shadow map. So, they are output as variables so as to be interpolated.

Computer Graphics with OpenGL ES (J. Han) 12-19 Shadow Mapping – Vertex Shader (2nd Pass)

Computer Graphics with OpenGL ES (J. Han) 12-20 Shadow Mapping – Vertex Shader (2nd Pass)

. What did we do? . For shadow map referencing, we did the following.

projection perspective view transform light transform homogeneous division light space space light space (in NDC)

. The result is named v_shadowCoord and is output so as to be interpolated. . Of course, the vertices went through the rendering pipeline as usual.

Computer Graphics with OpenGL ES (J. Han) 12-21 Shadow Mapping – Fragment Shader (2nd Pass)

sampler2DShadow (combined with GL_LINEAR) supports PCF. In textureProj, the second argument is divided by its last component such that the texture fetch occurs at (x/w, y/w, z/w).

Computer Graphics with OpenGL ES (J. Han) 12-22 Shadow Mapping – Fragment Shader (2nd Pass)

. What did we do? . For shadow map referencing, we used v_shadowCoord.

. In textureProj, the second argument is divided by its last component such that the texture is referenced at ((0.5x+0.5w)/w, (0.5y+0.5w)/w, (0.5z+0.5w)/w).

In [-1,1] of NDC . As x/w is in the range of [-1,1], 0.5x/w+0.5 is in the range of [0,1].

Computer Graphics with OpenGL ES (J. Han) 12-23