HLSL 中的高效条件上限和下限

Efficient conditional ceiling and floor in HLSL

我已经尝试解决这个问题很长时间了,但我无法完全正确。我想要做的是根据不同的 float.

float 舍入到最接近的整数

我基本上需要一个应该像这样工作的函数:

float roundParam(float val, float dir)
{
    if (dir >= 0)
        return ceil(val);
    else
        return floor(val);
}

这当然是非常低效的,因为每个向量分量都需要一个分支。我想通了,但它打破了整数:

float roundParam(float val, float dir)
{
    return round(val + 0.5 * sign(dir));
}

也许使用由 dir 选择的指针数组?

下面是 C。不清楚这种方法在 hsl, shader

中的可用性如何
float roundParam(float val, float dir) {
  static float (*f[2])(float) = {ceilf, floorf};
  return f[!!signbit(dir)](val);
}

在 C 中,您可以使用以下可向量化的函数。也许您可以在 hlsl 中使用相同的想法。此解决方案仅适用于您不关心 dir.

+0-0(带符号零)之间的区别
float roundParam_v2(float val, float dir)
{   
    union fl_i32{float f; int i;} x, y, d;
    x.f = val;
    d.f = dir;
    d.i = d.i & 0x80000000;   /* extract the sign bit              */
    x.i = x.i ^ d.i;          /* multiply x 1.0f if signbit is set */
    y.f = ceilf(x.f);         /* note that floor(z) = - ceil( -z)  */
    y.i = y.i ^ d.i;          /* multiply x 1.0f if signbit is set */
    return y.f;
}

感谢@wim 和他对 floor(x) = -ceil(-x)ceil(x) = -floor(-x) 的观察,我能够创建这个解决问题的函数:

float3 roundParam(float3 val, float3 dir)
{
    float3 dirSign = sign(dir);
    return dirSign * floor(dirSign * val) + dirSign;
}

怎么样:

float roundParam(float val, float dir)
{
return ceil(val)*(float)(dir>=0)+floor(val)*(float)(dir<0);
}

它可能会进一步优化,但编译器可能已经进行了优化。

顺便说一句,如果您将 [flatten] 标记添加到 if 条件,它可能已经被编译器优化了。而这么简单的分支,不管你打不打标签,它很可能已经被编译器压扁了。

检查编译后的代码并查看分支是否已被删除会很有趣。我目前处于afk状态,所以无法查看...