在三维场景中,经常会遇到需要与场景中模型进行交互的问题,最常见的方式是选择模型,对模型进行各种移动、高亮等操作,这些操作一般需要使用osg中提供的求交器来实现,在OSG中提供了如下的求交器(OSG 3.4.0版本)
包括:组求交器(IntersectorGroup),线段求交器(LineSegmentIntersector),平面求交器(PlaneIntersector),多面体求交器(PolytopeINtersector),射线求交器(RayIntersector),本文主要对平面求交器(PlaneIntersector)作一些介绍,后续会陆续补充其他求交器的内容
- 平面求交器的使用场景
平面求交器的一个常用的使用场景是求与某些模型的交线,包括轮廓线、截面线(横断面、纵断面等),如下图所示,求平面与地形的交线:
- 使用方式
在使用平面求交器前,需要使用一个平面构造出求交器,查看它的构造函数:
osg::Plane的构造方式有多种,简单来说就是立体解析几何中关于平面的构造方式都可以(比如平面的一般式、点法式、三点式等),之后可以传入第二个参数boundingPolytope,这个参数是用来对平面进行一些约束的。我们构造的平面事实上是无穷大的,利用这个参数就可以约束平面只在某个区域内与场景中的对象求交。这个参数的构造同样是有许多面构成。当构造完成之后就可以使用最常见的求交方式完成,如下所示:
osg::ref_ptr<osgUtil::PlaneIntersector> intersector = new osgUtil::PlaneIntersector(plane, boundingPolytope);
osgUtil::IntersectionVisitor intersectionVisitor;
intersectionVisitor.reset();
intersectionVisitor.setIntersector(intersector.get());
sceneNode->accept(intersectionVisitor);
在完成之后就可以从intersector对象中获取交到的内容了
osgUtil::PlaneIntersector::Intersections& intersections = intersector->getIntersections();
可以从交到的许多Intersection中得到交到的节点polyline,一般来说,求交得到的点坐标是在局部坐标之下的,需要经过变换得到世界坐标中的值,在intersection中已经记录了这个矩阵,于是可以通过下面的变化将所有坐标转到世界坐标系中:
osgUtil::PlaneIntersector::Intersections::iterator itr;
for (itr = intersections.begin();
itr != intersections.end();
++itr)
{
osgUtil::PlaneIntersector::Intersection& intersection = *itr;
if (intersection.matrix.valid())
{
// OSG_NOTICE<<" transforming "<<std::endl;
// transform points on polyline
for (auto pitr = intersection.polyline.begin();
pitr != intersection.polyline.end();
++pitr)
{
*pitr = (*pitr) * (*intersection.matrix);
}
// matrix no longer needed.
intersection.matrix = 0;
}
}
另一个需要注意的地方:交到的polyline事实上坐标是混乱的,如果将这些点直接练成一段LINES_STRIP,会发现这些线交错在一起,因此在求交完成之后还需要我们对这些交到的结果进行处理,至于具体的处理方式依赖于实际的应用,本文中提供的求与地面交线的方式很简单,可以将这些交点按x值从小到大或者从大到小依次排列,剔除里面重复的x值即可(使用std::set就可以)
- 示例
本文提供一个示例,简单的求出平面与一个由VPB生成地形的交线,主要代码如下:
//用来为顶点排序
bool compareVec3(const osg::Vec3d& p1, const osg::Vec3d& p2)
{
return p1.x() < p2.x();
}
void computeIntersection()
{
if (_controlPoints.size() < 1)
{
//获取求交平面的第一个点
_firstPointX = x;
_firstPointY = y;
_firstPointZ = z;
}
else
{
//绘制求交线
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array;
osg::Plane plane;
osg::Polytope boundingPolytope;
//地形的法线(VPB生成的地形,如果是地球表面需要计算)
osg::Vec3d upVector(0.0, 0.0, 1.0);
//求交平面的起始点和终点
osg::Vec3d endPoint = osg::Vec3(x, y, z);
osg::Vec3d startPoint = osg::Vec3(_firstPointX,_firstPointY,_firstPointZ);
// 构造求交平面(点法式)
osg::Vec3d planeNormal = (endPoint - startPoint) ^ upVector;
planeNormal.normalize();
plane.set(planeNormal, startPoint);
// 第一个约束面
osg::Vec3d startPlaneNormal = upVector ^ planeNormal;
startPlaneNormal.normalize();
boundingPolytope.add(osg::Plane(startPlaneNormal, startPoint));
// 第二个约束面
osg::Vec3d endPlaneNormal = planeNormal ^ upVector;
endPlaneNormal.normalize();
boundingPolytope.add(osg::Plane(endPlaneNormal, endPoint));
//构造平面求交器
osg::ref_ptr<osgUtil::PlaneIntersector> intersector = new osgUtil::PlaneIntersector(plane, boundingPolytope);
osgUtil::IntersectionVisitor intersectionVisitor;
intersector->setRecordHeightsAsAttributes(true);
intersectionVisitor.reset();
intersectionVisitor.setIntersector(intersector.get());
_viewer->getCamera()->accept(intersectionVisitor);
osgUtil::PlaneIntersector::Intersections& intersections = intersector->getIntersections();
//对结果进行变换(局部坐标转到世界坐标)
osgUtil::PlaneIntersector::Intersections::iterator itr;
for (itr = intersections.begin();
itr != intersections.end();
++itr)
{
osgUtil::PlaneIntersector::Intersection& intersection = *itr;
if (intersection.matrix.valid())
{
for (auto pitr = intersection.polyline.begin();
pitr != intersection.polyline.end();
++pitr)
{
*pitr = (*pitr) * (*intersection.matrix);
}
// matrix no longer needed.
intersection.matrix = 0;
}
}
//剔除重复的x值以及对交点按x值进行排序
std::set<osg::Vec3d, decltype(compareVec3)*>
myVec3Array(compareVec3);
osgUtil::PlaneIntersector::Intersections::iterator itrs;
for (itrs = intersections.begin();
itrs != intersections.end();
++itrs)
{
osgUtil::PlaneIntersector::Intersection& intersection = *itrs;
for (auto pitr = intersection.polyline.begin();
pitr != intersection.polyline.end();
++pitr)
{
myVec3Array.insert(*pitr);
}
}
for (auto pitr = myVec3Array.begin(); pitr != myVec3Array.end(); ++pitr)
{
vertexArray->push_back(*pitr));
}
osg::ref_ptr<osg::Vec4Array> colorArray = new osg::Vec4Array();
colorArray->push_back(osg::Vec4(255,0,0,0));
geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth();
lineWidth->setWidth(2.0f);
geometry->getOrCreateStateSet()->setAttributeAndModes(lineWidth, osg::StateAttribute::ON);
geometry->setVertexArray(vertexArray);
osg::ref_ptr<osg::DrawArrays> drawArray = new osg::DrawArrays(osg::DrawArrays::LINE_STRIP, 0, vertexArray->size());
geometry->addPrimitiveSet(drawArray);
_geode->addChild(geometry);
_geode->dirtyBound();
}
}