1、
2、
In Tutorial 15 we learnt how to create lightmaps, which encompasses(包含) static lighting. While it produces very nice shadows, it doesn’t deal with animated(动画) models.
Shadow maps are the current (as of 2016) way to make dynamic shadows. The great thing about them is that it’s fairly(相当地) easy to get to work. The bad thing is that it’s terribly difficult to get to work right.
In this tutorial, we’ll first introduce the basic algorithm(算法), see its shortcomings(缺点), and then implement some techniques(技术) to get better results. Since at time of writing (2012) shadow maps are still a heavily researched topic(研究课题), we’ll give you some directions to further improve(改善) your own shadowmap, depending on your needs.
Basic shadowmap
The basic shadowmap algorithm consists(包含) in two passes. First, the scene(场景) is rendered from the point of view of the light. Only the depth of each fragment is computed. Next, the scene is rendered as usual, but with an extra test to see it the current fragment is in the shadow.
The “being in the shadow” test is actually quite simple. If the current sample is further from the light than the shadowmap at the same point, this means that the scene contains an object that is closer(更接近) to the light. In other words, the current fragment is in the shadow.
The following image might help you understand the principle(原理) :
Rendering the shadow map
In this tutorial, we’ll only consider(考虑) directional(定向的) lights - lights that are so far away that all the light rays can be considered parallel(平行的). As such, rendering the shadow map is done with an orthographic(直角的) projection matrix. An orthographic matrix is just like a usual perspective(透视的) projection matrix, except that no perspective is taken into account - an object will look the same whether it’s far or near the camera.
Setting up the rendertarget and the MVP matrix
Since Tutorial 14, you know how to render the scene into a texture in order to access it later from a shader.
Here we use a 1024x1024 16-bit depth texture to contain the shadow map. 16 bits are usually enough for a shadow map. Feel free to experiment(实验;尝试) with these values. Note that we use a depth texture, not a depth renderbuffer, since we’ll need to sample(取样) it later.
// The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer. GLuint FramebufferName = 0; glGenFramebuffers(1, &FramebufferName); glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName); // Depth texture. Slower than a depth buffer, but you can sample it later in your shader GLuint depthTexture; glGenTextures(1, &depthTexture); glBindTexture(GL_TEXTURE_2D, depthTexture); glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT16, 1024, 1024, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0); glDrawBuffer(GL_NONE); // No color buffer is drawn to. // Always check that our framebuffer is ok if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) return false;
The MVP matrix used to render the scene from the light’s point of view is computed as follows :
- The Projection matrix is an orthographic matrix which will encompass everything in the axis-aligned box (-10,10),(-10,10),(-10,20) on the X,Y and Z axes respectively. These values are made so that our entire *visible *scene is always visible ; more on this in the Going Further section.
- The View matrix rotates the world so that in camera space, the light direction is -Z (would you like to re-read Tutorial 3 ?)
- The Model matrix is whatever you want.
glm::vec3 lightInvDir = glm::vec3(0.5f,2,2); // Compute the MVP matrix from the light‘s point of view glm::mat4 depthProjectionMatrix = glm::ortho<float>(-10,10,-10,10,-10,20); glm::mat4 depthViewMatrix = glm::lookAt(lightInvDir, glm::vec3(0,0,0), glm::vec3(0,1,0)); glm::mat4 depthModelMatrix = glm::mat4(1.0); glm::mat4 depthMVP = depthProjectionMatrix * depthViewMatrix * depthModelMatrix; // Send our transformation to the currently bound shader, // in the "MVP" uniform glUniformMatrix4fv(depthMatrixID, 1, GL_FALSE, &depthMVP[0][0])
3、
4、
5、