前言
C++计算obb包围盒的完整代码,渲染使用的是osg。参考了如何生成OBB(OrientedboundingBox)方向包围盒,代码流程差不多,但是我这篇代码更流畅,包含调用部分,原链接中的矩阵类不适用C++,所以在本文中有的用数组代替。另文中的Math::Vec3f等同于osg::Vec3f。代码还没有时间认真整理,后面有空可以再优化一下,另数学方面不是很精通,有什么错漏欢迎指出。
第一步 计算协方差矩阵
/*计算协方差矩阵 */
std::vector<std::vector<float>> computeConvarienceMatrix(const std::vector<Math::Vec3f>& pVertices)
{
int numVertices = pVertices.size();
std::vector<Math::Vec3f> pVectors(numVertices, Math::Vec3f(0.0, 0.0, 0.0));
//Compute the average x,y,z
Math::Vec3f avg(0.0f, 0.0f, 0.0f);
for (int i = 0; i < numVertices; i++)
{
pVectors[i] = (Math::Vec3f)pVertices[i];
avg += pVertices[i];
}
avg /= numVertices;
for (int i = 0; i < numVertices; i++)
pVectors[i] -= (avg);
//Compute the covariance
std::vector<std::vector<float>> covarianceMat(3, { 0.0,0.0,0.0 });
for (int c = 0; c < 3; c++)
{
float data[3] = { 0 };
for (int r = c; r < 3; r++)
{
float acc = 0.0f;
for (int i = 0; i < numVertices; i++)
{
data[0] = pVectors[i].x();
data[1] = pVectors[i].y();
data[2] = pVectors[i].z();
acc += data[c] * data[r];
}
acc /= numVertices;
covarianceMat[c][r] = acc;
covarianceMat[r][c] = acc; //对称矩阵
}
}
return covarianceMat;
}
第二步 计算特征向量
计算出来的特征向量就是轴向量;
/*求特征向量 */
void jacobiSolver(std::vector<std::vector<float>>& matrix, std::vector<Math::Vec3f>& eVectors)
{
float eps = (float)1.0E-5;
float p, q, spq;
float cosa = 0, sina = 0; //cos(alpha) and sin(alpha)
float temp;
float s1 = 0.0f; //sums of squares of diagonal
float s2; //elements
bool flag = true; //determine whether to iterate again
Math::Vec3f eValue(0, 0, 0);
float data[3];
float t[3][3] = {
1.0f,0,0,
0,1.0f,0,
0,0,1.0f };//To store the product of the rotation matrices.
do
{
for (int i = 0; i < 2; i++)
{
for (int j = i + 1; j < 3; j++)
{
if (std::abs(matrix[i][j]) < eps)
matrix[i][j] = 0.0f;
else
{
q = std::abs(matrix[i][i] - matrix[j][j]);
if (q > eps)
{
p = (2.0f * matrix[i][j] * q) / (matrix[i][i] - matrix[j][j]);
spq = (float)std::sqrt(p * p + q * q);
cosa = (float)std::sqrt((1.0f + q / spq) / 2.0f);
sina = p / (2.0f * cosa * spq);
}
else
sina = cosa = (std::sqrt(2.0f)) / 2; //INV_SQRT_TWO 2分之根号2
for (int k = 0; k < 3; k++)
{
temp = t[k][i];
t[k][i] = temp * cosa + t[k][j] * sina;
t[k][j] = temp * sina - t[k][j] * cosa;
}
for (int k = i; k < 3; k++)
{
if (k > j)
{
temp = matrix[i][k];
matrix[i][k] = cosa * temp + sina * matrix[j][k];
matrix[j][k] = sina * temp - cosa * matrix[j][k];
}
else
{
data[k] = matrix[i][k];
matrix[i][k] = cosa * data[k] + sina * matrix[k][j];
if (k == j)
matrix[j][k] = sina * data[k] - cosa * matrix[j][k];
}
}
data[j] = sina * data[i] - cosa * data[j];
for (int k = 0; k <= j; k++)
{
if (k <= i)
{
temp = matrix[k][i];
matrix[k][i] = cosa * temp + sina * matrix[k][j];
matrix[k][j] = sina * temp - cosa * matrix[k][j];
}
else
matrix[k][j] = sina * data[k] - cosa * matrix[k][j];
}
}
}
}
s2 = 0.0f;
for (int i = 0; i < 3; i++)
{
eValue[i] = matrix[i][i];
s2 += eValue[i] * eValue[i];
}
if (std::abs(s2) < (float)1.e-5 || std::abs(1 - s1 / s2) < eps)
flag = false;
else
s1 = s2;
} while (flag);
eVectors[0].set(t[0][0], t[0][1], t[0][2]);
eVectors[1].set(t[1][0], t[1][1], t[1][2]);
eVectors[2].set(t[2][0], t[2][1], t[2][2]);
Math::Vec3f cross;
cross = eVectors[0].cross(eVectors[1]);
if (cross.dot(eVectors[2]) < 0.0f)
eVectors[2] = -eVectors[2];
}
第三步 特征向量施密特正交化
/*正交化 */
void schmidtOrthogonal(Math::Vec3f& v0, Math::Vec3f& v1, Math::Vec3f& v2)
{
v0.normalize();
v1 = v1 - ((v1 * v0) * v0);
v1.normalize();
v2 = v0.cross(v1);
}
第四步 计算中心点和半长款
有了中心点和半长宽,就能计算出包围盒的各个顶点
//计算中心点和半场宽
void computeCenterNHalflength(std::vector<Math::Vec3f>& vecVertex, osg::Vec3f& leftbottom, osg::Vec3f& rightTop, osg::Vec3f& rightbottom, osg::Vec3f& leftTop)
{
std::vector<std::vector<float>> conMatrix3f = computeConvarienceMatrix(vecVertex);
std::vector<Math::Vec3f> eVectors(3, Math::Vec3f(0, 0, 0)); //特征向量
jacobiSolver(conMatrix3f, eVectors);
schmidtOrthogonal(eVectors[0], eVectors[1], eVectors[2]);
float infinity = Base::POS_INFINITY32;
Math::Vec3f minExtents(infinity, infinity, infinity);
Math::Vec3f maxExtents(-infinity, -infinity, -infinity);
std::vector<Math::Vec3f> vertices = vecVertex;
int numVertices = vecVertex.size();
//求中心点position
Math::Vec3f position(0.0f, 0.0f, 0.0f);
for (int i = 0; i < numVertices; i++)
{
position += vertices[i];
}
position /= numVertices;
for (int index = 0; index < numVertices; index++)
{
Math::Vec3f displacement = vertices[index] - position;
//计算每个点在方向轴上的点乘作为长度
minExtents[0] = std::min(minExtents.x(), displacement.dot(eVectors[0]));
minExtents[1] = std::min(minExtents.y(), displacement.dot(eVectors[1]));
minExtents[2] = std::min(minExtents.z(), displacement.dot(eVectors[2]));
maxExtents.x() = std::max(maxExtents.x(), displacement.dot(eVectors[0]));
maxExtents.y() = std::max(maxExtents.y(), displacement.dot(eVectors[1]));
maxExtents.z() = std::max(maxExtents.z(), displacement.dot(eVectors[2]));
}
Math::Vec3f offset = maxExtents - minExtents;
offset /= (2.0f);
offset += (minExtents);
//矫正中心点
position += (eVectors[0] * (offset.x()));
position += (eVectors[1] * (offset.y()));
position += (eVectors[2] * (offset.z()));
Math::Vec3f halfExtents;
halfExtents.x() = (maxExtents.x() - minExtents.x()) / 2.0f;
halfExtents.y() = (maxExtents.y() - minExtents.y()) / 2.0f;
halfExtents.z() = (maxExtents.z() - minExtents.z()) / 2.0f;
Math::Vec3f mleftbtm = position - halfExtents.x() * eVectors[0] - halfExtents.z() * eVectors[2];
Math::Vec3f mrightbottom = position + halfExtents.x() * eVectors[0] - halfExtents.z() * eVectors[2];
Math::Vec3f mrightTop = position + halfExtents.x() * eVectors[0] + halfExtents.z() * eVectors[2];
Math::Vec3f mleftTop = position - halfExtents.x() * eVectors[0] + halfExtents.z() * eVectors[2];
leftbottom.set(mleftbtm.x(), mleftbtm.y(), mleftbtm.z());
rightTop.set(mrightTop.x(), mrightTop.y(), mrightTop.z());
rightbottom.set(mrightbottom.x(), mrightbottom.y(), mrightbottom.z());
leftTop.set(mleftTop.x(), mleftTop.y(), mleftTop.z());
}
调用代码
void updateRectBox(std::vector<Math::Vec3f> vecVertex)
{
osg::Vec3f leftbottom, rightTop, rightbottom, leftTop;
computeCenterNHalflength(vecVertex, leftbottom, rightTop, rightbottom, leftTop);
//绘制边框
osg::ref_ptr<osg::Geometry> geomLine = new osg::Geometry;
osg::ref_ptr<osg::Vec3Array> temVertex = new osg::Vec3Array();
temVertex->push_back(leftbottom);
temVertex->push_back(rightbottom);
temVertex->push_back(rightTop);
temVertex->push_back(leftTop);
geomLine->setVertexAttribArray(0, temVertex);
geomLine->setVertexAttribBinding(0, osg::Geometry::BIND_PER_VERTEX);
//颜色数组
osg::Vec4Array* vColors = new osg::Vec4Array;
vColors->push_back(COLOR_RECT);
geomLine->setColorArray(vColors);
geomLine->setColorBinding(osg::Geometry::BIND_OVERALL);
//绘制图元属性
osg::ref_ptr<osg::DrawArrays> vPri = new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP, 0, temVertex->size());
geomLine->addPrimitiveSet(vPri.get());
osg::Geode* pRectGeode = new osg::Geode;
pRectGeode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
pRectGeode->addDrawable(geomLine);
}