OpenGL中的Ray Sphere交点 [英] Ray Sphere Intersections in OpenGL

查看:174
本文介绍了OpenGL中的Ray Sphere交点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用ray-sphere交点在OpenGL中执行鼠标拾取。我在网上找到了一些公式,而我尝试的最后一个公式是Stack Exchange上的用户提出的一个公式:

Ray-Sphere十字路口



从我对这个答案的理解中,我正在检查if二次方程具有正根。我并不在乎交叉点在哪里发生,只要光线与球体相交。我的代码如下,但似乎没有工作。

  bool Mesh :: useObject(vec3相机,vec3方向){

// a =( (方向.v [0] --camera.v [0])*(direction.v [0])。(xB-xA)2 +(yB-yA)2 +(zB-zA)2
float a = - camera.v [0])
+(direction.v [1] - camera.v [1])*(direction.v [1] - camera.v [1])$ ​​b $ b +( direction.v [2] - camera.v [2])*(direction.v [2] - camera.v [2]));

// b = 2 *((xB-xA)(xA-xC)+(yB-yA)(yA-yC)+(zB-zA)(zA-zC))
float b = 2 *((direction.v [0] - camera.v [0])*(camera.v [0] - mOrigin.v [0])
+(direction.v [1 ] - camera.v [1])*(camera.v [1] - mOrigin.v [1])$ ​​b $ b +(direction.v [2] - camera.v [2])*(camera.v [2] - mOrigin.v [2]));

// c =(xA-xC)2 +(yA-yC)2 +(zA-zC)2 -r 2
float c =((camera.v [0] - mOrigin.v [1])*(camera.v [1])(camera.v [0] ] - mOrigin.v [1])$ ​​b $ b +(camera.v [2] - mOrigin.v [2])*(camera.v [2] - mOrigin.v [2])
- (mRadius)*(mRadius));

float delta =(b * b) - (4 * a * c);

if(delta <0)
返回false;

else {
std :: cout<< 对象<< mMesh< 处于位置(<=
mOrigin.v [0],<
mOrigin.v [1]<,< <
mOrigin.v [2]<<)\\\
<<
大小:<< mRadius<<的std :: ENDL;
返回true;
}
}

我的C ++技巧非常生疏,所以我很抱歉这是做这件事的一个可怕的方式。这种方法需要三个浮标的两个矢量,相机的位置以及相机前面1个单元中的一个点的坐标(我称之为这个方向)。

我希望获得对象的位置,当我面对一个对象并按下某个键时,它的半径是,但是只有当我站在场景中的某个对象上时,才能获得输出,或者如果我站在另一个大物体旁边。很明显,我的计算有问题(或者更糟糕的是,我的代码),但我无法弄清楚是什么。 :(b / b)

让我知道你是否需要更多的代码,或者如果有什么不清楚的地方,这是我的第一个问题,所以如果这个问题很糟糕,对不起。



编辑:我不应该使用鼠标坐标作为我的射线上的一个点。稍微编辑我的问题。


我假设有一个笛卡尔坐标系,一个由piont P_ray0 定义的光线和一个方向 code> Dir_ray ,进一步由点 P_centerS 和半径 radius_S
请注意,射线 P_ray0 上的点可能是相机的位置,而射线的方向 Dir_ray 可能是视线的导演。



为了检查光线是否与球体相交,光线与平面的交点必须找到,这是正常的射线和球体的中心点在哪里
如果距离到球体中心的反射点小于球体的半径,则射线与球体相交。
如果距离精确等于球体的半径,则射线接触球体。
如果射线偏离了球体的大点。



射线与平面的交点是通过将射线的方程> P_ray = dist * Dir_ray + P_ray0 代入平面的方程式中点(P_plane - P_plane0,NV_plane)== 0

  P_isect = P_ray0 + Dir_ray * dist; 
dist = dot(P_plane0 - P_ray0,NV_plane)/ dot(Dir_ray,NV_plane)

