OSG求交器(一)面求交器PlaneIntersector

在三维场景中,经常会遇到需要与场景中模型进行交互的问题,最常见的方式是选择模型,对模型进行各种移动、高亮等操作,这些操作一般需要使用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();
	}
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值