如何生成OBB(OrientedboundingBox)方向包围盒


如何生成OBB(OrientedboundingBox)方向包围盒

一、包围盒简介:

          包围盒是一个简单的几何空间,里面包含着复杂形状的物体。为物体添加包围体的目的是快速的进行碰撞检测或者进行精确的碰撞检测之前进行过滤(即当包围体碰撞,才进行精确碰撞检测和处理)。包围体类型包括球体、轴对齐包围盒(AABB)、有向包围盒(OBB)、8-DOP以及凸壳。

包围盒广泛地应用于碰撞检测,比如射击、点击、相撞等,每一个物体都有自己的包围盒。因为包围盒一般为规则物体,因此用它来代替物体本身进行计算,会比直接用物体本身更加高效和简单。


         目前广泛应用的是AABB和OBB包围盒,其中AABB包围盒更常见。因为它的生成方法很简单,因它与坐标轴是对齐的。但它也有不足,它不随物体旋转,可以看出当图中的老虎沿着Z轴方向站立时,AABB包围盒还和老虎比较贴合,但当老虎转了一个角度后,AABB包围盒便增加了较大的空隙,对于较精确的碰撞检测效果不太好。这时就需要OBB包围盒,它始终沿着物体的主成分方向生成最小的一个矩形包围盒,可以随物体旋转,可用于较精确的碰撞检测。

二、OBB包围盒生成思路:

OBB的生成思路简单来说就是根据物体表面的顶点,通过PCA(主成分分析)获得特征向量,即OBB的主轴。

主成分分析是一种通过正交变换,将一组可能相关的变量集合变换成一组线性不相关的变量集合,即主成分。

在这里就要引入协方差矩阵的概念,如果学过线性代数就会知道。协方差表示的使两个变量之间的线性相关程度。协方差越小则表示两个变量之间越独立,即线性相关性小。


通过协方差的计算公式,可以得到协方差矩阵。


主对角线的元素表示变量的方差。主对角线的元素较大则表示强信号。非主对角线的元素表示变量之间的协方差。较大的非对角线元素表示数据的畸变。

为了减小畸变,可以重新定义变量间的线性组合,将协方差矩阵对角化。协方差矩阵的的元素是实数并且对称。

协方差矩阵的特征向量表示OBB包围盒的方向。大的特征值对应大的方差,所以应该让OBB包围盒沿着最大特征值对应的特征向量的方向。

例:

有如下一些点:

(3.7, 1.7), (4.1, 3.8), (4.7, 2.9), (5.2, 2.8), (6.0, 4.0), (6.3, 3.6), (9.7, 6.3), (10.0, 4.9), (11.0, 3.6), (12.5, 6.4)


计算得到协方差矩阵:


对角化协方差矩阵:


于是可以得到特征向量,也就是OBB的坐标轴:


再计算得到OBB的中心点(8.10,4.05),长宽的半长度分别为4.96 和1.49。即可一画出二维平面若干点生成的OBB包围盒。

关于讲解详细地可以查阅协方差矩阵和PCA分析的相关文章。我参考了如下两篇:

1、OBB generate via PCA:  https://hewjunwei.wordpress.com/2013/01/26/obb-generation-via-principal-component-analysis/

2、PCA的数学原理:    http://blog.csdn.net/xiaojidan2011/article/details/11595869

三、代码实现:

思路:

首先计算出协方差矩阵,用雅可比迭代法求出特征向量,再进行施密特正交化。

1、计算协方差矩阵

  1. <span style="font-size:14px;">public Matrix3 computeCovarianceMatrix(Vector3[] pVertices,int numVertices) throws CloneNotSupportedException{  
  2.         Matrix3 covariance=new Matrix3();     
  3.         Vector3[] pVectors=new Vector3[numVertices];  
  4.           
  5.         //Compute the average x,y,z  
  6.         Vector3 avg=new Vector3(0.0f,0.0f,0.0f);  
  7.         for(int i=0;i<numVertices;i++){  
  8.             pVectors[i]= (Vector3)pVertices[i].clone();  
  9.             avg.add(pVertices[i]);  
  10.         }  
  11.         avg.divide(numVertices);  
  12.         for(int i=0;i<numVertices;i++)  
  13.             pVectors[i].sub(avg);  
  14.           
  15.         //Compute the covariance   
  16.         for(int c=0;c<3;c++)  
  17.         {  
  18.             float data[]=new float[3];  
  19.             for(int r=c;r<3;r++)  
  20.             {  
  21.                 covariance.setColRow(c, r, 0.0f);  
  22.                 float acc=0.0f;  
  23.                 //cov(X,Y)=E[(X-x)(Y-y)]  
  24.                 for(int i=0;i<numVertices;i++)  
  25.                 {  
  26.                     data[0]=pVectors[i].x;  
  27.                     data[1]=pVectors[i].y;  
  28.                     data[2]=pVectors[i].z;  
  29.                     acc+=data[c]*data[r];  
  30.                 }  
  31.                   
  32.                 acc/=numVertices;  
  33.                 covariance.setColRow(c, r, acc);  
  34.                 //symmetric  
  35.                 covariance.setColRow(r, c, acc);  
  36.             }  
  37.               
  38.         }  
  39.         return covariance;  
  40.           
  41.     }</span>  

