确定级联阴影贴图分割范围

Posted

技术标签:

【中文标题】确定级联阴影贴图分割范围【英文标题】:Determining cascaded shadow map split ranges 【发布时间】:2014-04-06 13:08:15 【问题描述】:

我正在做级联阴影贴图,我相信我在进行拆分比较以选择正确的阴影贴图的方式方面存在问题。就目前而言,阴影贴图总体上有效,但在某些情况下,在某些角度上它不起作用。

目前光照着色阶段是这样的:

"#version 420                                                                                                                   

const float DEPTH_BIAS = 0.00005;                                                                                               

layout(std140) uniform UnifDirLight                                                                                                     
                                                                                                                               
    mat4 mVPMatrix[4];                                                                                                          
    mat4 mCamViewMatrix;    
    vec4 mSplitDistance;                                                                                                    
    vec4 mLightColor;                                                                                                           
    vec4 mLightDir;                                                                                                             
    vec4 mGamma;                                                                                                                
    vec2 mScreenSize;                                                                                                           
 UnifDirLightPass;                                                                                                             

layout (binding = 2) uniform sampler2D unifPositionTexture;                                                                     
layout (binding = 3) uniform sampler2D unifNormalTexture;                                                                       
layout (binding = 4) uniform sampler2D unifDiffuseTexture;                                                                      
layout (binding = 6) uniform sampler2DArrayShadow unifShadowTexture;                                                            

out vec4 fragColor;                                                                                                             

void main()                                                                                                                     
                                                                                                                               
    vec2 texcoord = gl_FragCoord.xy / UnifDirLightPass.mScreenSize;                                                             

    vec3 worldPos = texture(unifPositionTexture, texcoord).xyz;                                                                 
    vec3 normal   = normalize(texture(unifNormalTexture, texcoord).xyz);                                                        
    vec3 diffuse  = texture(unifDiffuseTexture, texcoord).xyz;                                                                  

    vec4 camPos = UnifDirLightPass.mCamViewMatrix * vec4(worldPos, 1.0);     // legit way of determining the split?                                            

    int index = 3;                                                                        
    if (camPos .z > UnifDirLightPass.mSplitDistance.x)                                                                           
       index = 0;                                                                                                              
    else if (camPos .z > UnifDirLightPass.mSplitDistance.y)                                                                      
       index = 1;                                                                                                            
    else if (camPos .z > UnifDirLightPass.mSplitDistance.z)                                                                      
       index = 2;                                                                                                              

    vec4 projCoords = UnifDirLightPass.mVPMatrix[index] * vec4(worldPos, 1.0);                                                  
    projCoords.w    = projCoords.z - DEPTH_BIAS;                                                                                
    projCoords.z    = float(index);                                                                                             
    float visibilty = texture(unifShadowTexture, projCoords);                                                                   

    float angleNormal = clamp(dot(normal, UnifDirLightPass.mLightDir.xyz), 0, 1);                                               

    fragColor = vec4(diffuse, 1.0) * visibilty * angleNormal * UnifDirLightPass.mLightColor;                                    

而“mSplitDistance”,每个分量是该分割的截锥体的中心远距离,乘以主摄像机视图矩阵

Vec4 camFarDistCenter;
CameraFrustrum cameraFrustrum = CalculateCameraFrustrum(nearDistArr[cascadeIndex], farDistArr[cascadeIndex], lighting.mCameraPosition, lighting.mCameraDirection, camFarDistCenter);

.....

camFarDistCenter = lighting.mCameraViewMatrix * camFarDistCenter;
splitDistances[cascadeIndex] = camFarDistCenter.z;

以下是我为每个分割创建相机截锥体的方法,如果感兴趣,我相信这是一个非常常见的算法:

