1、默认的相机和全部模型求交的方式spa
1.1 传统的模型与屏幕点求交的方法以下:3d
1 osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa); 2 if ( viewer ) 3 { 4 osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector = 5 new osgUtil::LineSegmentIntersector(osgUtil::Intersector::WINDOW, ea.getX(), ea.getY()); 6 osgUtil::IntersectionVisitor iv( intersector.get() ); 7 viewer->getCamera()->accept( iv ); 8 9 if ( intersector->containsIntersections() ) 10 { 11 osgUtil::LineSegmentIntersector::Intersection result = *(intersector->getIntersections().begin()); 12 doUserOperations( result ); 13 } 14 }
从上面能够知道经过点击屏幕上一点其实就是构造一根线,而后将这条线和场景中的模型进行碰撞检测,会生成一个结果集,经过遍历结果集就能够处理相交的点,默认结果集中第一个点就是最近的相交点code
可是在osg和点云结合的程序中,经过线和点云求交是几乎没有交点的,因此咱们须要重写与点云模型的求交器,下面的PointIntersector求交器就是派生自osgUtil::LineSegmentIntersector,而后重写求交逻辑的点云模型求交器,主要参考的就是OpenSceneGraph Cookbook,下面就是点云模型和屏幕点求交的用法:orm
1.二、OSG中与点云模型选点的方法:对象
1 osg::ref_ptr<PointIntersector> intersector = new PointIntersector(osgUtil::Intersector::WINDOW, ea.getX(), ea.getY()); 2 osgUtil::IntersectionVisitor iv(intersector.get()); 3 viewer->getCamera()->accept(iv); 4 5 if (intersector->containsIntersections()) 6 { 7 //osg::Vec3d worldpoint = CRealInteractionUtil::getNeartestPoint(intersector, osg::Vec2f(ea.getX(), ea.getY()), transformMatrix*vpw); 8 PointIntersector::Intersection result = *intersector->getIntersections().begin();
//....
9 }
以上经过osg里面重写的求交器能够与场景中的点云模型进行求交,而后求得相交点的坐标,求交的原理大概就是PointIntersector求交器首先构造一个管状空间,此空间参数能够设置,默认半径是两米,因此求交的结果集是屏幕点生成线,而后经过线扩充成圆柱,根据此圆柱和点云模型求得结果集,结果集会包括屏幕点附近的点,此时如何知道结果集中哪一个点时认为认为的选中点呢?默认结果集中第一个结果就是最近的相交点blog
2、指定的模型节点和相机求交的方式get
经过以上方式也能够求得和点云求交选中点的坐标,但若是是单独与osg中的点云节点求交,此时就不能经过屏幕点构造求交器了,缘由以下:it
camera中的点都是基于屏幕的二维坐标,用屏幕点构造求交器也是二维坐标,因此二者能够进行求交得出碰撞点io
特定节点中的点多是基于世界坐标系的三维点,因此求交器也得是世界坐标系的三维点来构造的,若是用屏幕点构造求交器去求交就会致使没结果,由于坐标系没有统一块儿来ast
因此须要先构造三维场景中的一个开始点和一个结束点构造求交器,以下:
1 osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa); 2 if (!viewer) 3 { 4 return false; 5 } 6 osg::Matrixd Matrix_V = viewer->getCamera()->getViewMatrix();//观察矩阵-将对象由世界坐标变换为像机坐标 7 osg::Matrixd Matrix_P = viewer->getCamera()->getProjectionMatrix();//投影矩阵-投影到屏幕 8 osg::Matrixd Matrix_W = viewer->getCamera()->getViewport()->computeWindowMatrix();//视口矩阵-将投影坐标变换到二维视口 9 osg::Matrixd vwp = Matrix_V*Matrix_P*Matrix_W; //求得 世界到屏幕的矩阵 10 osg::Matrixd inverseVPW = osg::Matrixd::inverse(vwp); 11 osg::Vec3d world_sta = osg::Vec3d(ea.getX(), ea.getY(), 0)*inverseVPW;//屏幕坐标转成世界坐标 12 osg::Vec3d world_end = osg::Vec3d(ea.getX(), ea.getY(), 1)*inverseVPW; 13 osg::ref_ptr<PointIntersector> intersector = new PointIntersector(world_sta, world_end); 14 osgUtil::IntersectionVisitor iv(intersector.get()); 15 m_pDataManagerSingleton->getVectorNode()->accept(iv); 16 if (!intersector->containsIntersections()) 17 { 18 return false; 19 } 20 osg::Vec3d worldpoint = CRealInteractionUtil::getNeartestPoint(intersector, osg::Vec2f(ea.getX(), ea.getY()), vwp); 21 PointIntersector::Intersection result = *(intersector->getIntersections().begin());
1 osg::Vec3d CRealInteractionUtil::getNeartestPoint(PointIntersector* intersector, osg::Vec2f& curScreenXY, osg::Matrixd& vpw) 2 { 3 vector<osg::Vec3d> points; 4 for (osgUtil::LineSegmentIntersector::Intersections::iterator iter = intersector->getIntersections().begin(); iter != intersector->getIntersections().end(); iter++) 5 { 6 points.push_back(iter->getWorldIntersectPoint()); 7 } 8 float distance = 10000; 9 osg::Vec3d curNeartestPoint; 10 for (int i = 0; i < points.size(); i++) 11 { 12 osg::Vec3d tempPoint = points[i] * vpw; 13 osg::Vec2f tempScreenXY = osg::Vec2f(tempPoint[0], tempPoint[1]); 14 float lenth = (tempScreenXY - curScreenXY).length2(); 15 if (lenth < distance) 16 { 17 distance = lenth; 18 curNeartestPoint = points[i]; 19 } 20 } 21 return curNeartestPoint; 22 }
以上代码就是经过三维中的一个开始点和一个结束点构造一个柱状空间与点云模型求交,此时会求出不少个相交的点结果,若是咱们默认也认为结果集的第一个就是正确的点,那就错了,开始我也是如此使用的,可是始终以为会选偏,就是选不许,返回的第一个点老是在屏幕点附近,而不是正中心的那个点,即便我把半径设为很小,仍是会偏,由于求交的结果集是在一个圆柱里面的,都是杂乱无章的,咱们是没法判断哪一个是正确的那个点的,此时咱们能够将结果集的三维点先投影到屏幕中,经过与屏幕点的点再次求一个最近点,此时这个点的三维坐标就是正确的点云求交点了