《实时碰撞检测算法技术》读书笔记(三):计算逼近包围球

计算所有顶点的AABB获取一个逼近包围球

第一步:获取各坐标轴上的6个端点(这里不理解,“各坐标轴上的6个端点”是什么?),可以选择其中间隔距离最远的两个顶点,这两个顶点的中心即为球心,且两顶点距离的一半则是球体半径。

第二步:全部顶点再次循环。对于位于当前球体外部的所有顶点,该步骤将更新一个新球体包含原球体以及外部顶点。即相对于原球心,新球体的直径将延伸至外部顶点。

 

第一步的代码MostSeparatedPointsOnAABB()SphereFromDistantPoints()如下:

//Compute indices to the two most separated points of the (up to) six points
//defining the AABB encompassing the point set. Return these as min and max.
void MostSeparatedPointsOnAABB(int &min, int &max, Point pt[], int numPts) {
    //First find most extreme points along principal axes
    int minx = 0, maxx = 0, miny = 0, maxy = 0, minz = 0, maxz = 0;
    for(int i = 1; i < numPts; i++) {
        if(pt[i].x < pt[minx].x) minx = i;
        if(pt[i].x > pt[maxx].x) maxx = i;

        if(pt[i].y < pt[miny].y) miny = i;
        if(pt[i].y > pt[maxy].y) maxy = i;


        if(pt[i].z < pt[minz].z) minz = i;
        if(pt[i].z > pt[maxz].z) maxz = i;
    }
    //Compute the squared distances for the three pairs of points
    float dist2x = Dot(pt[maxx] - pt[minx], pt[maxx] - pt[minx]);
    float dist2y = Dot(pt[maxy] - pt[miny], pt[maxy] - pt[miny]);
    float dist2z = Dot(pt[maxz] - pt[minz], pt[maxz] - pt[minz]);
    //Pick the pair (min, max) of points most distant
    min = minx;
    max = maxx;
    if(dist2y > dist2x && dist2y > dist2z) {
        max = maxy;
        min = miny;
    }
    if(dist2z > dist2x && dist2z > dist2y) {
        max = maxz;
        min = minz;
    }
}

void SphereFromDistantPoints(Sphere &s, Point pt[], int numPts) {
    //Find the most separated point pair defining the encompassing AABB
    int min, max;
    MostSeparatedPointsOnAABB(min, max, pt, numPts);
    //Set up sphere to just encompass these two points
    s.c = (pt[min] + pt[max]) * 0.5f;
    s.r = Dot(pt[max] - s.c, pt[max] - s.c);
    s.r = Sqrt(s.r);
}

第二步:

//Given Sphere s and Point p, update s (if needed) to just encompass p
void SphereOfSphereAndPt(Sphere &s, Point &p) {
    //Compute squared distance between point and sphere center
    Vector d = p - s.c;
    float dist2 = Dot(d, d);

    //Only update s if point p is outside it
    if(dis2 > s.r * s.r) {
        float dist = Sqrt(dist2);
        float newRadius = (s.r + dist) * 0.5f;
        float k = (newRadius - s.r) / dist;
        s.r = newRadius;
        s.c += d*k;
    }
}


注意,代码中的d不是单位向量,故要除以dist使其变成单位向量


计算逼近包围球全部代码如下:

void RitterSphere(Sphere &s, Point pt[], int numPts) {
    //Get sphere encompassing two approximately most distant points
    SphereFromDistantPoints(s, pt, numPts);

    //Grow sphere to include all points
    for(int i = 0; i < numPts; i++) {
        SphereOfSphereAndPt(s, pt[i]);
    }
}

以下是一个由2D三角形计算求得的逼近包围圆


最大离散方向包围球

利用统计方法分析顶点云并确定最大离散方向。若给定该方向,将距离最远的两点投影至该方向上,即可得到球心和半径。


利用协方差矩阵可以获取该轴。对于协方差矩阵特征向量和特征值有以下关系:若特征向量为最大特征向量,则特征值为沿该轴的顶点数据具有的最大方差值;若为最小特征向量,则特征值为沿该轴的顶点数据具有的最小方差值。

关于计算协方差矩阵的方法以及其他相关计算(如Jacobi方法)在此不加赘述。

