带阴影的聚光灯变成方形

Spotlight with shadows becomes square-like

几天来,我一直在关注 OGLDev (part 1, part 2) 的阴影贴图教程,我几乎完成了它的实施。

我的问题是,当我启用阴影贴图时,出现阴影的照明区域显示为 "strip",我不确定教程中是否有意这样做。

也许图片可以帮助解释我的问题。

但是阴影是有用的:

我的聚光灯顶点着色器:

#version 330

in vec2 textureCoordinate;
in vec4 vertex;

uniform mat4 mMatrix;
uniform mat4 pMatrix;
uniform mat4 vMatrix;
uniform mat4 lightV;
uniform mat4 lightP;
uniform vec3 normal;
uniform vec3 eye;

out vec2 TexCoord0;
out vec4 LightSpacePos;
out vec3 Normal0;
out vec3 WorldPos0;
out vec3 EyePos;

void main()
{   
    EyePos = eye;
    TexCoord0 = textureCoordinate;
    WorldPos0 = (mMatrix * vertex).xyz;
    Normal0 = (mMatrix * vec4(normal, 0.0)).xyz;   

    mat4 lightMVP = lightP * inverse(lightV) * mMatrix;
    LightSpacePos =  lightMVP * vertex; 

    mat4 worldMVP = pMatrix * vMatrix * mMatrix;
    gl_Position = worldMVP * vertex;
}

和片段着色器:

#version 330

struct BaseLight
{
    vec4 color;
    float intensity;
};

struct Attenuation
{
    float constant;
    float linear;
    float exponent;
};

struct PointLight
{
    BaseLight base;
    Attenuation atten;
    vec3 position;
    float range;
};

struct SpotLight
{
    struct PointLight base;
    vec3 direction;
    float cutoff;
};

in vec2 TexCoord0;
in vec3 WorldPos0;
in vec3 EyePos;
in vec4 LightSpacePos;
out vec4 fragColor;        
uniform sampler2D sampler;                        
uniform sampler2D shadowMap;                             

in vec3 Normal0;
uniform float specularIntensity;
uniform float specularPower;
uniform SpotLight spotLight;

float CalcShadowFactor(vec4 LightSpacePos)                                                  
{                                                                                           
    vec3 ProjCoords = LightSpacePos.xyz / LightSpacePos.w;                                  
    vec2 UVCoords;                                                                          
    UVCoords.x = 0.5 * ProjCoords.x + 0.5;                                                  
    UVCoords.y = 0.5 * ProjCoords.y + 0.5;                                                  
    float z = 0.5 * ProjCoords.z + 0.5;                                                     
    float Depth = texture(shadowMap, UVCoords).x;                                          
    if (Depth < z + 0.00001)                                                                 
        return 0.5;                                                                         
    else                                                                                    
        return 1.0;                                                                         
} 

vec4 CalcLightInternal(BaseLight Light, vec3 LightDirection, vec3 Normal, float ShadowFactor)                                                  
{                                                                                           
    vec4 AmbientColor = Light.color * Light.intensity;                   
    float DiffuseFactor = dot(Normal, -LightDirection);                                     

    vec4 DiffuseColor  = vec4(0, 0, 0, 0);                                                  
    vec4 SpecularColor = vec4(0, 0, 0, 0);                                                  

    if (DiffuseFactor > 0) {                                                                
        DiffuseColor = Light.color * Light.intensity * DiffuseFactor;    

        vec3 VertexToEye = normalize(EyePos - WorldPos0);                             
        vec3 LightReflect = normalize(reflect(LightDirection, Normal));                     
        float SpecularFactor = dot(VertexToEye, LightReflect);                              
        SpecularFactor = pow(SpecularFactor, specularPower);                               
        if (SpecularFactor > 0) {                                                           
            SpecularColor = Light.color * specularIntensity * SpecularFactor;                         
        }                                                                                   
    }                                                                                       

    return (AmbientColor + ShadowFactor * (DiffuseColor + SpecularColor));                  
}   

vec4 CalcPointLight(PointLight l, vec3 Normal, vec4 LightSpacePos)                   
{                                                                                           
    vec3 LightDirection = WorldPos0 - l.position;                                           
    float Distance = length(LightDirection);                                                
    LightDirection = normalize(LightDirection);                                             
    float ShadowFactor = CalcShadowFactor(LightSpacePos);                                   

    vec4 Color = CalcLightInternal(l.base, LightDirection, Normal, ShadowFactor);           
    float Attenuation =  l.atten.constant +                                                 
                         l.atten.linear * Distance +                                        
                         l.atten.exponent * Distance * Distance;                                 

    return Color / Attenuation;                                                             
}   

vec4 CalcSpotLight(SpotLight l, vec3 Normal, vec4 LightSpacePos)                     
{                                                                                           
    vec3 LightToPixel = normalize(WorldPos0 - l.base.position);                             
    float SpotFactor = dot(LightToPixel, l.direction);                                      

    if (SpotFactor > l.cutoff) {                                                            
        vec4 Color = CalcPointLight(l.base, Normal, LightSpacePos);                         
        return Color * (1.0 - (1.0 - SpotFactor) * 1.0/(1.0 - l.cutoff));                   
    }                                                                                       
    else {                                                                                  
        return vec4(0,0,0,0);                                                               
    }                                                                                       
}

void main(void)
{       
    fragColor = texture2D(sampler, TexCoord0.xy) * CalcSpotLight(spotLight, normalize(Normal0), LightSpacePos);
}

这是故意的。由于您的阴影贴图覆盖了 space 中的一个类似金字塔的区域,您的聚光灯的圆锥体可能会被它遮挡。发生这种情况是因为渲染阴影摄像机视野之外的东西时,它会被视为未发光。因此阴影相机的视图金字塔将可见。


src

要解决此问题,您有 2 个选项

1: 使阴影贴图相机的 fov 更大,以便阴影相机的金字塔变得比聚光灯的锥体更宽

2: 更改计算界外区域阴影的方式。当前,当您对阴影贴图进行采样时,如果它在阴影纹理之外,您将在那里应用阴影。如果你改变这个,如果阴影纹理之外的东西你认为它变小了,这个问题就会消失。

编辑: 我推荐第二种选择。解决方案可能是: 将以下行插入 FragmentShader::CalcShadowFactor(),就在 float Depth = ... 部分

之前
if (UVCoords.x < 0.0 || UVCoords.x > 1.0 || UVCoords.y < 0.0 || UVCoords.y > 1.0) return 1.0;

注: 没有通用的方法可以说哪个更好。在涉及阳光的地形环境中,您可能希望将越界区域视为 litten。但是,例如,当您使用用户手中的手电筒时,您必须将越界区域视为 unlitten.