CameraFrustrum CalculateCameraFrustrum(const float minDist, const float maxDist, const Vec3& cameraPosition, const Vec3& cameraDirection, Vec4& camFarZ)

    CameraFrustrum ret =  Vec4(-1.0f, -1.0f, 1.0f, 1.0f), Vec4(-1.0f, -1.0f, -1.0f, 1.0f), Vec4(-1.0f, 1.0f, 1.0f, 1.0f), Vec4(-1.0f, 1.0f, -1.0f, 1.0f),
                           Vec4(1.0f, -1.0f, 1.0f, 1.0f), Vec4(1.0f, -1.0f, -1.0f, 1.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f), Vec4(1.0f, 1.0f, -1.0f, 1.0f) ;

    const Vec3 forwardVec = glm::normalize(cameraDirection);
    const Vec3 rightVec   = glm::normalize(glm::cross(forwardVec, Vec3(0.0f, 0.0f, 1.0f)));
    const Vec3 upVec      = glm::normalize(glm::cross(rightVec, forwardVec));

    const Vec3 nearCenter = cameraPosition + forwardVec * minDist;
    const Vec3 farCenter  = cameraPosition + forwardVec * maxDist;

    camFarZ = Vec4(farCenter, 1.0);

    const float nearHeight = tan(glm::radians(70.0f) / 2.0f) * minDist;
    const float nearWidth = nearHeight * 1920.0f / 1080.0f;
    const float farHeight  = tan(glm::radians(70.0f) / 2.0f) * maxDist;
    const float farWidth = farHeight * 1920.0f / 1080.0f;

    ret[0] = Vec4(nearCenter - (upVec * nearHeight) - (rightVec * nearWidth), 1.0);
    ret[1] = Vec4(nearCenter + (upVec * nearHeight) - (rightVec * nearWidth), 1.0);
    ret[2] = Vec4(nearCenter + (upVec * nearHeight) + (rightVec * nearWidth), 1.0);
    ret[3] = Vec4(nearCenter - (upVec * nearHeight) + (rightVec * nearWidth), 1.0);

    ret[4] = Vec4(farCenter - upVec * farHeight - rightVec * farWidth, 1.0);
    ret[5] = Vec4(farCenter + upVec * farHeight - rightVec * farWidth, 1.0);
    ret[6] = Vec4(farCenter + upVec * farHeight + rightVec * farWidth, 1.0);
    ret[7] = Vec4(farCenter - upVec * farHeight + rightVec * farWidth, 1.0);

    return ret;

像我一样在相机空间中进行分割比较是否合理?这是一个潜在的问题吗?

【问题讨论】:

投反对票有什么理由吗?有没有遗漏的信息? 【参考方案1】:
    void CalculateCameraFrustrum(glm::mat4& projectionMatrix, 
                             glm::mat4  viewMatrix,       // viewMatrix = light POV
                             glm::vec3  camera            // camera = eye position + eye direction
                             float      zNear, 
                             float      zFar, 
                             glm::vec4  point[4])         // point[4] = shadow map boundaries
    glm::mat4 shadMvp =  projectionMatrix * (viewMatrix * glm::translate(camera));
    glm::vec3 transf;
    float     maxX = zNear, minX = zFar, 
              maxY = zNear, minY = zFar;

    int i = -1;
    while (++i < 4)
    
        transf = shadMvp * point[i];

        transf.x /= transf.w;
        transf.y /= transf.w;

        if(transf.x > maxX) 
            maxX = transf.x;
        if(transf.x < minX) 
            minX = transf.x;
        if(transf.y > maxY) 
            maxY = transf.y;
        if(transf.y < minY) 
            minY = transf.y;
    

    float scaleX  =  2.0f / (maxX - minX),
          scaleY  =  2.0f / (maxY - minY),
          offsetX = -0.5f * (maxX + minX) * scaleX,
          offsetY = -0.5f * (maxY + minY) * scaleY;

    shadMvp = glm::mat4(1); // Identity matrix

    shadMvp[0][0] = scaleX;
    shadMvp[1][1] = scaleY;
    shadMvp[0][3] = offsetX;
    shadMvp[1][3] = offsetY;

    projectionMatrix *= shadMvp;
 // No need to calculate view frustum splitting, 
  // only the boundaries of the shadow maps levels are needed (glm::ortho(...)).
  // :)

【讨论】:

以上是关于确定级联阴影贴图分割范围的主要内容,如果未能解决你的问题,请参考以下文章

级联阴影贴图意外行为

全向阴影贴图

✠OpenGL-8-阴影

提高点光源的阴影贴图性能?

一步步学OpenGL 23 -《阴影贴图1》

阴影贴图未正确投影