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;
}
//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;
}
3条答案
按热度按时间x6yk4ghg1#
不管你使用的是2D还是3D,到目前为止,最简单的解决方案是为每个可拾取的对象分配唯一的颜色,而这种颜色通常根本不会使用。然后,对于您渲染(到后台缓冲区)的每个单击事件,使用应用于每个对象的唯一颜色呈现相同的场景。通过禁用照明等,问题就变成了查看鼠标下方的像素颜色,并使用查找表来查看哪个对象被单击。
即使在16位色深的情况下,仍然可以获得2^16个独特的可拾取对象,而在现实中,少于2^24个的颜色深度在现代应用中是很少见的。
5lhxktic2#
当我们使用2D对象时,如果鼠标的位置在对象内部,则对象被指向。对于不同的几何形状,这种处于内部的概念是不同的。
对于矩形宽度左上角
c
和width
,height
,函数可能如下所示:对于中心为
c
、半径为r
的圆,它可能如下所示:对于另一个形状,您必须找出另一个函数来计算鼠标位置是否在内部,但在许多情况下,您可以将其简化为边界矩形或球体。
fdx2calv3#
调查问卷要求的是2D解决方案,但乍一看并不明显。对于所有想要在3D中完成此操作,但又不想放在一起的Pixel_idMap缓冲区的人,我准备了以下解决方案。
它从相机中产生光线,可用于在搜索所有对象时查找碰撞。它将选择最靠近摄影机的碰撞。
注意!如果您有许多对象,这会很慢,因为它需要循环遍历所有对象。
主要原则:
帮助功能: