Math.sin() .cos() 的成本 vs 古怪的自己的实现

Jon*_*ter 1 javascript performance trigonometry game-engine

所以大约一个小时前,我经历了一个美好而尴尬的时刻,在很长一段时间后点击了一些东西。推动 30 我终于得到了窦和余弦。在一个只能被描述为基本上重新实现轮子的插曲之后。


为了让 WASD 控件在两个轴上移动,考虑到玩家的视线方向,我决定使用一个简单的开关:

switch (key) {
case 'up':
    movement.z -= 1 * modulation.z;
    movement.x += 1 * modulation.x;
    break;
case 'down':
    movement.z += 1 * modulation.z;
    movement.x -= 1 * modulation.x;
    break;
case 'left':
    movement.x -= 1 * modulation.z;
    movement.z -= 1 * modulation.x;
    break;
case 'right':
    movement.x += 1 * modulation.z;
    movement.z += 1 * modulation.x;
    break;

...
}
Run Code Online (Sandbox Code Playgroud)

...其中调制.z 和.x 将基于玩家面对的方向。调制值需要在 -1 到 +1 的范围内。(这应该是我的第一个线索,是的!)所以我帮自己拿了一些可靠的笔和纸,并且对我 9 年级的教育一无所知,我想出了:

function setModulation(rotation) {
    var rotationTemp = 0;
    var modulation = {};

    rotationTemp = rotation;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.z = 1 - rotationTemp * 2;

    rotationTemp = rotation + 90;
    if (rotationTemp > 180) {
        rotationTemp = 360 - rotationTemp;
    }
    rotationTemp /= 180;

    modulation.x = 1 - rotationTemp * 2;
    modulation.x *= -1;

    return modulation;
};
Run Code Online (Sandbox Code Playgroud)

在此期间学习和/或记住了一点,在重新审视这段代码时,我突然明白了:这是 cos 和 sin 的工作。亲爱的。

function setModulationMath(rotation) {
    var modulation = {};

    modulation.z = Math.cos(rotation * Math.PI / 180);
    modulation.x = Math.sin(rotation * Math.PI / 180);

    return modulation;
};
Run Code Online (Sandbox Code Playgroud)

到目前为止很开心!使用数学数学是什么!但后来我检查了性能。我自己的“实现”在 FF、Chrome 和 IE 中要快得多。使用 1.000.000 次迭代,它在我本地设置上的增益介于 35% 到 45% 之间。

任何人都知道为什么?它只是 Math.cos() / .sin() 吗?可衡量的增长超出了人们的预期,不是吗?任何有经验的人愿意分享她*他的见解?

switch (key) {
case 'up':
    movement.z -= 1 * modulation.z;
    movement.x += 1 * modulation.x;
    break;
case 'down':
    movement.z += 1 * modulation.z;
    movement.x -= 1 * modulation.x;
    break;
case 'left':
    movement.x -= 1 * modulation.z;
    movement.z -= 1 * modulation.x;
    break;
case 'right':
    movement.x += 1 * modulation.z;
    movement.z += 1 * modulation.x;
    break;

...
}
Run Code Online (Sandbox Code Playgroud)

Aur*_*ílý 5

您的功能很可能会更快。然而,这种比较没有任何意义,因为这些函数会产生完全不同的结果。让我们来看看错误的基准:

var err_z = 0;
var err_x = 0;
for (var i = 0; i < 360; ++i){
    var mm = setModulationMath(i);
    var m = setModulation(i);
    err_z += Math.abs(mm.z - m.z);
    err_x += Math.abs(mm.x - m.x);
}
Run Code Online (Sandbox Code Playgroud)

和 …

err_z == 49.17730025861921
err_x == 113.58865012930961
Run Code Online (Sandbox Code Playgroud)

这很糟糕。如果两者完全相似,您会期望值接近 0。

虽然我的第一个假设是我必须以某种方式伪造我的方式:它完美地工作。两个函数给出相同的调制值。这就是说:我很高兴被告知这是一个 hack 只因为 [x]... :)

可以证明,z 轴上的平均误差为 0.137,x 轴上的平均误差为 0.316。这对您来说可能已经足够了,但肯定存在差异更大的值。您可能只检查正交方向吗?在这种情况下,您需要的是switch,而不是三角函数。

编辑:哦,这是 z 部分的图表。波浪线是余弦,直线是您的近似值。

图表

但我离题了。

(尝试)自己实现三角函数、数学函数等不一定是坏事。这真的取决于应用程序的具体情况,有时你会发现你不需要这样的精度,或者你的角度将仅表示为整数度数……在这些情况下,使用更简单的近似值或查找表可能会有所帮助。

事实上,Javascript(以及许多其他标准库)中的正弦和余弦经过很好的优化和非常精确。