优化球体折叠函数(GLSL)

Optimizing sphere fold function (GLSL)

有一个球体折叠函数,它根据与原点的距离变换 space。有两个常量参数:fR2mR2。该函数如下所示:

const float fR2 = 1.0;
const float mR2 = 0.25;

vec3 s_fold(in vec3 v) {

    float mag = dot(v, v);

    if (mag < mR2)
    {
       v = v * fR2 / mR2;
    }
    else if (mag < fR2)
    {
       v = v * fR2 / mag;
    }

    return v;

}

可以用,但是因为if-else分支,所以运行很慢。如果我使用 step 函数,可以去掉分支:

float a = step(mR2 , mag);
float b = step(fR2 , mag);

float sc = dot( vec3(
    1.0-a,
    a*(1.0-b),
    b
), vec3(
    fR2 / mR2,
    fR2 / mag,
    1.0)
);

return sc*v;

现在速度快了一点,但我想进一步优化它。我找到了 one-line solution,但它给了我不同的结果:

return v*clamp(max(mR2/mag,mR2),0.0,fR2);

我不清楚,怎么可能用一个 clamp.

来计算

您的 if-else 分支是类似于 res= v * F 的函数,其中 F 是三个可能值之一:常量 (fR2/mR2)、(双曲线)变量 (fR2/mag),又是一个常数 (1.0)。

clamp(x, minVal, maxVal) 执行相同的逻辑 (const-var-const)。所以你可以这样写:

res = v * clamp(fR2/mag, 1.0, fR2/mR2); //with fR2 >= mR2

换个方式写吧:

res = v * fR2 * clamp(1.0/mag, 1.0/fR2, 1.0/mR2);

我们可以避免两次除法因为1/mR2 <= 1/fR2意味着fR2 >= mR2

res = v * fR2 / clamp(mag, mR2, fR2);

最终代码为

const float fR2 = 1.0;
const float mR2 = 0.25;

vec3 s_fold(in vec3 v) {
    return v * fR2 / clamp(dot(v, v), mR2, fR2);
}


mag=0

时,您的解决方案和发布的 "liner" 都可能被零除