确定鼠标指向哪个对象?

rsaldnfx  于 2022-10-18  发布在  其他
关注(0)|答案(3)|浏览(149)

有人能告诉我你能想到的最简单的方法来确定被点击的对象,这样我就可以使用GLUT函数独立地移动那个对象吗?

x6yk4ghg

x6yk4ghg1#

不管你使用的是2D还是3D,到目前为止,最简单的解决方案是为每个可拾取的对象分配唯一的颜色,而这种颜色通常根本不会使用。然后,对于您渲染(到后台缓冲区)的每个单击事件,使用应用于每个对象的唯一颜色呈现相同的场景。通过禁用照明等,问题就变成了查看鼠标下方的像素颜色,并使用查找表来查看哪个对象被单击。
即使在16位色深的情况下,仍然可以获得2^16个独特的可拾取对象,而在现实中,少于2^24个的颜色深度在现代应用中是很少见的。

5lhxktic

5lhxktic2#

当我们使用2D对象时,如果鼠标的位置在对象内部,则对象被指向。对于不同的几何形状,这种处于内部的概念是不同的。
对于矩形宽度左上角cwidthheight,函数可能如下所示:

bool isInsideRectangle(double x, double y) {
    // The mouse is inside if the coordinates are 'inside' all walls
    return (x > c.x &&
            y > c.y &&
            x < c.x + width &&
            y < c.y + height);
}

对于中心为c、半径为r的圆,它可能如下所示:

bool isInsideCircle(double x, double y) {
    // The mouse is inside if the distance to the center of the circle
    // is less than the radius
    return std::sqrt((x - c.x)*(x - c.x) + (y - c.y)*(y - c.y)) < r;
}

对于另一个形状,您必须找出另一个函数来计算鼠标位置是否在内部,但在许多情况下,您可以将其简化为边界矩形或球体。

fdx2calv

fdx2calv3#

调查问卷要求的是2D解决方案,但乍一看并不明显。对于所有想要在3D中完成此操作,但又不想放在一起的Pixel_idMap缓冲区的人,我准备了以下解决方案。
它从相机中产生光线,可用于在搜索所有对象时查找碰撞。它将选择最靠近摄影机的碰撞。
注意!如果您有许多对象,这会很慢,因为它需要循环遍历所有对象。

//Must put somewhere in your code to initialize mouse handler
glutMouseFunc(HandleMouse);

主要原则:


# include <limits>

# include <Eigen/Core>

# include <GL/glew.h>

# include <GL/glut.h>

using namespace Eigen;

//You glut mouse handling function
void HandleMouse(int button, int state, int x, int y) {

    //Assuming you only want to process left mouse button clicks
    if (state != GLUT_DOWN || button != GLUT_LEFT_BUTTON) {
        return;
    }

    Vector3d up; up << 0, 0, 1; // if you changed your "up"-vector in your world, then this line needs to be fixed

    //Assuming you keep track of the camera position and where it is pointing
    //Do some math and make a ray come out of the camera
    Matrix4d custom_modelview = GetModelViewFromCamPos(camera_pos, camera_look_at, up);
    Matrix4d inverse_projection = GetInverseProjectionMatrix();
    Vector3d ray_from_camera = Get3dRayInWorldFromModelViewAndInverseProjection(x, y, custom_modelview, inverse_projection);

    //Search the object which intersects with ray
    SearchForClosestIntersectingObject(ray_from_camera, camera_pos);

}

//Your intersection search function
void SearchForClosestIntersectingObject(Vector3d ray_from_camera, Vector3d camera_pos) {
    double smallest_dist_to_camera = std::numeric_limits<double>::max();
    Foo* leading_cand = NULL;

    //Search
    for (Foo* obj : my_objects) {

        //You will need to implement your own collision detector. This function should take in a camera position and a 3d vector ray which goes out
        //from the camera. It should return the distance from a collision to camera if a collision is detected, or else return -1.
        //The math is different depending on if you are intersecting with a plane, a ball, a square or triangle etc.
        double intersection_dist_to_camera = obj->RayCollidesWithMe(ray_from_camera, camera_pos);

        if (intersection_dist_to_camera != -1 && intersection_dist_to_camera < smallest_dist_to_camera) {
            smallest_dist_to_camera = intersection_dist_to_camera;
            leading_cand = obj;
        }

    }
    if (leading_cand != NULL) {
        //Do whatever with your clicked object
        DoWhatever(leading_cand);
    }

}

帮助功能:

//Make a modelview matrix from a specific camera position, target and direction up;
Matrix4d GetModelViewFromCamPos(Vector3d eye, Vector3d target, Vector3d upDir)
{
    // compute the forward vector from target to eye
    Vector3d forward = eye - target;
    forward.normalize();                 // make unit length

    // compute the left vector
    Vector3d left = upDir.cross(forward); // cross product
    left.normalize();

    // recompute the orthonormal up vector
    Vector3d up = forward.cross(left);    // cross product

    // init 4x4 matrix
    Matrix4d matrix; matrix << 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1;

    // set rotation part, inverse rotation matrix: M^-1 = M^T for Euclidean transform
    matrix << left(0), up(0), forward(0), 0, left(1), up(1), forward(1), 0, left(2), up(2), forward(2), 0, -left(0) * eye(0) - left(1) * eye(1) - left(2) * eye(2), -up(0) * eye(0) - up(1) * eye(1) - up(2) * eye(2), -forward(0) * eye(0) - forward(1) * eye(1) - forward(2) * eye(2), 1;

    return matrix;
}

//A function which grabs a copy of the projection matrix from GL and makes some changes to it
Matrix4d GetInverseProjectionMatrix() {
    double op[16]; // original projection matrix
    glGetDoublev(GL_PROJECTION_MATRIX, op);

    double a = op[0];
    double b = op[5];
    double c = op[10];
    double d = op[14];
    double e = op[11];

    double inverse_proj_arr[16] = { 0.0 };
    inverse_proj_arr[0] = 1.0 / a;
    inverse_proj_arr[5] = 1.0 / b;
    inverse_proj_arr[11] = 1.0 / d;
    inverse_proj_arr[14] = 1.0 / e;
    inverse_proj_arr[15] = -c / (d * e);

    Matrix4d inverse_proj;
    inverse_proj = Map<Matrix4d>(inverse_proj_arr);
    return inverse_proj;
}

//Make a ray
Vector3d Get3dRayInWorldFromModelViewAndInverseProjection(int mouse_x, int mouse_y, Matrix4d mv, Matrix4d ip) {
    float space_x = (2.0f * mouse_x) / glutGet(GLUT_WINDOW_WIDTH) - 1.0f;
    float space_y = 1.0f - (2.0f * mouse_y) / glutGet(GLUT_WINDOW_HEIGHT);
    float space_z = 1.0f;
    Vector3d ray_nds;
    ray_nds << space_x, space_y, space_z;
    Vector4d ray_clip = Vector4d(ray_nds(0), ray_nds(1), -1.0, 1.0);
    Vector4d ray_eye = ip * ray_clip;
    ray_eye << ray_eye(0), ray_eye(1), -1, 0;
    Vector4d ray_world_4d = mv.inverse().transpose() * (ray_eye);
    Vector3d ray_world_3d; ray_world_3d << ray_world_4d(0), ray_world_4d(1), ray_world_4d(2);
    ray_world_3d.normalize();
    return ray_world_3d;
}

相关问题