通过光线投射拾取对象

发布于 2025-01-08 01:36:56 字数 2286 浏览 3 评论 0原文

我在检测盒子内鼠标点击的光线投射算法中一直遇到不准确的问题。我完全不知道如何正确解决这个问题,而且它已经困扰我好几个星期了。

最容易用图片描述问题(以 [0, 0, -30] 为中心的框):

问题的屏幕截图

黑线代表绘制的实际碰撞框,绿色框代表实际被击中的区域。请注意它的偏移量(如果框距离原点较远,偏移量似乎会变大)并且略小于绘制的碰撞框。

这是一些相关的代码,

射线盒投射:

double BBox::checkFaceIntersection(Vector3 points[4], Vector3 normal, Ray3 ray) {

    double rayDotNorm = ray.direction.dot(normal);
    if(rayDotNorm == 0) return -1;

    Vector3 intersect = points[0] - ray.origin;
    double t = intersect.dot(normal) / rayDotNorm;
    if(t < 0) return -1;

    // Check if first point is from under or below polygon
    bool positive = false;
    double firstPtDot = ray.direction.dot( (ray.origin - points[0]).cross(ray.origin - points[1]) );
    if(firstPtDot > 0) positive = true;
    else if(firstPtDot < 0) positive = false;
    else return -1;

    // Check all signs are the same
    for(int i = 1; i < 4; i++) {
        int nextPoint = (i+1) % 4;
        double rayDotPt = ray.direction.dot( (ray.origin - points[i]).cross(ray.origin - points[nextPoint]) );
        if(positive && rayDotPt < 0) {
            return -1;
        }
        else if(!positive && rayDotPt > 0) {
            return -1;
        }
    }

    return t;
}

鼠标到射线:

GLint viewport[4];
GLdouble modelMatrix[16];
GLdouble projectionMatrix[16];

glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);

GLfloat winY = GLfloat(viewport[3] - mouse_y);

Ray3 ray;
double x, y, z;
gluUnProject( (double) mouse_x, winY, 0.0f, // Near
              modelMatrix, projectionMatrix, viewport,
              &x, &y, &z );
ray.origin = Vector3(x, y, z);

gluUnProject( (double) mouse_x, winY, 1.0f, // Far
              modelMatrix, projectionMatrix, viewport,
          &x, &y, &z );
ray.direction = Vector3(x, y, z);

if(bbox.checkBoxIntersection(ray) != -1) {
    std::cout << "Hit!" << std::endl;
}

我尝试将实际射线绘制为一条线,它似乎与绘制的框正确相交。

我通过减去盒子位置的所有点和光线原点/方向部分解决了偏移问题,但我不知道为什么会这样,并且碰撞盒的大小仍然不准确。

有什么想法/替代方法吗?如果需要的话我还有其他代码可以提供。

I've been having a problem with inaccuracies in my ray casting algorithm for detecting mouse hits within a box. I'm completely at a loss as to how to fix this properly and it's been bugging me for weeks.

The problem is easiest described with a picture (box centered around [0, 0, -30]):

screen shot of problem

The black lines represent the actual hitbox which is drawn and the green box represents what actually appears to get hit. Notice how it's offset (which seems to get larger if the box is further from the origin) and is slightly smaller than the drawn hitbox.

Here's some relevant code,

ray-box cast:

double BBox::checkFaceIntersection(Vector3 points[4], Vector3 normal, Ray3 ray) {

    double rayDotNorm = ray.direction.dot(normal);
    if(rayDotNorm == 0) return -1;

    Vector3 intersect = points[0] - ray.origin;
    double t = intersect.dot(normal) / rayDotNorm;
    if(t < 0) return -1;

    // Check if first point is from under or below polygon
    bool positive = false;
    double firstPtDot = ray.direction.dot( (ray.origin - points[0]).cross(ray.origin - points[1]) );
    if(firstPtDot > 0) positive = true;
    else if(firstPtDot < 0) positive = false;
    else return -1;

    // Check all signs are the same
    for(int i = 1; i < 4; i++) {
        int nextPoint = (i+1) % 4;
        double rayDotPt = ray.direction.dot( (ray.origin - points[i]).cross(ray.origin - points[nextPoint]) );
        if(positive && rayDotPt < 0) {
            return -1;
        }
        else if(!positive && rayDotPt > 0) {
            return -1;
        }
    }

    return t;
}

mouse to ray:

GLint viewport[4];
GLdouble modelMatrix[16];
GLdouble projectionMatrix[16];

glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);

GLfloat winY = GLfloat(viewport[3] - mouse_y);

Ray3 ray;
double x, y, z;
gluUnProject( (double) mouse_x, winY, 0.0f, // Near
              modelMatrix, projectionMatrix, viewport,
              &x, &y, &z );
ray.origin = Vector3(x, y, z);

gluUnProject( (double) mouse_x, winY, 1.0f, // Far
              modelMatrix, projectionMatrix, viewport,
          &x, &y, &z );
ray.direction = Vector3(x, y, z);

if(bbox.checkBoxIntersection(ray) != -1) {
    std::cout << "Hit!" << std::endl;
}

I've tried drawing the actual ray as a line and it seems to intersect the drawn box correctly.

I had the offset problem partially fixed by minusing all the points and the ray origin/direction by the boxes position, but I have no idea why that worked and the size of the hitbox still remained inaccurate.

Any ideas/alternative approaches? I have other code to supply if it's needed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

你的呼吸 2025-01-15 01:36:56

你假设了错误的方向。正确的是:

ray.direction = Vector3(far.x - near.x, far.y - near.y, far.z - near.z);

如果不减去近处和远处的交点,你的方向就会偏离。

You're assuming a wrong direction. Correct would be:

ray.direction = Vector3(far.x - near.x, far.y - near.y, far.z - near.z);

Without subtracting near and far intersection points, your direction will be off.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文