习惯了绘图软件的坐标显示功能,想让点云软件也能实现不管鼠标在场景的哪里移动,总能知道坐标,即使不太精确也没关系。对于三维地形模型来说,随便一个求交器就能轻松实现,对点云就例外了。有了概略坐标就能估算相对位置和距离,可以满足大部分需求,但在点云配准时,必须要有精确坐标作为参考点。本文给出了使用osg获取点云的概略坐标和精确坐标的方法。
1、坐标显示
osgUtil中内置了线段求交器LineSegmentIntersector和多面体求交器PolytopeIntersector,但没有针对点的求交器。这两个默认的求交器只能对封闭区域求交,如三维地形。其原理都是射线求交。
基于OpenGL的反馈模式,可以利用屏幕坐标和深度值反算出三维坐标值,但这个值随着视距的远近其精度不一,越远精度越低。有深度值,意味着屏幕的该像素被渲染,故称之为像素求交。在给定较大的像素阈值(5pixel)的情况下,不需要太严格就能命中点云,并显示反馈的近似三维坐标。
2、精确获取点云坐标
有了上一步的粗略坐标,可以找到候选点集,再对该点集遍历,计算屏幕坐标,与实际屏幕坐标最接近的点云便是目标点云。
获取候选点集的方法有:NodeVisitor或者Intersector。这里练习一下Intersector的用法。
从LineSegmentIntersector派生一个PointLineSegmentIntersector点线求交器,在其内部对geom应用PrimitiveFunctor的模板派生类TemplatePrimitiveFunctor,语法如osg::drawable::accept(PrimitiveFunctor & )。TemplatePrimitiveFunctor 是一个能够访问点、线段、面片、点数组的类,它模拟openGL的绘制函数,功能上类似NodeVisitor,只是处理的粒度更细了,名字也改成了Functor,并增加了模板和多继承。
class PrimitiveFunctor { public: virtual ~PrimitiveFunctor() {} virtual void setVertexArray(unsigned int count,const Vec2* vertices) = 0; virtual void setVertexArray(unsigned int count,const Vec3* vertices) = 0; virtual void setVertexArray(unsigned int count,const Vec4* vertices) = 0; virtual void setVertexArray(unsigned int count,const Vec2d* vertices) = 0; virtual void setVertexArray(unsigned int count,const Vec3d* vertices) = 0; virtual void setVertexArray(unsigned int count,const Vec4d* vertices) = 0; virtual void drawArrays(GLenum mode,GLint first,GLsizei count) = 0; virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices) = 0; virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) = 0; virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) = 0; virtual void begin(GLenum mode) = 0; virtual void vertex(const Vec2& vert) = 0; virtual void vertex(const Vec3& vert) = 0; virtual void vertex(const Vec4& vert) = 0; virtual void vertex(float x,float y) = 0; virtual void vertex(float x,float y,float z) = 0; virtual void vertex(float x,float y,float z,float w) = 0; virtual void end() = 0; void useVertexCacheAsVertexArray() { setVertexArray(_vertexCache.size(),&_vertexCache.front()); } std::vector<Vec3> _vertexCache; bool _treatVertexDataAsTemporary; }; |
template<class T> class TemplatePrimitiveFunctor : public PrimitiveFunctor, public T |
//T的样子,需实现下面几个被TemplatePrimitiveFunctor调用的函数 class PrimitiveHandler { public: void operator()( const osg::Vec3& v1, bool treatVertexDataAsTemporary ); void operator()( const osg::Vec3& v1, const osg::Vec3& v2, bool treatVertexDataAsTemporary ); void operator()( const osg::Vec3& v1, const osg::Vec3& v2, const osg::Vec3& v3,bool treatVertexDataAsTemporary ); void operator()( const osg::Vec3& v1, const osg::Vec3& v2, const osg::Vec3& v3, const osg::Vec3& v4, bool treatVertexDataAsTemporary ); } |
针对本主题,在向T传入鼠标反馈点,与顶点比较,距离在一定阈值的加入候选交集表。
有了候选交集表后,就可以从camera获取相机-视图-投影-窗口的变换矩阵matrixVPW,从而将模型坐标变换为窗口坐标,然后再与鼠标的窗口坐标比较,找出最近的点云。计算matrixVPW的方法为:
osg::Matrix matrixVPW(cam->getViewMatrix() * cam->getProjectionMatrix()); if (cam->getViewport()) matrixVPW.postMult(cam->getViewport()->computeWindowMatrix()); |