### OpenSceneGraph 中的 OBB 包围盒实现
在计算机图形学中,包围盒是一种用于加速碰撞检测和可见性测试的技术。OBB( Oriented Bounding Box )相对于 AABB(Axis-Aligned Bounding Box),能够更紧密地贴合物体形状,因此常被用来优化复杂的几何体操作。
#### 背景介绍
OpenSceneGraph (OSG) 是一种开源的高性能场景图库,广泛应用于三维可视化领域。为了支持高效的碰撞检测和裁剪功能,OSG 提供了多种类型的包围盒结构,其中包括轴向对齐包围盒(AABB)[^1] 和方向包围盒(OBB)。然而,默认情况下,OSG 的 `BoundingSphere` 或 `BoundingBox` 类主要用于简单的边界表示,而未直接提供现成的 OBB 实现方法。这意味着开发者需要自行扩展来满足特定需求。
以下是基于 OSG 构建自定义 OBB 包围盒的一种可能方案:
---
#### 自定义 OBB 结构设计
首先定义一个类来描述 OBB 的基本属性,通常包括中心位置、半径以及旋转矩阵或法向量集合等参数:
```cpp
#include <osg/Vec3>
#include <osg/BoundingBox>
class OBB {
public:
osg::Vec3 center; // Center of the box.
osg::Matrix rotation; // Rotation matrix defining orientation.
osg::Vec3 halfSize; // Half-size extents along each axis.
OBB(const osg::Vec3& c, const osg::Matrix& r, const osg::Vec3& h)
: center(c), rotation(r), halfSize(h) {}
bool intersectsWith(const OBB& other) const;
};
```
上述代码片段展示了如何创建一个基础版本的方向包围盒对象[^2] 。其中包含了三个核心成员变量分别代表盒子的位置(`center`) , 方位 (`rotation`) 及大小范围(`halfSize`)。
---
#### 计算节点树中的 OBB 数据
当处理复杂模型时,可以通过遍历整个场景图并收集所有子节点的相关信息来动态构建合适的 OBB 表示形式。下面是一个递归函数的例子,它会沿着指定路径读取每个叶子级别的几何数据,并将其转换为相应的局部空间坐标系下的顶点列表以便进一步分析:
```cpp
void computeLocalVertices(osg::Node* node, std::vector<osg::Vec3>& vertices){
if(auto geode = dynamic_cast<osg::Geode*>(node)){
for(auto drawable : geode->getDrawables()){
auto geometry = dynamic_cast<osg::Geometry*>(drawable);
if(geometry && geometry->getVertexArray()){
osg::Vec3Array* array = static_cast<osg::Vec3Array*>(geometry->getVertexArray());
for(unsigned int i=0;i<array->size();i++)
vertices.push_back((*array)[i]);
}
}
}else{
for(unsigned int i=0;i<node->getNumChildren();i++)
computeLocalVertices(node->getChild(i),vertices);
}
}
```
此部分逻辑负责提取目标区域内的全部有效顶点样本集合作为基础素材参与后续运算过程[^3].
---
#### 使用 PCA 方法求解最佳拟合平面
主成分分析(Principal Component Analysis,PCA)算法可以帮助找到一组正交基底使得原始多维分布投影后的方差最大化从而得到最优近似结果。具体来说就是先统计所有采样点关于质心坐标的协方差阵再通过特征分解获取主导方向作为新的参考框架完成最终定位调整工作流程如下所示:
```cpp
std::tuple<osg::Vec3, osg::Matrix> calculateOrientations(const std::vector<osg::Vec3>& points){
// Compute centroid and covariance matrix...
Eigen::MatrixXd covMat(3,3);
Eigen::VectorXd meanPoint(3);
for(int dim=0;dim<3;++dim){
double sumDim=0.;
for(auto &p:points){sumDim+=p[dim];}meanPoint(dim)=sumDim/(double)(points.size());
for(int row=0;row<=dim;++row){
double prodSum=0.;
for(auto &p:points){prodSum += p[row]*p[dim];}
covMat(row,dim)=(prodSum-(meanPoint(row)*meanPoint(dim)*(double)(points.size())))/(double)(points.size()-1);
if(row<dim)covMat(dim,row)=covMat(row,dim);
}
}
// Perform eigenvalue decomposition on covariance matrix to get principal axes...
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(covMat);
Eigen::Vector3d evals = es.eigenvalues();
Eigen::Matrix3d evecs = es.eigenvectors();
// Convert back into OSG types ...
osg::Vec3 xAxis(evecs.col(0).data()), yAxis(evecs.col(1).data()), zAxis(evecs.col(2).data());
osg::Matrix rot(xAxis.x(),yAxis.x(),zAxis.x(),
xAxis.y(),yAxis.y(),zAxis.y(),
xAxis.z(),yAxis.z(),zAxis.z(),
0., 0., 0.);
return {osg::Vec3(meanPoint.data()),rot};
}
```
该段程序实现了从给定的一系列离散点集中推导出适配它们的最佳定向矩形框姿态的过程[^4].
---
#### 集成至现有渲染管道
最后一步则是将新生成好的 OBB 描述符应用回实际绘图环节当中去。这一般涉及到修改原有材质状态或者额外附加辅助显示层等内容。例如我们可以借助于 LineSet 来勾勒轮廓边缘线条效果达到直观展示目的:
```cpp
osg::ref_ptr<osg::Group> createDebugLinesForOBB(const OBB& obb){
osg::ref_ptr<osg::LineSet> lines(new osg::LineSet);
osg::ref_ptr<osg::Vec3Array> coords(new osg::Vec3Array);
// Define corners based off local space dimensions transformed by global pose...
osg::Vec3 cornerOffsets[]={
{-obb.halfSize.x(),-obb.halfSize.y(),-obb.halfSize.z()},
{+obb.halfSize.x(),-obb.halfSize.y(),-obb.halfSize.z()},
{+obb.halfSize.x(),+obb.halfSize.y(),-obb.halfSize.z()},
{-obb.halfSize.x(),+obb.halfSize.y(),-obb.halfSize.z()},
{-obb.halfSize.x(),-obb.halfSize.y(),+obb.halfSize.z()},
{+obb.halfSize.x(),-obb.halfSize.y(),+obb.halfSize.z()},
{+obb.halfSize.x(),+obb.halfSize.y(),+obb.halfSize.z()},
{-obb.halfSize.x(),+obb.halfSize.y(),+obb.halfSize.z()}
};
for(auto &offset:cornerOffsets)(*coords)+=(obb.rotation * offset + obb.center);
// Connect pairs appropriately forming wireframe representation...
unsigned short indices[]={0,1,2,3,0,
4,5,6,7,4,
0,4,1,5,2,6,3,7};
lines->setVertexArray(coords.get());
lines->setColorArray(new osg::Vec4Array({{1.f,.8f,.2f,1.f}}));
lines->setColorBinding(osg::Geometry::BIND_OVERALL);
lines->addPrimitiveSet(new osg::DrawElementsUInt(GL_LINE_LOOP,sizeof(indices)/sizeof(*indices),(unsigned*)indices));
osg::ref_ptr<osg::Geode> debugGeo=new osg::Geode;
debugGeo->addDrawable(lines.release());
return new osg::Group(debugGeo.release());
}
```
这样就完成了完整的 OBB 创建与调试视图呈现链条搭建[^5].
---