实施射线拾取

我有一个使用DirectX和OpenGL的渲染器和一个3D场景。 视口和窗口具有相同的尺寸。

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

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

如果这不是一个选项,我会去一些types的ID渲染。 分配每个对象,select一种独特的颜色,用这些颜色渲染对象,最后从鼠标指针下的帧缓冲区中读出颜色。

编辑:如果问题是如何构build射线从鼠标坐标您需要以下内容:投影matrixP和相机变换C. 如果鼠标指针的坐标是(x,y) ,视口的大小是(宽度,高度) ,沿光线的剪辑空间中的一个位置是:

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

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

以下也是如此:

 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 

这是观看视锥体:

观看平截头体

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

  1. 将窗口坐标(0..640,0..480)重新调整为[-1,1],左下angular为(-1,-1),右上angular为(1,1)。
  2. unview = (P * M).inverse() = M.inverse() * P.inverse() ,其中Munview = (P * M).inverse() = M.inverse() * P.inverse()matrixP是投影matrix。

然后确定相机在世界空间中的位置,并从相机开始画一条光线,并通过在近平面上find的点。

相机位于M.inverse().col(4) ,即逆ModelViewmatrix的最后一列。

最终伪代码:

 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 

好吧,很简单,背后的理论总是一样的

1)将您的二维坐标的两倍不要投影到三维空间上。 (每个API都有自己的function,但是如果你愿意,你可以实现自己的function)。 一个在Min Z,一个在Max Z.

2)用这两个值计算从Min Z开始并指向Max Z的vector。

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

4)现在你有一个射线,你可以做一个射线三angular/射线平面/射线相交的东西,并得到你的结果…

我有一点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,你也可以自己实现它,它的function相对简单,在opengl.org

好吧,这个主题是旧的,但它是最好的,我发现在这个话题上,它帮了我一些,所以我会张贴在这里为那些谁是以下;-)

这是我得到它的工作方式,而不必计算投影matrix的逆:

 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; } 

这是假设一个透视投影,但是这个问题从来没有出现在正字上。

我和普通的光线拾取有相同的情况,但有些地方是错误的。 我已经执行了正确的方式,但它只是不工作。 我想,我犯了一些错误,但不知道在哪里。 我的matix乘法,逆和vectormatix乘法都看起来工作正常,我testing了他们。 在我的代码我正在WM_LBUTTONDOWN反应。 所以lParam将[Y] [X]坐标作为双字中的2个单词返回。 我解压缩他们,然后转换为标准化的空间,我检查了这部分也工作正常。 当我点击左下angular – 我得到接近值-1 -1和所有3个其他angular落的良好值。 然后,我使用linepoins.vtx数组进行debugging,甚至不接近现实。

 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;