鼠标到射线 - 球体碰撞检测

时间:2011-11-03 18:55:07

标签: c++ opengl

我一直试图找到一种可以检测射线(代表枪支子弹)和敌人周围球体之间交叉点的工作算法....我尝试了一些在网上找到的但是似乎都没有正常工作,也许我做错了什么......

这是我目前正在使用的那个:

    //// Ray-sphere intersection. 
    // p=(ray origin position - sphere position),
    // d=ray direction,
    // r=sphere radius,
    // Output:
    // i1=first intersection distance,
    // i2=second intersection distance
    // i1<=i2
    // i1>=0
    // returns true if intersection found,false otherwise.// 


    bool Player::RaySphereIntersect(const Vector3 &p, const Vector3 &d, double r,                 double  &i1, double &i2){ 
double det,b;   
b = -Vector3::dot(p,d); 
det = b*b - Vector3::dot(p,p) + r*r;    
if (det<0){     
    return false;   
}   
det= sqrt(det); 
i1= b - det;    
i2= b + det;    

// intersecting with ray?   
if(i2<0) 
    return false;   
if(i1<0)
    i1=0;   
return true;
    }

我使用敌人的位置作为球体位置,大致是玩家枪的位置作为射线原点和射线方向的投影鼠标坐标...这是我用来投影鼠标坐标的OpenGL代码遥远的飞机:

    Vector3 projectedMouse(float mx, float my){

GLdouble model_view[16];
GLint viewport[4];
GLdouble projection[16];

GLfloat winX, winY, winZ;
GLdouble dx, dy, dz, bx, by, bz;

glGetDoublev(GL_MODELVIEW_MATRIX, model_view);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);

winX = (float)mx;
 winY = (float)viewport[3] - (float)my;

glReadPixels ((int)mx, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ); 
gluUnProject(winX, winY, 1, model_view, projection, viewport, &dx, &dy, &dz);

projectedAim = Vector3(dx, dy, dz);

return projectedAim;
    }

这似乎是正确的因为我正在用它画一个GL线,它看起来很好......所以也许它是交叉码,但似乎没有任何作用......我尝试了另一个应该返回交叉点距离,但对于任何给定的敌人位置,它仍然给我非常随机的结果:

    double intersectRaySphere(Vector3 rO, Vector3 rV, Vector3 sO, double sR) 
       Vector3 Q = sO-rO;
       double c = Q.magnitude();
       double v = Vector3::dot(Q,rV);
       double d = sR*sR - (c*c - v*v);

       // If there was no intersection, return -1
       if (d < 0.0) return (-1.0f);

       // Return the distance to the [first] intersecting point
       return (v - sqrt(d));

他们都被略微修改以匹配我正在使用的库中的Math函数....任何人都可以发现它们有问题,或者建议另一个吗?这让我发疯了......

谢谢!

1 个答案:

答案 0 :(得分:2)

看起来你正在用Cramer的规则来解决交叉问题。考虑替代。多项式的根将告诉你交点。

从2D情况开始我们想要的是查看位于圆C中心的点P的正交(因此最小)距离是否小于圆C的半径R.

基本上。我们找到圆心与光线/线之间的最小距离。我们如何做到这一点?有几种方法。

我们知道最短距离是一条直线,它具有从圆形中间开始的正交斜率(在R2负数中)。然后我们找到两条线之间的交点。如果我们不得不超过长度R我们在外面,我们不在乎它有多远。

http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html

解决两条线的交点,看到它的交点比R更远可能不是最有效的方法,但幸运的是,wolfram有更好的方法使用更高级别的数学做同样的事情。

现在考虑R3中与射线相交的射线基本上是相同的,但是“正交”比R2更难取悦,所以我们使用双交叉产品。并求解参数方程。

http://www.cs.umbc.edu/~olano/435f02/ray-sphere.html

这是一种聪明的方法,可以看到我们的光线的任何部分是否满足我们球体的方程作为约束。

float a = Vector3::dot(d,d);
float b = Vector3::dot(d * 2, p); 
float c = Vector3::dot(p,p) - r*r
// if the discriminant of the quadratic formula is positive
// we have at least one intersection
return (b*b - 4 * a * c) >= 0 

简而言之。当我的列是函数及其派生词时,我才发现Cramer的规则对微分方程很有帮助。通常在找到Wronskian时。