【问题标题】:Ray-plane intersection: inaccurate results - rounding errors?射线平面相交:结果不准确 - 舍入误差?
【发布时间】:2013-02-27 10:56:30
【问题描述】:

我使用 C++ 和 OpenGL 设置了一个简单的 3d 第一人称演示,它似乎运行良好。我的目标是:当用户将相机指向一个平面并单击鼠标左键时,我想绘制一条从玩家位置指向相机所面对方向的射线与该平面的交点。

所以,我从两个向量开始,Vector positionVector rotation,其中 Vector 是一个非常标准的三维向量类:

class Vector
{
  public:
    GLfloat x, y, z;

    Vector() {};

    Vector(GLfloat x, GLfloat y, GLfloat z)
    {
      this->x = x;
      this->y = y;
      this->z = z;
    }

    GLfloat dot(const Vector &vector) const
    {
      return x * vector.x + y * vector.y + z * vector.z;
    }

    ... etc ...

还有Plane p,Plane 是一个简单的结构,用于存储平面的法线和 d。我直接从 Christer Ericson 的“实时碰撞检测”一书中复制了这个结构:

struct Plane 
{
  Vector n; // Plane normal. Points x on the plane satisfy Dot(n,x) = d
  float d; // d = dot(n,p) for a given point p on the plane
};

首先,我将position 作为射线的起点,我称之为a。我使用那个点和rotation 来找到射线的终点b。然后我使用一种算法从同一本书中找到射线和平面的交点。实际上我自己也实现了相同的方法,但我在这里直接使用书中的代码只是为了确保我没有搞砸任何事情:

void pickPoint()
{
    const float length = 100.0f;

    // Points a and b
    Vector a = State::position;
    Vector b = a;

    // Find point b of directed line ab
    Vector radians(Math::rad(State::rotation.x), Math::rad(State::rotation.y), 0);
    const float lengthYZ = Math::cos(radians.x) * length;

    b.y -= Math::sin(radians.x) * length;
    b.x += Math::sin(radians.y) * lengthYZ;
    b.z -= Math::cos(radians.y) * lengthYZ;

    // Compute the t value for the directed line ab intersecting the plane
    Vector ab = b - a;

    GLfloat t = (p.d - p.n.dot(a)) / p.n.dot(ab);

    printf("Plane normal: %f, %f, %f\n", p.n.x, p.n.y, p.n.z);
    printf("Plane value d: %f\n", p.d);
    printf("Rotation (degrees): %f, %f, %f\n", State::rotation.x, State::rotation.y, State::rotation.z);
    printf("Rotation (radians): %f, %f, %f\n", radians.x, radians.y, radians.z);
    printf("Point a: %f, %f, %f\n", a.x, a.y, a.z);
    printf("Point b: %f, %f, %f\n", b.x, b.y, b.z);
    printf("Expected length of ray: %f\n", length);
    printf("Actual length of ray: %f\n", ab.length());
    printf("Value t: %f\n", t);

    // If t in [0..1] compute and return intersection point
    if(t >= 0.0f && t <= 1.0f) 
    {
        point = a + t * ab;
        printf("Intersection: %f, %f, %f\n", point.x, point.y, point.z);
    }
    // Else no intersection
    else
    {
        printf("No intersection found\n");
    }

    printf("\n\n");
}

当我用 OpenGL 渲染这个点时,它看起来非常接近光线和平面的交点。但是通过打印出实际值,我发现对于特定的位置和旋转,交点最多可以偏离 0.000004。这是交点不准确的示例 - 我知道交点不在平面上,因为它的 Y 值应该是 0,而不是 0.000002。我也可以将它代入平面方程并得到一个不等式:

Plane normal: 0.000000, 1.000000, 0.000000
Plane value d: 0.000000
Rotation (degrees): 70.100044, 1.899823, 0.000000
Rotation (radians): 1.223477, 0.033158, 0.000000
Point a: 20.818802, 27.240383, 15.124892
Point b: 21.947229, -66.788452, -18.894285
Expected length of ray: 100.000000
Actual length of ray: 100.000000
Value t: 0.289702
Intersection: 21.145710, 0.000002, 5.269455

现在,我知道浮点数只是实数的近似值,所以我猜测这种不准确性只是浮点舍入的影响,尽管我可能在代码的其他地方犯了错误。我知道交叉点只偏离了非常小的量,但我仍然关心它,因为我计划使用这些点通过将它们捕捉到任意方向的网格来定义模型或关卡的顶点,所以我实际上想要这些点在那个网格上,即使它们有点不准确。这可能是一种被误导的方法——我真的不知道。

所以我的问题是:这种不准确性只是工作中的浮点舍入,还是我在其他地方犯了错误?

如果只是浮点取整,有什么办法处理吗?我尝试以各种方式对旋转和位置向量的值进行四舍五入,这显然会导致交点不太准确,但有时我仍然会得到不在平面上的交点。我确实阅读了一个类似问题 (Is this plane-ray intersection code correct?) 的答案,其中提到要保持较大的尺寸,但我不确定这到底是什么意思。

抱歉,如果之前有人问过这个问题 - 我进行了搜索,但没有看到任何我遇到的问题。谢谢!

【问题讨论】:

    标签: c++ collision-detection linear-algebra game-physics floating-accuracy


    【解决方案1】:

    您的数学似乎是正确的,这绝对看起来像是一个舍入错误。我有一种强烈的感觉,就是这条线:

    GLfloat t = (p.d - p.n.dot(a)) / p.n.dot(ab);
    

    也就是说,我没有看到任何其他计算 t 的方法。您可以通过在 printf 语句中使用“%.12f”(或更多)来验证是否丢失了精度。查明罪魁祸首的另一种方法是尝试逐步进行 t 计算并沿途打印结果以查看是否在某处丢失了精度。

    如果精度对你来说真的那么重要,你是否尝试过使用双精度浮点?

    【讨论】:

      猜你喜欢
      • 2012-02-14
      • 1970-01-01
      • 1970-01-01
      • 2014-07-21
      • 1970-01-01
      • 1970-01-01
      • 2012-11-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多