You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vulkan单方向光阴影计算异常 求助排查

Vulkan方向光阴影异常:模型出现分割线排查方案

核心问题定位

你遇到的模型半区颜色异常、出现分割线的问题,大概率是阴影空间坐标超出[-1,1]有效范围导致的——阴影图采样时,只有坐标落在[-1,1]内的像素才能正确映射到纹理,超出部分会触发边界采样(比如clamp到边缘),进而出现明显的分割效果。以下是针对性的排查和修复步骤:


1. 核对v_FragPosLightSpace的变换链

方向光的阴影空间变换必须严格遵循:LightSpaceMatrix = 正交投影矩阵 * 光照视角矩阵 * 模型矩阵,顶点着色器中正确的计算逻辑应该是:

// 顶点着色器中正确的阴影空间坐标计算
vec4 worldPos = modelMat * vec4(inPos, 1.0);
v_FragPosLightSpace = lightSpaceMatrix * worldPos;

注意:方向光必须用正交投影矩阵,不能用透视投影,否则会因近远平面导致深度畸变,进一步引发采样异常。

2. 可视化阴影空间坐标范围

在片元着色器中临时输出v_FragPosLightSpace的归一化结果,直接观察模型在阴影空间中的覆盖情况:

// 调试用片元着色器(替换原光照计算逻辑)
void main() {
    vec3 lightSpacePos = v_FragPosLightSpace.xyz / v_FragPosLightSpace.w;
    // 将[-1,1]范围映射为[0,1]的RGB颜色输出
    fragColor = vec4((lightSpacePos + 1.0) / 2.0, 1.0);
}

如果输出颜色出现明显的黑白分割,说明光照视角的视锥体只覆盖了模型的一半,需要调整正交投影的left/right/top/bottom参数,扩大视锥体范围以包裹整个模型。

3. 修正阴影图采样逻辑

  • 必须将阴影空间坐标转换为[0,1]的纹理UV:
    vec2 uv = (lightSpacePos.xy + 1.0) / 2.0;
    
  • 优先使用Vulkan的深度比较采样器(sampler2DShadow类型),避免手动深度比较的精度问题:
    float shadow = texture(shadowMap, vec3(uv, lightSpacePos.z));
    
  • 如果用普通sampler2D采样,必须添加**深度偏移(bias)**解决阴影 acne,同时严格做深度比较:
    float closestDepth = texture(shadowMap, uv).r;
    float currentDepth = lightSpacePos.z;
    // 根据场景调整bias值,比如0.005
    float shadow = currentDepth - 0.005 > closestDepth ? 1.0 : 0.0;
    

4. 检查RenderPass与深度附件配置

  • 阴影图渲染的RenderPass必须绑定深度纹理附件,格式要匹配(比如VK_FORMAT_D32_SFLOAT)。
  • 阴影管线必须开启深度测试和深度写入:
    VkPipelineDepthStencilStateCreateInfo depthState{};
    depthState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
    depthState.depthTestEnable = VK_TRUE;
    depthState.depthWriteEnable = VK_TRUE;
    depthState.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
    
  • 渲染阴影图时,关闭颜色附件写入,只保留深度写入,避免资源浪费和潜在的状态冲突。

5. 处理Vulkan纹理坐标的Y轴翻转

Vulkan纹理原点在左上角,而光照视角的投影通常以左下角为原点,因此需要做Y轴翻转:

  • 方案一:采样时翻转UV的Y分量
    vec2 uv = vec2((lightSpacePos.x + 1.0)/2.0, 1.0 - (lightSpacePos.y + 1.0)/2.0);
    
  • 方案二:构建正交投影矩阵时直接翻转Y轴
    glm::mat4 proj = glm::ortho(left, right, bottom, top, nearPlane, farPlane);
    proj = glm::scale(proj, glm::vec3(1.0, -1.0, 1.0));
    

内容的提问来源于stack exchange,提问作者Onuralp Şahin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 19:03:39