在这种情况下,平面的法向量 NV_plane 和光线的方向是相等的,因为我们想要找到光线与平面的交点,这是正常的到射线。
这简化了公式,因为 dot(Dir_ray,NV_plane)== 1

  P_isect = P_ray0 + Dir_ray * dot(P_plane0  -  P_ray0,NV_plane); 

设置下面的c ++示例:

  #include  

bool isectRraySphere(
const vec3& P_ray0,
const vec3& Dir_ray,
const vec3& P_centerS,
float radius_S)
{
//标准化射线的Dir_ray,它等于平面的法向量。
//注意,如果`directory`已经是一个单位矢量(长度为1.0的矢量),则可以跳过。
vec3 NV = normalize(Dir_ray);

//使用上面的公式计算交点。
vec3 P_isect = P_ray0 + NV * dot(P_centerS - P_ray0,NV);

//计算球体中心与交点之间的距离
float isect_dist = length(P_isect - P_centerS);

//将交点距离与字幕半径进行比较。
bool isect = isect_dist< radius_S;

return isect;
}

通过不比较交叉点的距离和球体的半径,但距离的平方。所以可以避免一个 sqrt 操作:

  vec3 dv = P_isect  - P_centerS; 
bool isect =(dv [0] * dv [0] + dv [1] * dv [1] + dv [2] * dv [2])< (radius_S * radius_S);

寻求类型定义和向量操作的完整性。请注意,这些操作与 glm 库非常相似。

 使用vec3 = std :: array< float,3>; 
vec3 operator +(vec3 a,vec3 b){return {a [0] + b [0],a [1] + b [1],a [2] + b [2]}; (vec3 a,vec3 b){return {a [0] -b [0],a [1] -b [1],a [2] -b [2]};}
vec3 }
vec3 operator *(vec3 v,float d){return {v [0] * d,v [1] * d,v [2] * d}; }
vec3 length(vec3 v){return sqrt(v [0] * v [0] + v [1] * v [1] + v [2] * v [2]); }
vec3 normalize(vec3 v){float len = length(v); return {v [0] / len,v [1] / len,v [2] / len}; }
float dot(vec3 a,T_B b){return a [0] * b [0] + a [1] * b [1] + a [2] * b [2]; }


I'm trying to use ray-sphere intersections to perform mouse picking in OpenGL. I found a few formulae online, and the last one I tried was one suggested by a user on Stack Exchange:

Ray-Sphere intersection

From what I've understood of this answer, I'm checking if the quadratic equation has positive roots. I don't really care where the intersection takes place, just if the ray intersects the sphere at all. My code is as follows, but doesn't seem to work.

bool Mesh::useObject(vec3 camera, vec3 direction) {

    // a = (xB-xA)²+(yB-yA)²+(zB-zA)²
    float a = ((direction.v[0] - camera.v[0])*(direction.v[0] - camera.v[0])
        + (direction.v[1] - camera.v[1])*(direction.v[1] - camera.v[1])
        + (direction.v[2] - camera.v[2])*(direction.v[2] - camera.v[2]));

    // b = 2*((xB-xA)(xA-xC)+(yB-yA)(yA-yC)+(zB-zA)(zA-zC))
    float b = 2 * ((direction.v[0] - camera.v[0])*(camera.v[0] - mOrigin.v[0])
        + (direction.v[1] - camera.v[1])*(camera.v[1] - mOrigin.v[1])
        + (direction.v[2] - camera.v[2])*(camera.v[2] - mOrigin.v[2]));

    // c = (xA-xC)²+(yA-yC)²+(zA-zC)²-r²
    float c = ((camera.v[0] - mOrigin.v[0])*(camera.v[0] - mOrigin.v[0])
        + (camera.v[1] - mOrigin.v[1])*(camera.v[1] - mOrigin.v[1])
        + (camera.v[2] - mOrigin.v[2])*(camera.v[2] - mOrigin.v[2])
        - (mRadius)*(mRadius));

    float delta = (b*b) - (4 * a*c);

    if (delta < 0)
        return false;

    else {
        std::cout << "Object " << mMesh << " is at postion (" <<
            mOrigin.v[0] << ", " <<
            mOrigin.v[1] << ", " <<
            mOrigin.v[2] << ")\n" <<
            "Size: " << mRadius << std::endl;
        return true;
    }
}

