如何在自己的项目中使用BVH(层次包围盒)
使用BVH的场景
BVH可用在碰撞检测、光线追踪等场景,但由于项目的局限性,本文仅针对碰撞检测做出一定的介绍。
怎么确定BVH的使用方向
众所周知,在Opencascade中,模型是由TopoDS和AIS_Shape共同组成的,TopoDS控制模型的拓扑结构,AIS_Shape控制模型的显示样式。基于这个前提下,我先给出结论:需要用布尔运算计算出的新模型,就用TopoDS相关的布尔运算。如果是想做碰撞检测、光追,就用AIS_Shape中的Mesh(三角网格)计算
在碰撞检测中,操作TopoDS和AIS_Shape的区别
TopoDS是模型的根本,所以官方提供了一般的TopoDS算法,比如BRepExtrema_DistShapeShape
和BRepAlgoAPI_Common
等方法。这些方法虽然可以计算出模型的距离或者新模型,但计算的时间太长了,根本无法满足项目的要求。
简单来说,如果要计算两个模型的距离,就算BRepExtrema_DistShapeShape
只需要几秒钟的计算,但在实时碰撞检测的情况下,这个时间是远远无法满足软件要求的。这也是为什么需要引入AIS_Shape(Mesh计算)
模型 | 在碰撞检测中的计算时间 |
---|---|
TopoDS_Shape | 方法多样,但很多计算多余,且耗时 |
AIS_Shape(Mesh) | 计算超级快,我的项目里至少提速50倍,但用起来不方便,没有现成的BVH方法直接操作自己的模型 |
BVH操作AIS_Shape(三角网格)的具体方法
我这里一直说AIS_Shape,只是想泛指渲染的数据,我想大部分新手一开始接触Opencascade都对AIS_Shape有所了解,而不是其他专业的概念
构建好BVH容器
// Define BVH Builder
opencascade::handle <BVH_LinearBuilder <Standard_Real, 3> > aLBuilder = new BVH_LinearBuilder <Standard_Real, 3>();
// Create the ShapeSet
opencascade::handle <BVH_BoxSet <Standard_Real, 3, std::vector<BVH_Vec3d>> > aTriangleBoxSet[2]; // 我需要计算两个Shape的距离,所以是声明两个BoxSet
上述代码中,容器是通过智能指针构建的,这也是很重要的一点。虽然也可以直接声明一个局部变量,但后续移动遍历、拷贝变量的时候会特别耗时,会导致算法变得很慢。
提取TopoDS每个面的网格信息
上文说到BVH操作Mesh不太方便,也是因为这个问题。我们需要手动的提取出TopoDS的网格信息,比如:
TopoDS_Shape A = BRepBuilderAPI_Transform(tool.toolHeads[toolNum]->Shape(), tool.toolHeads[toolNum]->Transformation()).Shape();
TopTools_IndexedMapOfShape aMapShapes;
TopExp::MapShapes(A, TopAbs_FACE, aMapShapes);
for (Standard_Integer iS = 1; iS <= aMapShapes.Extent(); ++iS)
{
TopoDS_Face face = TopoDS::Face(aMapShapes(iS));
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(face, aLoc);
const int aNbTriangles = aTriangulation->NbTriangles();
//...
}
上述代码中,有2个要注意的点:
TopoDS
要经过BRepBuilderAPI_Transform
移动,移动的依据是渲染出来的AIS_Shape,因为Opencascade中,AIS_Shape的移动不会带着TopoDS跑,如果想要准确的三角网格信息,需要把TopoDS和AIS_Shape的位置关联起来,再用BRep_Tool::Triangulation
- 需要遍历每个面,将每个面进行三角网格划分。
将网格数据存入BVH_BoxSet
我们需要先声明几个变量,这几个变量的作用是初始化BVH_Box。
// Define BVH Builder
opencascade::handle <BVH_LinearBuilder <Standard_Real, 3> ><