opengl4.0 shading讲基础阴影贴图的部分有很多我不知道的细节,(基础阴影贴图算法当然所有书都有讲)所以干脆全部贴到下面把。P241
To create an OpenGL application that creates shadows using the shadow mapping technique,
use the following steps. We'll start by setting up a FBO to contain the shadow map texture,
and then move on to the required shader code:
1. In the main OpenGL program, set up a FBO with a depth buffer only. Declare a
GLuint variable named shadowFBO to store the handle to this framebuffer. The
depth buffer storage should be a texture object. You can use something similar to the
following code to accomplish this:
GLfloat border[]={1.0f,0.0f,0.0f,0.0f};
//The shadow maptexture
GLuint depthTex;
glGenTextures(1,&depthTex);
glBindTexture(GL_TEXTURE_2D,depthTex);
glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,
shadowMapWidth,shadowMapHeight,0,
GL_DEPTH_COMPONENT,GL_UNSIGNED_BYTE,NULL);
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_BORDER);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_BORDER);
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR,
border);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_MODE,
GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_FUNC,
GL_LESS);
//Assign the shadow map to texture channel 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,depthTex);
//Create and set up the FBO
glGenFramebuffers(1,&shadowFBO);
glBindFramebuffer(GL_FRAMEBUFFER,shadowFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D,depthTex,0);
GLenum drawBuffers[]={GL_NONE};
glDrawBuffers(1,drawBuffers);
// Revert to the default framebuffer for now
glBindFramebuffer(GL_FRAMEBUFFER,0);
2. Use the following code for the vertex shader:
#version 400
layout (location=0) in vec3 VertexPosition;
layout (location=1) in vec3 VertexNormal;
out vec3 Normal;
out vec3 Position;
// Coordinate to be used for shadow map lookup
out vec4 ShadowCoord;
uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVP;
uniform mat4 ShadowMatrix;
void main()
{
Position = (ModelViewMatrix *
vec4(VertexPosition,1.0)).xyz;
Normal = normalize( NormalMatrix * VertexNormal );
// ShadowMatrix converts from modeling coordinates
// to shadow map coordinates.
ShadowCoord =ShadowMatrix * vec4(VertexPosition,1.0);
gl_Position = MVP * vec4(VertexPosition,1.0);
}
3. Use the following code for the fragment shader:
#version 400
// Declare any uniforms needed for the Phong shading model
uniform sampler2DShadow ShadowMap;
in vec3 Position;
in vec3 Normal;
in vec4 ShadowCoord;
layout (location = 0) out vec4 FragColor;
vec3 phongModelDiffAndSpec()
{
// Compute only the diffuse and specular components of
// the Phong shading model.
}
subroutine void RenderPassType();
subroutine uniform RenderPassType RenderPass;
subroutine (RenderPassType)
void shadeWithShadow()
{
vec3 ambient = …;// compute ambient component here
vec3 diffAndSpec = phongModelDiffAndSpec();
// Do the shadow-map look-up
float shadow = textureProj(ShadowMap, ShadowCoord);
// If the fragment is in shadow, use ambient light only.
FragColor = vec4(diffAndSpec * shadow + ambient, 1.0);
}
subroutine (RenderPassType)
void recordDepth()
{
// Do nothing, depth will be written automatically
}
void main() {
// This will call either shadeWithShadow or recordDepth
RenderPass();
}
Within the main OpenGL program, perform the following steps when rendering.
Pass 1
1. Set the viewport, view, and projection matrices to those that are appropriate for the
light source.
2. Bind to the framebuffer containing the shadow map (shadowFBO).
3. Clear the depth buffer.
4. Select the subroutine function recordDepth.
5. Enable front-face culling.
6. Draw the scene.
Pass 2
1. Select the viewport, view, and projection matrices appropriate for the scene.
2. Bind to the default framebuffer.
3. Disable culling (or switch to back-face culling).
4. Select the subroutine function shadeWithShadow.
5. Draw the scene.
How it works...
The first block of the preceding code demonstrates how to create a framebuffer object
(FBO) for our shadow map texture. The FBO contains only a single texture connected to its
depth buffer attachment. The first few lines of code create the shadow map texture. The
texture is created using the glTexImage2D function with an internal format of GL_DEPTH_
COMPONENT. Note that NULL is provided as the last argument. This tells OpenGL to allocate
space for the texture, but to leave the data uninitialized.
We use GL_NEAREST for GL_TEXTURE_MAG_FILTER and GL_TEXTURE_MIN_FILTER here,
although GL_LINEAR could also be used, and might provide slightly better looking results. We
use GL_NEAREST here so that we can see the aliasing artifacts clearly, and the performance
will be slightly faster.
Next, the GL_TEXTURE_WRAP_* modes are set to GL_CLAMP_TO_BORDER. When a fragment
is found to lie completely outside of the shadow map (outside of the light's frustum), then
the texture coordinates for that fragment will be greater than one or less than zero. When
that happens, we need to make sure that those points are not treated as being in shadow.
When GL_CLAMP_TO_BORDER is used, the value that is returned from a texture lookup
(for coordinates outside the 0..1 range) will be the border value. The default border value is
(0,0,0,0). When the texture contains depth components, the first component is treated as the
depth value. A value of zero will not work for us here because a depth of zero corresponds to
points on the near plane. Therefore all points outside of the light's frustum will be treated as
being in shadow! Instead, we set the border color to (1,0,0,0) using the glTexParameterfv
function, which corresponds to the maximum possible depth.
The next two calls to glTexParameteri affect settings that are specific to depth textures.
The first call sets GL_TEXTURE_COMPARE_MODE to GL_COMPARE_REF_TO_TEXTURE. When
this setting is enabled, the result of a texture access is the result of a comparison, rather than
a color value retrieved from the texture. The third component of the texture coordinate (the
r component) is compared against the value in the texture at location (s,t). The result of the
comparison is returned as a single floating point value. The comparison function that is used
is determined by the value of GL_TEXTURE_COMPARE_FUNC, which is set on the next line.
In this case, we set it to GL_LESS which means that the result will be 1.0 if the r value of the
texture coordinate is less than the value stored at (s,t). (Other options include GL_LEQUAL,
GL_ALWAYS, GL_GEQUAL, and so on.)
The next few lines create and set up the FBO. The shadow map texture is attached to the FBO
as the depth attachment with the function glFramebufferTexture2D. For more details
about FBOs, check out the recipe in Chapter 3, Rendering to a texture.
The vertex shader is fairly simple. It converts the vertex position and normal to camera
coordinates and passes them along to the fragment shader via the output variables
Position and Normal. The vertex position is also converted into shadow map coordinates
using ShadowMatrix. This is the matrix S that we referred to some time back. It converts a
position from modeling coordinates to shadow coordinates. The result is sent to the fragment
shader via the output variable ShadowCoord.
As usual, the position is also converted to clip coordinates and assigned to the built-in output
variable gl_Position.
In the fragment shader, we provide different functionality for each pass. In the main function,
we call RenderPass, which is a subroutine uniform that will call either recordDepth or
shadeWithShadow. For the first pass (shadow map generation), the subroutine function
recordDepth is executed. This function does nothing at all! This is because we only need to
write the depth to the depth buffer. The OpenGL fixed functionality will do this automatically
(assuming that gl_Position was set correctly by the vertex shader), so there is nothing for
the fragment shader to do.
During the second pass, the function shadeWithShadow is executed. We compute the ambient
component of the shading model and store the result in the variable ambient. We then
compute the diffuse and specular components and store that in the variable diffuseAndSpec.
The next step is the key to the shadow mapping algorithm. We use the built-in texture access
function textureProj to access the shadow map texture ShadowMap. Before using the texture
coordinate to access the texture, the textureProj function will divide the first three coordinates
of the texture coordinate by the fourth coordinate. Remember that this is exactly what is needed
to convert the homogeneous position (ShadowCoord) to a true Cartesian position.
After this perspective division, the textureProj function will use the result to access the
texture. As this texture's type is sampler2DShadow, it is treated as texture containing depth
values, and rather than returning a value from the texture, it returns the result of a comparison.
The first two coordinates of ShadowCoord are used to access a depth value within the texture.
That value is then compared against the value of the third component of ShadowCoord. When
GL_NEAREST is the interpolation mode (as it is in our case) the result will be 1.0 or 0.0. As we
set the comparison function to GL_LESS, this will return 1.0, if the value of the third component
of ShadowCoord is less than the value within the depth texture at the sampled location. This
result is then stored in the variable shadow. Finally, we assign a value to the output variable
FragColor. The result of the shadow map comparison (shadow) is multiplied by the diffuse
and specular components, and the result is added to the ambient component. If shadow is 0.0,
that means that the comparison failed, meaning that there is something between the fragment
and the light source. Therefore, the fragment is only shaded with ambient light. Otherwise,
shadow is 1.0, and the fragment is shaded with all three shading components.
When rendering the shadow map, note that we culled the front faces. This is to avoid the
"z-fighting" that can occur when front faces are included in the shadow map. Note that this
only works if our mesh is completely closed. If back faces are exposed, you may need to use
another technique (such as glPolygonOffset) to avoid this. I'll talk a bit more about this in
the next section.
主要有以下细节:
textureproj(除以w),
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR,
border)(设置border颜色防止超出裁切面默认为0的错误将其改为1(试想,如果该点在灯光阴影贴图左坐标外,如果贴图默认值为0,那么该点的Z一定大于0,也就是说大于贴图采样值,那不就意味着它被挡住了在阴影中吗));
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_MODE,
GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_FUNC,
GL_LESS);
渲染阴影深度图的subroutine为空
为防止Z fighting,渲染阴影图cull front face