2、雅可比迭代法求出特征向量:

  1. public void jacobiSolver(Matrix3 matrix,float[] eValue ,Vector3[] eVectors ){  
  2.     float eps1=(float)0.00001;  
  3.     float eps2=(float)0.00001;  
  4.     float eps3=(float)0.00001;  
  5.       
  6.     System.out.println("----------the 1.e-5----------");  
  7.     System.out.println(eps1);  
  8.       
  9.     float p,q,spq;  
  10.     float cosa = 0,sina=0;  //cos(alpha) and sin(alpha)  
  11.     float temp;  
  12.     float s1=0.0f;    //sums of squares of diagonal  
  13.     float s2;          //elements  
  14.       
  15.     boolean flag=true;  //determine whether to iterate again  
  16.     int iteration=0;   //iteration counter  
  17.       
  18.     Vector3 mik=new Vector3() ;//used for temporaty storage of m[i][k]  
  19.     float[] data=new float[3];  
  20.     Matrix3 t=new Matrix3();//To store the product of the rotation matrices.  
  21.     t=Matrix3.identity();  
  22.     do{  
  23.           
  24.         iteration++;  
  25.           
  26.         for(int i=0;i<2;i++)  
  27.         {  
  28.             for(int j=i+1;j<3;j++)  
  29.             {  
  30.                 if(Math.abs(matrix.getColRow(j, i))<eps1)  
  31.                     matrix.setColRow(j, i, 0.0f);  
  32.                 else{  
  33.                     q=Math.abs(matrix.getColRow(i, i)-matrix.getColRow(j, j));  
  34.                     if(q>eps2){  
  35.                         p=(2.0f*matrix.getColRow(j, i)*q)/(matrix.getColRow(i, i)-matrix.getColRow(j, j));  
  36.                         spq=(float)Math.sqrt(p*p+q*q);  
  37.                         cosa=(float)Math.sqrt((1.0f+q/spq)/2.0f);  
  38.                         sina=p/(2.0f*cosa*spq);  
  39.                     }  
  40.                     else  
  41.                         sina=cosa=INV_SQRT_TWO;  
  42.                   
  43.                 for(int k=0;k<3;k++){  
  44.                     temp=t.getColRow(i, k);  
  45.                     t.setColRow(i, k, (temp*cosa+t.getColRow(j, k)*sina));  
  46.                     t.setColRow(j, k, (temp*sina-t.getColRow(j, k)*cosa));  
  47.                 }  
  48.                 for(int k=i;k<3;k++)  
  49.                 {  
  50.                     if(k>j){  
  51.                         temp=matrix.getColRow(k, i);  
  52.                         matrix.setColRow(k, i, (cosa*temp+sina*matrix.getColRow(k, j)));  
  53.                         matrix.setColRow(k, j, (sina*temp-cosa*matrix.getColRow(k, j)));  
  54.                     }  
  55.                     else  
  56.                     {  
  57.                         data[k]=matrix.getColRow(k, i);  
  58.                         matrix.setColRow(k, i, (cosa*data[k]+sina*matrix.getColRow(j, k)));  
  59.                           
  60.                         if(k==j)  
  61.                             matrix.setColRow(k, j, (sina*data[k]-cosa*matrix.getColRow(k, j)));  
  62.                     }  
  63.                 }  
  64.                 data[j]=sina*data[i]-cosa*data[j];  
  65.                   
  66.                 for(int k=0;k<=j;k++)  
  67.                 {  
  68.                     if(k<=i)  
  69.                     {  
  70.                         temp=matrix.getColRow(i, k);  
  71.                         matrix.setColRow(i, k, (cosa*temp+sina*matrix.getColRow(j, k)));  
  72.                         matrix.setColRow(j, k, (sina*temp-cosa*matrix.getColRow(j, k)));  
  73.                           
  74.                     }  
  75.                     else  
  76.                         matrix.setColRow(j, k, (sina*data[k]-cosa*matrix.getColRow(j, k)));  
  77.                       
  78.                 }  
  79.                 }  
  80.                 }  
  81.         }  
  82.         s2=0.0f;  
  83.         for(int i=0;i<3;i++)  
  84.         {  
  85.             eValue[i]=matrix.getColRow(i, i);  
  86.             s2+=eValue[i]*eValue[i];  
  87.         }  
  88.           
  89.         if(Math.abs(s2)<(float)1.e-5||Math.abs(1-s1/s2)<eps3)  
  90.             flag=false;  
  91.         else  
  92.             s1=s2;  
  93.     }while(flag);  
  94.       
  95.     eVectors[0].x=t.data[0];  
  96.     eVectors[0].y=t.data[1];  
  97.     eVectors[0].z=t.data[2];  
  98.     eVectors[1].x=t.data[3];  
  99.     eVectors[1].y=t.data[4];  
  100.     eVectors[1].z=t.data[5];  
  101.     eVectors[2].x=t.data[6];  
  102.     eVectors[2].y=t.data[7];  
  103.     eVectors[2].z=t.data[8];  
  104.       
  105.     System.out.println("----------------eVector without transform-----------");  
  106.     for(int i=0;i<3;i++)  
  107.         printVector(eVectors[i]);  
  108.     System.out.println();  
  109.       
  110.     mik.x=data[0];  
  111.     mik.y=data[1];  
  112.     mik.z=data[2];  
  113.     Vector3 cross=new Vector3();  
  114.     Vector3.cross(cross, eVectors[0], eVectors[1]);  
  115.     if((Vector3.dot(cross, eVectors[2])<0.0f))  
  116.             eVectors[2].inverse();  
  117.       
  118. }  

