您的位置:首页 > 其它

鼠标点击选择三维图形中一个点

2014-04-02 17:15 127 查看
这些天在学习OpenGL,做一个绘制机器人轨迹的mfc软件,遇到了一系列问题。最棘手之一就是点击屏幕选择三维图形中的点了。查阅资料找到一种解决办法:绘制一条垂直屏幕的直线,与三维图形的第一个交点就是要选择的点了。也叫着拣选射线法:代码如下:
class Point3f;
class LineSegment;
LineSegment GetSelectionRay(int mouse_x, int mouse_y) {
// 获取 Model-View,Projection 矩阵 & 获取Viewport 视区
GLdouble modelview[16];
GLdouble projection[16];
GLint viewport[4];
glGetDoublev (GL_MODELVIEW_MATRIX, modelview);
glGetDoublev (GL_PROJECTION_MATRIX, projection);
glGetIntegerv (GL_VIEWPORT, viewport);
GLdouble world_x, world_y, world_z;
// 获取近裁剪面上的交点
gluUnProject( (GLdouble) mouse_x, (GLdouble) mouse_y, 0.0,
modelview, projection, viewport,
&world_x, &world_y, &world_z);
Point3f near_point(world_x, world_y, world_z);
// 获取远裁剪面上的交点
gluUnProject( (GLdouble) mouse_x, (GLdouble) mouse_y, 1.0,
modelview, projection, viewport,
&world_x, &world_y, &world_z);
Point3f far_point(world_x, world_y, world_z);
return LineSegment(near_point, far_point);
}


但是这种方法存在着一个弊端:

就是图形如果发生了旋转,平移,或者缩放,就不可用了,解决方案是:对屏幕坐标模型进行完全一致的转换,比如旋转平移缩放等,修改后的代码如下:

LineSegment COpenGL::GetSelectionRay(int mouse_x, int mouse_y) {

// 获取 Model-View,Projection 矩阵 & 获取Viewport 视区

GLint    viewport[4];
GLdouble modelview[16];
GLdouble projection[16];

mouse_y=screenHeight-mouse_y;//屏幕高度减鼠标位置就是viewport中的y
glPushMatrix();

// 变换要绘图函数里的顺序一样,否则坐标转换会产生错误
glTranslatef(xtran,ytran,ztran); // Move Left 1.5 Units And Into The Screen 6.0
glScalef(m_zoom,m_zoom,m_zoom);
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
glRotatef(zrot,0.0f,0.0f,1.0f);

glGetIntegerv(GL_VIEWPORT, viewport); // 得到的是最后一个设置视口的参数
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);

glPopMatrix();
GLdouble world_x, world_y, world_z;

// 获取近裁剪面上的交点
gluUnProject( (GLdouble) mouse_x, (GLdouble) mouse_y, 0.0,
modelview, projection, viewport,
&world_x, &world_y, &world_z);
Point3f near_point(world_x, world_y, world_z);
// 获取远裁剪面上的交点
gluUnProject( (GLdouble) mouse_x, (GLdouble) mouse_y, 1.0,
modelview, projection, viewport,
&world_x, &world_y, &world_z);
Point3f far_point(world_x, world_y, world_z);
return LineSegment(near_point, far_point);

}

注:mouse_x mouse_y是鼠标点击的坐标,world_x,world_y,world_z是OpenGL坐标,返回值是一个贯穿视境的直线。screenHeight是窗口高度,因为OpenGL中原点和mfc中的原点位置不同,一个在左下角,一个左上角,所以需要进行转换。

获得了直线以后,还要判断图像上面的点哪个在这条直线上,就要运用立体几何知识,主要有以下几种方法:

1、三角形两边之和大于第三边,可以用我们获得的直线两端点分别与之做差求长度,两个长度相加如果和直线的长度差别不大,则说明点在线上;AC+BC=AB或者AC+AB=BC 或者AB+BC=AC

2、利用向量的方法,在同一条直线上的点,矢量平行,AC//BC, (Ax-Cx)/(Bx-Cx)=(Ay-Cy)/(By-Cy)=(Az-Cz)/(Bz-Cz)

3、使用线段的方程,求出AB的方程,搜索满足方程的点,

4、……

第二种方法的代码如下,

在Linesegment中增加函数bool ifHasPoint(float x,float y,float z,float delta);//delta是允许的误差,delta越小越精确

bool LineSegment::ifHasPoint(float x,float y,float z,float delta)
{
if(
((aPoint->x-x)/(bPoint->x-x)-(aPoint->y-y)/(bPoint->y-y)<delta)

&&((aPoint->x-x)/(bPoint->x-x)-(aPoint->z-z)/(bPoint->z-z)<delta)
)
return true;
return false;
}


在调用时遍历所有点找到返回true的点;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