2

我已经使用C++和OpenGL设置了简单的3D第一人称演示,并且它看起来工作得很好。我的目标是:当用户将摄像机指向一个平面并单击鼠标左键时,我想绘制一个光线指向相机朝向玩家位置的方向与该平面的交点。雷射平面交叉点:结果不准确 - 舍入误差?

于是,我开始了与两个向量,Vector positionVector rotation,其中向量是一个非常标准的三维矢量类:

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,与平面是一个简单的结构存储在正常的飞机和d。我复制这个结构直接从书“实时碰撞检测,”通过和Christer埃里克森:

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

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

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

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

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