Math 实现光线拾取
我有一个使用directx和openGL的渲染器,还有一个3d场景。视口和窗口的尺寸相同Math 实现光线拾取,math,opengl,geometry,directx,picking,Math,Opengl,Geometry,Directx,Picking,我有一个使用directx和openGL的渲染器,还有一个3d场景。视口和窗口的尺寸相同 如何以独立于平台的方式拾取给定的鼠标坐标x和y?如果可以,在CPU上进行拾取,方法是通过鼠标指针计算眼睛的光线,并将其与模型相交 如果这不是一个选项,我会使用某种类型的ID渲染。为要拾取的每个对象指定唯一的颜色,使用这些颜色渲染对象,最后在鼠标指针下从帧缓冲区读取颜色 编辑:如果问题是如何从鼠标坐标构建光线,则需要以下内容:投影矩阵p和摄影机变换C。如果鼠标指针的坐标为(x,y),并且视口的大小为(宽度,高
如何以独立于平台的方式拾取给定的鼠标坐标x和y?如果可以,在CPU上进行拾取,方法是通过鼠标指针计算眼睛的光线,并将其与模型相交 如果这不是一个选项,我会使用某种类型的ID渲染。为要拾取的每个对象指定唯一的颜色,使用这些颜色渲染对象,最后在鼠标指针下从帧缓冲区读取颜色 编辑:如果问题是如何从鼠标坐标构建光线,则需要以下内容:投影矩阵p和摄影机变换C。如果鼠标指针的坐标为(x,y),并且视口的大小为(宽度,高度),则沿光线的剪辑空间中的一个位置为:
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
很简单,这背后的理论总是一样的 1) 将二维坐标的两倍取消投影到三维空间。(每个API都有自己的函数,但如果需要,可以实现自己的函数)。一个在最小Z,一个在最大Z 2) 使用这两个值计算从Min Z到Max Z的向量 3) 使用向量和一个点计算从Min Z到MaxZ的光线
4) 现在你有了一条射线,用它你可以做一个射线三角形/射线平面/射线交集,得到你的结果…我对DirectX的经验不多,但我确信它与OpenGL类似。你要的是GlunProject呼叫 假设您有一个有效的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,您也可以自己实现它,它的功能相对简单,如所述 首先,您需要确定鼠标单击在近平面上的位置:
unview=(P*M).inverse()=M.inverse()*P.inverse()
,其中M
是模型视图矩阵,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
好的,这个话题很老了,但它是我在这个话题上找到的最好的,它对我有一点帮助,所以我将在这里为下面的人发帖子;-) 这就是我让它工作的方式,而不必计算投影矩阵的逆:
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;
}
void应用程序::leftButtonPress(u32 x,u32 y){
GL::Viewport vp=GL::getViewport();//只需调用glGet GL_Viewport
vec3f p=vec3f::from(
((浮动)(vp.width-x)/(浮动)vp.width),
((浮动)y/(浮动)vp.高度),
1.);
//或者,vec3f p=vec3f::from(
//((浮动)x/(浮动)vp.宽度),
//((浮动)(vp.height-y)/(浮动)vp.height),
// 1.);
p*=vec3f::from(平截头体宽度,平截头体高度,1.);
p+=vec3f::from(应用程序左截头体,应用程序下截头体,0);
//现在p元素在(-1,1)中
vec3f near=p*vec3f::from(APP_trustum_near);
vec3f-far=p*vec3f::from(APP_-rustum_-far);
//世界坐标中的光线
光线={u-camera->getPos(),-(\u-camera->getBasis()*(远-近).normalize())};
_光线->设置(ray.origin,ray.dir,10000.);//这是一个调试顶点数组,用于在屏幕上查看光线
节点*节点=_场景->碰撞(光线,变换());
cout我的情况与普通光线拾取相同,但有点不对劲。我以正确的方式执行了非投影操作,但它就是不起作用。我想,我犯了一些错误,但不知道在哪里。我的matix乘法、逆乘法和vector by matix乘法都可以正常工作,我已经对它们进行了测试。
在我的代码中,我对WM_LBUTTONDOWN做出了反应。因此lParam返回[Y][X]坐标为dword中的两个字。我提取它们,然后转换为规范化空间,我检查了这部分是否也可以正常工作。当我单击左下角时,我得到的值接近-1-1,其他三个角的值都很好。然后我使用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;
@Tom,这个问题并不完全清楚。不管怎样,我已经编辑了我的答案,希望能对你有所帮助。值得注意的是,如果你使用类似DirectX的矩阵,那么乘法顺序是相反的。谢谢:)我一开始应该更清楚,但你编辑的答案正是我想要的!我不确定错误是在你的工作中还是在我的工作中,但我可以只有当我将近剪裁平面的负值用作鼠标剪裁的z坐标时,该算法才能工作。即:mouse\u clip=[float(x)*2/float(width)-1,1-float(y)*2/float(height),-1*近剪裁平面,1]
只是为了省去其他人的麻烦:这种方法只有在鼠标剪辑中的3.坐标不是0而是-接近深度时才有效。此外,对于正交矩阵,p的计算方式必须不同。@Tom如我所说,如果你不想使用glu函数,你可以自己实现它的功能,那么你只需要得到modelv查看每个的视图和投影矩阵,并获得每个的z窗口位置