C++计算三维空间点的obb包围框,包含osg调用

前言

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);
    }
OBB(Oriented Bounding Box,有向包围)是一种用于包围物体的几何形状,它既能够保持物体的形状,又能够提供方便的碰撞检测和投影。以下是使用C++实现OBB包围算法的基本步骤: 1. 进行物体的三维建模,并获得每个顶的位置信息。 2. 找到物体的最小和最大,以确定物体的中心。 3. 对于每个顶,将其与物体中心的位置向量相减,将这个向量存储在一个数组中。 4. 计算出物体的协方差矩阵,使用数组中的向量作为输入。 5. 对协方差矩阵进行特征值分解,以获得物体的主方向和大小。 6. 构建OBB包围的八个顶,通过将物体中心与主方向上的最小和最大边界分别相加和相减得到。 以下是一个简单的C++代码示例,用于计算OBB包围的8个顶: ```cpp #include <iostream> #include <vector> #include <Eigen/Dense> using namespace Eigen; void computeOBB(const std::vector<Vector3f>& points, Matrix3f& axes, Vector3f& center, Vector3f& extents) { int numPoints = points.size(); // Compute the center point of the object center.setZero(); for (int i = 0; i < numPoints; ++i) center += points[i]; center /= numPoints; // Compute the covariance matrix of the object Matrix3f covMat; covMat.setZero(); for (int i = 0; i < numPoints; ++i) { Vector3f v = points[i] - center; covMat += v * v.transpose(); } covMat /= numPoints; // Compute the eigenvalues and eigenvectors of the covariance matrix SelfAdjointEigenSolver<Matrix3f> eig(covMat); Vector3f eigenValues = eig.eigenvalues().real(); Matrix3f eigenVectors = eig.eigenvectors().real(); // Set the axes and extents of the OBB axes = eigenVectors.transpose(); extents = eigenValues.sqrt(); // Compute the 8 corners of the OBB std::vector<Vector3f> corners(8); for (int i = 0; i < 8; ++i) { Vector3f corner; for (int j = 0; j < 3; ++j) corner(j) = (i & (1 << j)) ? extents(j) : -extents(j); corners[i] = center + axes * corner; } } ``` 这个函数的输入是一个三维的向量数组,表示物体的顶位置。函数输出物体的OBB包围的8个顶,以及旋转矩阵和大小向量。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值