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

点光源PCF阴影出现圆形伪影问题排查

点光源阴影PCF实现中的伪影与质量问题

问题背景

我正在给DirectX 12渲染器添加点光源阴影,基础阴影渲染正常:
基础阴影正常效果

基础采样逻辑

辅助方法SamplePointLightShadowMaps:

// @See: https://en.wikipedia.org/wiki/Cube_mapping
float SamplePointLightShadowMaps(Texture2DArray<float> shadowMaps, SamplerState samplerSate, uint lightLinearIdx, float lightFarPlane, float3 toP)
{ 
    const float3 toPNormalized = normalize(toP);
    
    const float x = toPNormalized.x;
    const float y = toPNormalized.y;
    const float z = toPNormalized.z;

    const float absX = abs(x);
    const float absY = abs(y);
    const float absZ = abs(z);

    const int isXPositive = x > 0 ? 1 : 0;
    const int isYPositive = y > 0 ? 1 : 0;
    const int isZPositive = z > 0 ? 1 : 0;

    float maxAxis, uc, vc;
    int faceIdx;
    
    // POSITIVE X
    if (isXPositive && absX >= absY && absX >= absZ)
    {
        // u (0 to 1) goes from +z to -z
        // v (0 to 1) goes from -y to +y
        maxAxis = absX;
        uc = -z;
        vc = y;
        faceIdx = 0u;
    }
    // NEGATIVE X
    if (!isXPositive && absX >= absY && absX >= absZ)
    {
        // u (0 to 1) goes from -z to +z
        // v (0 to 1) goes from -y to +y
        maxAxis = absX;
        uc = z;
        vc = y;
        faceIdx = 1u;
    }
    // POSITIVE Y
    if (isYPositive && absY >= absX && absY >= absZ)
    {
        // u (0 to 1) goes from -x to +x
        // v (0 to 1) goes from +z to -z
        maxAxis = absY;
        uc = x;
        vc = -z;
        faceIdx = 2u;
    }
    // NEGATIVE Y
    if (!isYPositive && absY >= absX && absY >= absZ)
    {
        // u (0 to 1) goes from -x to +x
        // v (0 to 1) goes from -z to +z
        maxAxis = absY;
        uc = x;
        vc = z;
        faceIdx = 3u;
    }
    // POSITIVE Z
    if (isZPositive && absZ >= absX && absZ >= absY)
    {
        // u (0 to 1) goes from -x to +x
        // v (0 to 1) goes from -y to +y
        maxAxis = absZ;
        uc = x;
        vc = y;
        faceIdx = 4u;
    }
    // NEGATIVE Z
    if (!isZPositive && absZ >= absX && absZ >= absY)
    {
        // u (0 to 1) goes from +x to -x
        // v (0 to 1) goes from -y to +y
        maxAxis = absZ;
        uc = -x;
        vc = y;
        faceIdx = 5u;
    }

    // Convert range from -1 to 1 to 0 to 1
    const float2 uv = float2(0.5f * (uc / maxAxis + 1.0f),
            0.5f * (vc / maxAxis + 1.0f));
    
    return shadowMaps.SampleLevel(samplerSate, float3(uv, lightLinearIdx * NB_SHADOW_MAP_PER_LIGHT + faceIdx), 0).x * lightFarPlane;
}

采样方法:

float PointLightShadowMapping(Texture2DArray<float> shadowMaps, LightStruct light, float3 P, float2 pixelCoord, SamplerState samplerSate)
{
    const float3 toP = P - light.positionOrDirection.xyz;
    const float currentDepth = length(toP);
    
    const float bias = 0.05f;
    const float closestDepth = SamplePointLightShadowMaps(shadowMaps, samplerSate, light.linearIdx, light.farZ, toP);
    const float shadow = currentDepth - bias > closestDepth ? 0.0f : 1.0f;
    
    return shadow;
}

此时得到预期效果:
基础阴影预期效果

PCF修改后的问题

添加PCF后,修改的采样逻辑如下:

static const uint pcf3DSampleCount = 20u;

static float3 pcf3DSamples[pcf3DSampleCount] =
{
    float3(1, 1, 1), float3(1, -1, 1), float3(-1, -1, 1), float3(-1, 1, 1),
    float3( 1,  1, -1), float3( 1, -1, -1), float3(-1, -1, -1), float3(-1,  1, -1),
    float3( 1,  1,  0), float3( 1, -1,  0), float3(-1, -1,  0), float3(-1,  1,  0),
    float3( 1,  0,  1), float3(-1,  0,  1), float3( 1,  0, -1), float3(-1,  0, -1),
    float3( 0,  1,  1), float3( 0, -1,  1), float3( 0, -1, -1), float3( 0,  1, -1)
};

float PointLightShadowMapping(Texture2DArray<float> shadowMaps, LightStruct light, float3 P, float2 pixelCoord, SamplerState samplerSate)
{
    const float3 toP = P - light.positionOrDirection.xyz;
    const float currentDepth = length(toP);
    
    const float diskRadius = 0.05f;
    const float bias = 0.05f;;
    
    float shadow = 0.0f;
    for(uint i = 0u; i < pcf3DSampleCount; ++i)
    {
        const float3 sampleToP = toP + pcf3DSamples[i] * diskRadius;
        const float closestDepth = SamplePointLightShadowMaps(shadowMaps, samplerSate, light.linearIdx, light.farZ, sampleToP);
        
        shadow += currentDepth - bias > closestDepth ? 0.0f : 1.0f;
    }
    
    return shadow / pcf3DSampleCount;
}

出现的问题

  1. 墙面出现圆形伪影:
    墙面圆形伪影
  2. PCF质量较差:
    PCF质量差效果

请问这是什么原因?感谢解答!


内容的提问来源于stack exchange,提问作者TheChamp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.02 02:02:27