My C++ skills are pretty rusty, so I apologise if this is a horrible way of doing this. This method takes two vectors of three floats, the position of the camera, and the coordinates of a point in my scene 1 unit in front of the camera (I've called this direction).

I want to get the position of the object and it's radius when I face an object and press a key, but instead I get output only when I'm standing on one of the objects in my scene, or if I stand close to another large object. Clearly there's something wrong with my calculations (or worse, my code) but I cannot figure out what that is. :(

Let me know if you need any more code, or if anything is unclear. This is my first question with SO, so if this question is badly phrased, I'm sorry.

EDIT: I shouldn't have been using mouse coordinates as a point on my ray. Edited my question slightly.

解决方案

I assume there is a cartesian coordinate system, a ray defined by a piont P_ray0 and a direction Dir_ray, further a sphere defined by a point P_centerS and a radius radius_S. Note, the point on the ray P_ray0 may be the positton of the camera and the direction of the ray Dir_ray may be the directorn of the line of sight.

In order to check whether a ray intersects a sphere, the intersection point of the ray with the plane must be found, which is normal to the ray and where the center point of the sphere is on. If the distance of the intersection point to the center of the sphere is less then the radius of the sphere, then the ray intersects the sphere. If the distance is exact equal the radius of the sphere then the ray touches the sphere. If it is greater the the ray missed the sphere.

The intersection of a ray with a plane is calculated by substituting the equation of the ray
P_ray = dist * Dir_ray + P_ray0 into the equation of the plane
dot( P_plane - P_plane0, NV_plane ) == 0:

P_isect = P_ray0 + Dir_ray * dist;
dist    = dot( P_plane0 - P_ray0, NV_plane ) / dot( Dir_ray, NV_plane )

In this case the normal vector of the plane NV_plane and the direction of the ray are equal, because we want to find ther intersection point of the ray witt the plane, which is normal to the ray. This simplifies the formula, because dot( Dir_ray, NV_plane ) == 1:

P_isect = P_ray0 + Dir_ray * dot( P_plane0 - P_ray0, NV_plane );

Set the c++ example below:

#include <math.h>

bool isectRraySphere( 
      const vec3 &P_ray0,
      const vec3 &Dir_ray,
      const vec3 &P_centerS,
      float       radius_S )
{
    // Normalize the Dir_ray of the ray, which is equal the normalvector of the plane.
    // Note, this can be skipped, if `directory` already is a unit vector (a vector with a length of 1.0).
    vec3 NV = normalize( Dir_ray );

    // Calculate the intersection point, using the above formula.
    vec3 P_isect = P_ray0 + NV * dot( P_centerS - P_ray0, NV );

      // Calculate the distance between the center of the sphere and the intersection point
    float isect_dist = length( P_isect - P_centerS );

    // comparet the intersection distance with the radius of the speher.
    bool isect = isect_dist < radius_S;

    return isect;
}

An performance improvement can be achieved, by not comparing the distance of the intersection with the radius of the sphere, but the square of the distances. So one sqrt operation can be avoided:

vec3 dv = P_isect - P_centerS;
bool isect = (dv[0]*dv[0] + dv[1]*dv[1] + dv[2]*dv[2]) < (radius_S * radius_S); 

For the seek of completeness the type definitions and vector operations. Note, the operations are very similar the glm library.

using vec3 = std::array<float, 3>;
vec3 operator + (vec3 a, vec3 b) { return {a[0]+b[0], a[1]+b[1], a[2]+b[2]}; }
vec3 operator - (vec3 a, vec3 b) { return {a[0]-b[0], a[1]-b[1], a[2]-b[2]}; }
vec3 operator * (vec3 v, float d) { return {v[0]*d, v[1]*d, v[2]*d}; }
vec3 length(vec3 v) { return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2] ); }
vec3 normalize(vec3 v) { float len = length(v); return {v[0]/len, v[1]/len, v[2]/len}; }
float dot(vec3 a, T_B b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }

这篇关于OpenGL中的Ray Sphere交点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