This is mainly about directional ligt. we can use depth map to find out which fragment should be shadow.
The normal way is like follows:
- Use kowledge about framebuffer to create a depth texture(in light perspective).
When depth testing is enabled, OpenGL tests the depth value of a fragment against the content of the depth buffer. OpenGL performs a depth test and if this test passes, the fragment is rendered and the depth buffer is updated with the new depth value(In this way the depth buffer always contains the closest depth). If the depth test fails, the fragment is discarded. - In vertex shader, transform vertex into light perspective. and rasterizer will automatically interpolate and have this light perspective postion for each fragment in fragment shader.(rasterizer 会对每个fragment进行插值,然后找到他们对应的在light perspective下的位置。用英语解释起来有点别扭)
- Use depth buffer texture's data to determine fragment that needs shadow effet(chect whether this fragment is the closest depth or not). And update those fragments in fragmetn shader.
Optimization
- Shadow acne
Use bias in shadow calculation
float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005);
float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
2.Peter panning
use face culling
glCullFace(GL_FRONT);
RenderSceneToDepthMap();
glCullFace(GL_BACK);
3.Over sampling
some position not in the light perspective will be shadow. but it should lit in reality. use following way to lit those position
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
float ShadowCalculation(vec4 fragPosLightSpace)
{
[...]
if(projCoords.z > 1.0)
shadow = 0.0;
return shadow;
}
4.PCF(percentage-closer filtering)
jagged blocky edges(可以理解为shadow锯齿).解决方法就是多次采样,取平均值。下面的只是一个例子
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
for(int x = -1; x <= 1; ++x)
{
for(int y = -1; y <= 1; ++y)
{
float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
}
}
shadow /= 9.0;
参考
https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping