光线平面交叉:结果不准确 - 舍入误差?

时间:2013-02-27 01:42:23

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

我已经使用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的“Real-Time Collision Detection”一书中复制了这个结构:

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?),提到保持尺寸大,但我不确定这意味着什么。

对不起,如果之前已经问过这个问题 - 我搜索过,但是我没有看到任何与我有问题的东西。谢谢!

1 个答案:

答案 0 :(得分:1)

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

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

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

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