ShadowMap

Posted Tekkaman

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ShadowMap相关的知识,希望对你有一定的参考价值。

ShadowMap

  一个物体之所以会处在阴影当中,是由于在它和光源之间存在着遮蔽物,或者说遮蔽物离光源的距离比物体要近,这就是 shadow mapping 算法的基本原理。

  Pass1: 以光源为视点,或者说在光源坐标系下面对整个场景进行渲染,目的是要得到一副所有物体相对于光源的 depth map (也就是我们所说的shadow map),也就是这副图像中每个象素的值代表着场景里面离光源最近的 fragment 的深度值。由于这个pass中我们感兴趣的只是象素的深度值,所以可以把所有的光照计算关掉,打开 z-test 和 z-write 的 render state 。

  Pass2: 将视点恢复到原来的正常位置,渲染整个场景,对每个象素计算它和光源的距离,然后将这个值和 depth map中相应的值比较,以确定这个象素点是否处在阴影当中。然后根据比较的结果,对 shadowed fragment 和 lighted fragment 分别进行不同的光照计算,这样就可以得到阴影的效果了。 

  从上面的分析可以看出来,depth map的渲染只和光源的位置以及场景中物体的位置有关,无论视点怎么运动,只要光源和物体的相互位置关系不变,shadow map就可以被重复使用,因此对于没有动态光源的场景,shadow mapping 是很明智的一种选择。

 

  Shadows add a great deal of realism to a lighted scene and make it easier for a viewer to observe spatial relationships between objects.

  

    

    

    

  Vetex Shader:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

out VS_OUT {
    vec3 FragPos;
    vec3 Normal;
    vec2 TexCoords;
    vec4 FragPosLightSpace;
} vs_out;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform mat4 lightSpaceMatrix;

void main()
{    
    vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
    vs_out.Normal = transpose(inverse(mat3(model))) * aNormal;
    vs_out.TexCoords = aTexCoords;
    vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0);
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
View Code

  Pixel Shader中的深度比较函数:

float ShadowCalculation(vec4 fragPosLightSpace)
{
    // perform perspective divide
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    // transform to [0,1] range
    projCoords = projCoords * 0.5 + 0.5;
    // get closest depth value from light\'s perspective (using [0,1] range fragPosLight as coords)
    float closestDepth = texture(shadowMap, projCoords.xy).r; 
    // get depth of current fragment from light\'s perspective
    float currentDepth = projCoords.z;
    // check whether current frag pos is in shadow
    float shadow = currentDepth > closestDepth  ? 1.0 : 0.0;

    return shadow;
}  
View Code

     

Shadow acne

  上面的渲染结果中会有黑色的条纹。

    

 

  Because the shadow map is limited by resolution, multiple fragments can sample the same value from the depth map when they\'re relatively far away from the light source. The image shows the floor where each tilted panel represents a single texel of the depth map. As you can see, several fragments sample the same depth sample.

    

    

Peter panning

    

  This shadow artifact is called peter panning since objects seem slightly detached from their shadows. We can use a little trick to solve most of the peter panning issue by using front face culling when rendering the depth map.

Over sampling

    问题:depth-map以外的部分都黑了。

    

    深度大于1.0,直接无shadow即可。

    

    改进后的效果。

    

 【PCF

    生成的阴影有锯齿。

    

  solution to these jagged edges is called PCF or percentage-closer filtering which is a term that hosts many different filtering functions that produce softer shadows, making them appear less blocky or hard. The idea is to sample more than once from the depth map, each time with slightly different texture coordinates. For each individual sample we check whether it is in shadow or not. All the sub-results are then combined and averaged and we get a nice soft looking shadow.

  

参考:https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping

 

以上是关于ShadowMap的主要内容,如果未能解决你的问题,请参考以下文章

Shadowmap 适用于正射投影,但不适用于透视投影

引擎设计跟踪 ShadowMap 细节和分析

ShadowMap

OpenGL ES 学习教程(十六) ShadowMap的理解

《CSM and PCF》

Unity实时阴影技术(shadowMap)