3、施密特正交化:

  1. //Schmidt orthogonal  
  2.     public void schmidtOrthogonal(Vector3 v0,Vector3 v1,Vector3 v2){  
  3.         v0.normalize();  
  4.         System.out.println("------------v0 normalized--------------");  
  5.         printVector(v0);  
  6.           
  7.         //v1-=(v1*v0)*v0;  
  8.         System.out.println("------------v1 dot v0--------------");  
  9.         System.out.println(v1.dot(v0));  
  10.         v1.sub(Vector3.multiply(v1.dot(v0),v0));  
  11.         v1.normalize();  
  12.         Vector3.cross(v2,v0, v1);  
  13.     }  

4、计算半长度

将各点的(X,Y,Z)坐标投影到计算出的坐标轴上,position由累加所有点再求均值得到。求出center和半长度。

  1.             <pre name="code" class="java">                float infinity=Float.MAX_VALUE;  
  2. Vector3 minExtents =new Vector3(infinity,infinity,infinity);  
  3. Vector3 maxExtents=new Vector3(-infinity,-infinity,-infinity);  
  4.   
  5. for(int index=0;index<numVertices;index++)  
  6. {  
  7.     Vector3 vec=vertices[index];  
  8.     Vector3 displacement =Vector3.sub(vec, position);  
  9.       
  10.     minExtents.x=min(minExtents.x, Vector3.dot(displacement, orientation.getRowVector(1)));  
  11.     minExtents.y=min(minExtents.y, Vector3.dot(displacement, orientation.getRowVector(2)));  
  12.     minExtents.z=min(minExtents.z, Vector3.dot(displacement, orientation.getRowVector(3)));  
  13.       
  14.     maxExtents.x=max(maxExtents.x, Vector3.dot(displacement, orientation.getRowVector(1)));  
  15.     maxExtents.y=max(maxExtents.y, Vector3.dot(displacement, orientation.getRowVector(2)));  
  16.     maxExtents.z=max(maxExtents.z, Vector3.dot(displacement, orientation.getRowVector(3)));  
  17.       
  18. }  
  19. //offset = (maxExtents-minExtents)/2.0f+minExtents   
  20. Vector3 offset=Vector3.sub(maxExtents, minExtents);  
  21. offset.divide(2.0f);  
  22. offset.add(minExtents);  
  23.   
  24. System.out.println("-----------the offset-------------");  
  25. printVector(offset);  
  1. <span style="white-space:pre">        </span>//中心点  
  2.         position.add(orientation.getRowVector(1).multiply(offset.x));  
  3.         position.add(orientation.getRowVector(2).multiply(offset.y));  
  4.         position.add(orientation.getRowVector(3).multiply(offset.z));  
  5.         //半长度  
  6.         halfExtents.x=(maxExtents.x-minExtents.x)/2.0f;  
  7.         halfExtents.y=(maxExtents.y-minExtents.y)/2.0f;  
  8.         halfExtents.z=(maxExtents.z-minExtents.z)/2.0f;  


这是通过随机点生成的包围盒:






评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值