34

我有一个使用 directx 和 openGL 的渲染器和一个 3d 场景。视口和窗口的尺寸相同。

如何以独立于平台的方式实现拾取给定的鼠标坐标 x 和 y?

4

6 回答 6

31

如果可以,请通过鼠标指针计算来自眼睛的光线并将其与模型相交,从而在 CPU 上进行拾取。

如果这不是一个选项,我会选择某种类型的 ID 渲染。为每个要选择唯一颜色的对象分配颜色,用这些颜色渲染对象,最后从鼠标指针下的帧缓冲区中读出颜色。

编辑:如果问题是如何从鼠标坐标构造射线,您需要以下内容:投影矩阵P和相机变换C。如果鼠标指针的坐标是(x, y)并且视口的大小是(width, height)沿射线的剪辑空间中的一个位置是:

mouse_clip = [
  float(x) * 2 / float(width) - 1,
  1 - float(y) * 2 / float(height),
  0,
  1]

(请注意,我翻转了 y 轴,因为鼠标坐标的原点通常在左上角)

以下也是正确的:

mouse_clip = P * C * mouse_worldspace

这使:

mouse_worldspace = inverse(C) * inverse(P) * mouse_clip

我们现在有:

p = C.position(); //origin of camera in worldspace
n = normalize(mouse_worldspace - p); //unit vector from p through mouse pos in worldspace
于 2010-01-19T11:42:56.147 回答
25

这是视锥体:

视锥体

首先,您需要确定鼠标单击发生在近平面上的哪个位置:

  1. 将窗口坐标 (0..640,0..480) 重新缩放为 [-1,1],左下角为 (-1,-1),右上角为 (1,1)。
  2. 通过将缩放坐标乘以我所说的“unview”矩阵来“撤消”投影:unview = (P * M).inverse() = M.inverse() * P.inverse()其中M是 ModelView 矩阵,P是投影矩阵。

然后确定相机在世界空间中的位置,并从相机开始绘制一条射线,并通过您在近平面上找到的点。

相机位于M.inverse().col(4),即逆模型视图矩阵的最后一列。

最终伪代码:

normalised_x = 2 * mouse_x / win_width - 1
normalised_y = 1 - 2 * mouse_y / win_height
// note the y pos is inverted, so +y is at the top of the screen

unviewMat = (projectionMat * modelViewMat).inverse()

near_point = unviewMat * Vec(normalised_x, normalised_y, 0, 1)
camera_pos = ray_origin = modelViewMat.inverse().col(4)
ray_dir = near_point - camera_pos
于 2011-06-07T11:43:59.720 回答
2

嗯,很简单,这背后的理论总是一样的

1)将您的 2D 坐标取消投影到 3D 空间的两倍。(每个 API 都有自己的功能,但您可以根据需要实现自己的功能)。Min Z 一个,Max Z 一个。

2) 使用这两个值计算从 Min Z 指向 Max Z 的向量。

3) 使用向量和一个点计算从 Min Z 到 MaxZ 的射线

4)现在你有一条射线,你可以用它做一个射线-三角形/射线-平面/射线-某物相交并得到你的结果......

于 2010-01-19T21:35:31.463 回答
1

我几乎没有 DirectX 经验,但我确信它类似于 OpenGL。你想要的是 gluUnproject 调用。

假设您有一个有效的 Z 缓冲区,您可以使用以下命令在鼠标位置查询 Z 缓冲区的内容:

// obtain the viewport, modelview matrix and projection matrix
// you may keep the viewport and projection matrices throughout the program if you don't change them
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);

// obtain the Z position (not world coordinates but in range 0 - 1)
GLfloat z_cursor;
glReadPixels(x_cursor, y_cursor, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z_cursor);

// obtain the world coordinates
GLdouble x, y, z;
gluUnProject(x_cursor, y_cursor, z_cursor, modelview, projection, viewport, &x, &y, &z);

如果你不想使用 glu 你也可以实现 gluUnProject 你也可以自己实现它,它的功能相对简单,在opengl.org上有描述

于 2010-01-20T08:58:15.573 回答
0

好的,这个话题很老,但它是我在这个话题上找到的最好的,它对我有点帮助,所以我会在这里为那些关注的人发帖;-)

这是我让它工作的方式,而无需计算投影矩阵的逆:

void Application::leftButtonPress(u32 x, u32 y){
    GL::Viewport vp = GL::getViewport(); // just a call to glGet GL_VIEWPORT
vec3f p = vec3f::from(                        
        ((float)(vp.width - x) / (float)vp.width),
        ((float)y / (float)vp.height),
            1.);
    // alternatively vec3f p = vec3f::from(                        
    //      ((float)x / (float)vp.width),
    //      ((float)(vp.height - y) / (float)vp.height),
    //      1.);

    p *= vec3f::from(APP_FRUSTUM_WIDTH, APP_FRUSTUM_HEIGHT, 1.);
    p += vec3f::from(APP_FRUSTUM_LEFT, APP_FRUSTUM_BOTTOM, 0.);

    // now p elements are in (-1, 1)
    vec3f near = p * vec3f::from(APP_FRUSTUM_NEAR);
    vec3f far = p * vec3f::from(APP_FRUSTUM_FAR);

    // ray in world coordinates
    Ray ray = { _camera->getPos(), -(_camera->getBasis() * (far - near).normalize()) };

    _ray->set(ray.origin, ray.dir, 10000.); // this is a debugging vertex array to see the Ray on screen

    Node* node = _scene->collide(ray, Transform());
   cout << "node is : " << node << endl;
}

这假设了一个透视投影,但是对于正交投影从来没有出现过这个问题。

于 2012-04-29T16:24:33.983 回答
0

我在普通的光线拾取中遇到了同样的情况,但是出了点问题。我已经以正确的方式执行了 unproject 操作,但是它不起作用。我想,我犯了一些错误,但不知道在哪里。我的 matix multiplication 、 inverse 和 vector by matix 乘法都可以正常工作,我已经测试过了。在我的代码中,我对 WM_LBUTTONDOWN 做出反应。因此 lParam 将 [Y][X] 坐标作为 dword 中的 2 个字返回。我提取它们,然后转换为规范化空间,我检查了这部分也可以正常工作。当我单击左下角时 - 我的值接近 -1 -1 并且所有其他 3 个角的值都很好。然后我使用 linepoins.vtx 数组进行调试,它甚至不接近现实。

unsigned int x_coord=lParam&0x0000ffff; //X RAW COORD
unsigned int y_coord=client_area.bottom-(lParam>>16); //Y RAW COORD

double xn=((double)x_coord/client_area.right)*2-1; //X [-1 +1]
double yn=1-((double)y_coord/client_area.bottom)*2;//Y [-1 +1]

_declspec(align(16))gl_vec4 pt_eye(xn,yn,0.0,1.0); 
gl_mat4 view_matrix_inversed;
gl_mat4 projection_matrix_inversed;
cam.matrixProjection.inverse(&projection_matrix_inversed);
cam.matrixView.inverse(&view_matrix_inversed);

gl_mat4::vec4_multiply_by_matrix4(&pt_eye,&projection_matrix_inversed);
gl_mat4::vec4_multiply_by_matrix4(&pt_eye,&view_matrix_inversed);

line_points.vtx[line_points.count*4]=pt_eye.x-cam.pos.x;
line_points.vtx[line_points.count*4+1]=pt_eye.y-cam.pos.y;
line_points.vtx[line_points.count*4+2]=pt_eye.z-cam.pos.z;
line_points.vtx[line_points.count*4+3]=1.0;
于 2014-11-20T07:20:54.473 回答