void RigenSphere(Sphere &eigSphere, Point pt[], int numPts)
{
    Matrix33 m,v;
    //compute the covariance matrix m
    CovarianceMatrix(m, pt, numPts);
    //Decompose it into eigenvectors (in v) and eigenvalues(in m)
    Jacobi(m,v);

    //Find the compose with largest magnitude eigenvalue(largest spread)
    Vector e;
    int maxc = 0;
    float maxf, maxe = Abs(m[0][0]);
    if((maxf = Abs(m[1][1])) > maxe) maxc = 1, maxe = maxf;
    if((maxf = Abs(m[2][2])) > maxe) maxc = 2, maxe = maxf;
    e[0] = v[0][maxc];
    e[1] = v[1][maxc];
    e[2] = v[2][maxc];

    //Find the most exrteme points along direction ‘e’
    int imin, imax;
    ExtremePointsAlongDirection(e, pt, numPts, &imin, &imax);
    Point minpt = pt[imin];
    Point maxpt = pt[imax];

    float dist = Sqrt(Dot(maxpt - minpt, maxpt - minpt));
    eigSphere.r = dist*0.5f;
    eigSphere.c = (minpt + maxpt)*0.5f;
}

改进后的包围球代码如下:

void RitterEigenSphere(Sphere &s, Point pt[], int numPts) {
    //Start with sphere from maximum spread
    EigenSphere(s, pt, numPts);

    //Grow shpere to include all points
    for(int i = 0; i < numPts; i++)
        SphereOfSphereAndPt(s, pt[i]);
}


迭代修正包围球

上面的逼近包围球不甚让人满意,可以考虑减少半径后再次包围,像这样迭代多次求得更可靠的包围球。

代码如下:

//迭代修正包围球
void RitterIterative(Sphere &s, POINT pt[], int numPts) {
	int ITER_NUM = 8;
	RitterSphere(s, pt, numPts);
	Sphere s2 = s;

	while(ITER_NUM -- ) {
		s2.r *= 0.95f;
		for(int i = 0; i < numPts; i++) {
			SphereOfSphereAndPt(s2,pt[i]);
		}
		if(s2.r < s.r) s = s2;
	}
}

修正后的包围球:



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介  《实时碰撞检测算法技术》详细阐述了与碰撞检测问题相关的高效解决方案及相应的数据结构和算法,主要包括:碰撞检测系统中的设计问题、数学和几何学入门、包围体、基本图元测试、层次包围技术、空间划分、BSP树层次结构、凸体算法、基于GPU的碰撞检测、数值健壮性、几何健壮性以及优化操作。另外,《实时碰撞检测算法技术》还提供了相应的算法、代码以及伪代码,以帮助读者进一步理解计算方案的实现过程。  《实时碰撞检测算法技术》适合作为高等院校计算机及相关专业的教材和教学参考书,也可作为相关开发人员的自学教材和参考手册。第1章 概述1.1 内容概览1.2 关于本书的代码第2章 碰撞检测系统中的设计问题2.1 碰撞算法的设计因素2.2 应用程序中对象的表达方式2.3 查询类型2.4 环境模拟参数2.5 性能2.6 健壮性2.7 实现与使用的简洁性2.8 小结第3章 数学和几何学入门3.1 矩阵3.2 坐标系统和顶点3.3 向量3.4 质心坐标3.5 直线、光线和线段3.6 平面和半空间3.7 多边形3.8 多面体3.9 凸包计算3.10 域3.11 Minkowski和与Minkowski差3.12 小结第4章 包围体4.1 BV期望特征4.2 轴对齐包围盒4.3 Spheres体4.4 方向包围盒4.5 扫掠体4.6 半空间相交体4.7 其他类型的包围体4.8 小结第5章 基本图元测试5.1 最近点计算5.2 图元测试5.3 直线、光线和有向线段的相交测试5.4 其他类型的测试5.5 动态相交测试5.6 小结第6章 层次包围技术6.1 层次结构设计问题6.2 层次结构的构建策略6.3 层次结构的遍历6.4 包围体层次结构示例6.5 合并包围体6.6 高效的树型表达方式及遍历6.7 通过缓存机制改善查询6.8 小结第7章 空间划分第8章 BSP树层次结构第9章 凸体算法第10章 基于GPU的碰撞检测第11章 数值健壮性第12章 几何健壮性第13章 优化操作参考文献
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值