【问题标题】:DirectX texture sampling function SampleCmpLevelZero returns blank intermittently?DirectX 纹理采样函数 SampleCmpLevelZero 间歇性返回空白?
【发布时间】:2025-11-22 19:10:02
【问题描述】:

我正在尝试编写像素着色器 - 我想使用 Texture.SampleCmpLevelZero,因为它可用于没有 Texture.Sample 的循环构造中。

我已经构建了一个纹理,并且可以使用 Texture.Sample 对其进行精细采样,但切换到 SampleCmpLevelZero 对前几帧有效,然后变为空白,然后很少但间歇性地正确渲染。

我的场景是静态的(还有纹理数据) - 我正在渲染一个四边形,并且没有任何类型的摄像机移动 - 我可以通过更改 PS 着色器功能中的单行来可靠地重现这一点。

有人看过吗?

谢谢

SamplerState sampPointClamp
{
    Filter = MIN_MAG_MIP_POINT;
    AddressU = Clamp;
    AddressV = Clamp;
};

SamplerComparisonState ShadowSampler
{
   // sampler state
   Filter = MIN_MAG_MIP_POINT;
   AddressU = Clamp;
   AddressV = Clamp;

   // sampler comparison state
   ComparisonFunc = LESS;
   //ComparisonFilter = COMPARISON_MIN_MAG_MIP_POINT;
};

texture2D tex;

//on the fly full screen quad
PS_IN VS(uint id : SV_VertexID)
{
    PS_IN ret;
    ret.uv = float2( id & 1, (id & 2) >> 1 );
    ret.pos = float4( ret.uv * float2( 2.0f, -2.0f ) + float2( -1.0f, 1.0f), 0.0f, 1.0f );
    return ret;
}

float4 PS( PS_IN input ) : SV_Target
{
    //return float4(tex.SampleCmpLevelZero(ShadowSampler, input.uv, 0), 0, 0, 1); // Does not work properly
    return float4(tex.Sample(sampPointClamp, input.uv).x, 0, 0, 1); // Works fine
}

【问题讨论】:

    标签: directx hlsl


    【解决方案1】:

    样本应该可以在循环中正常工作:

    float4 PSColUV(COLUV_PIXEL input) : SV_Target
    {
        float4 output;
        for (int i = 0; i < 4; i++)
        {
            float f = float(i) / 256.0;
            float2 uv = input.UV + float2(i,i);
    
            output += g_txDiffuse.Sample(g_samLinear, uv);
        }
        return input.Col * output/4.0;
    }
    

    产生:

          ps_4_0
          dcl_sampler s0, mode_default
          dcl_resource_texture2d (float,float,float,float) t0
          dcl_input_ps linear v1.xyzw
          dcl_input_ps linear v2.xy
          dcl_output o0.xyzw
          dcl_temps 3
       0: mov r0.xyzw, l(0,0,0,0)
       1: mov r1.x, l(0)
       2: loop
       3:   ige r1.y, r1.x, l(4)
       4:   breakc_nz r1.y
       5:   itof r1.y, r1.x
       6:   add r1.yz, r1.yyyy, v2.xxyx
       7:   sample r2.xyzw, r1.yzyy, t0.xyzw, s0
       8:   add r0.xyzw, r0.xyzw, r2.xyzw
       9:   iadd r1.x, r1.x, l(1)
      10: endloop
      11: mul r0.xyzw, r0.xyzw, v1.xyzw
      12: mul o0.xyzw, r0.xyzw, l(0.250000, 0.250000, 0.250000, 0.250000)
      13: ret
    

    另外,您确实意识到您正在执行 PCF 查找而不是普通纹理样本,并且这不会为您提供纹理中的数据,而是会比较所有纹理子样本(例如 8双线性)与您的参考值(0),计算0或1,具体取决于它们是否小于或大于您的参考值,将这些布尔值过滤为0到1之间的数字

    回复评论:

    谢谢 - 我认为 Sample 不能在具有可变长度或 长度在编译时未知(?)。我得到的错误是“错误 X4014:在循环错误中不能有发散梯度操作: 编译表达式时出错”。关于你的另一点-我愿意 想要一个精确的样本 - 我以为这就是我得到的 - 我只是 尝试使用纹理进行一些程序纹理生成 缓冲区作为值表,让我计算真正的纹素值 基于 (u,v) 等.. – AnonDev

    http://msdn.microsoft.com/en-gb/library/windows/desktop/bb219848%28v=vs.85%29.aspx 《逐像素流控制与屏幕渐变的交互》

    请记住,像素是在(至少)2x2 块中执行的。您不能拥有会导致某些像素采样而其他像素不会采样的控制流,也不能在控制流中进行会导致采样操作获得不同梯度的计算。

    (嗯,你可以,但你需要使用SampleGrad。但是!在这种情况下,这不是你想要的。)

    您说的是“精确”样本。你的意思是你的资源只有一个mip map,你想在不过滤的情况下获取资源中的每个纹素? (即你正在做一个点过滤器?)。鉴于您对纹理作为值表的解释,那么我不明白为什么您需要将纹理作为 mipchain,并且只有顶层包含有用的信息。在这种情况下,您可以使用 LOD 为 0 的SampleLevel()。这意味着导数中不会有分歧,因为示例操作没有使用导数!

    这与SampleCmpLevelZero 有效但SampleCmp 无效的原因相同 :) 如果您是点采样,那么另一个好的候选者将是 Load(),因为您给它精确的纹素位置,您甚至可以使用它缓冲区。因此,如果您的纹理查找位置是基于像素 (X,Y) 的,那么您可以将这些直接传递给 Load(在考虑了半纹素偏移之后..)。

    无论如何,您真的不想使用 SampleCmp/LevelZero。它做错了你所追求的事情!它用于阴影贴图等。改用 LOD 为 0 的 SampleLevel

    【讨论】:

    • 谢谢 - 我认为 Sample 不能处于可变长度或编译时未知长度的循环中(?)。我得到的错误是“错误 X4014:在循环中不能有发散梯度操作错误:编译表达式时出错”。在您的另一点上-我确实想要一个精确的样本-我认为这就是我得到的-我只是尝试使用纹理缓冲区作为值表进行一些程序纹理生成,以让我计算真实的纹素值基于 (u,v) 等。
    • 我在回答中回复了,因为评论框太小了;)
    • 非常感谢 - 这开辟了比我想象的更多的可能性!
    【解决方案2】:

    问题是:

    SamplerComparisonState ShadowSampler
    {
       // sampler state
       Filter = MIN_MAG_MIP_POINT;
       AddressU = Clamp;
       AddressV = Clamp;
    
       // sampler comparison state
       ComparisonFunc = LESS;
       //ComparisonFilter = COMPARISON_MIN_MAG_MIP_POINT;
    };
    

    看起来有一段时间,ComparisonFilter 作为属性存在(正如它在文档中出现的那样),例如构建 3/5/2013 的 http://msdn.microsoft.com/en-gb/library/windows/desktop/bb509644(v=vs.85).aspx 但如果存在则不会编译。

    我通过将 Filter 属性更改为具有值 COMPARISON_MIN_MAG_MIP_POINT 来修复上述行为 - 那时一切正常

    【讨论】: