原文有35页,容我慢慢翻译,第一部分翻译了10页
reference:《Real-Time Rendering》
目录
|
17 前言 17.1 和射线的碰撞检测 17.2 使用BSP树的动态碰撞检测 17.3 一般层次的碰撞检测 17.3.2 不同层之间的碰撞检测 17.3.3 代价函数 17.4 OBB树 17.5 多重物体碰撞检测系统 17.5.1 广阶段的碰撞检测 17.5.2 总结 17.6 更多样的话题 17.6.1 及时碰撞检测 17.6.2 距离查询 17.6.3 可变形的模型 17.6.4 碰撞响应 17.6.5 基于GPU的碰撞检测 |
把东西击倒,尤其是在一些很突出的角度上,是一件极大的乐事。
——乔治·桑塔耶拿
碰撞检测是许多计算机图形应用基础而又重要的部分。它在虚拟操控,CAD/CAM,计算机动画,物理建模,游戏,飞行和驾驶模拟,机器自动,路径和运动规划(公差验证),装配(??),以及几乎所有虚拟现实的模拟中扮演着重要的角色。由于它广泛的使用,碰撞检测仍然是人们不断扩展研究的课题。
碰撞检测是适用于碰撞处理的一部分,碰撞处理可以分为三个部分:碰撞检测,碰撞计算,碰撞响应。碰撞检测的结果通常是一个布尔量,它告知了是否存在两个或者更多的物体相撞;而碰撞计算求出物体间碰撞的实际交点;最终,碰撞响应决定针对两个物体的碰撞,该采取什么样的行动。
在第一部分我们讨论简单而又相当快的碰撞检测技术。主要的想法是用线的集合来近似一个复杂的物体。这些线条将和场景中的基本体检测交点。这一技术在游戏中非常常见。另外一个近似的方法将会在第二部分介绍,在这里我们使用了BSP树来表达场景,一个角色可能是由圆柱体来表达的。然而,所有的物体不总是能够被近似表达为直线或者圆柱体,有一些应用也许需要更精确的检测。
想象一下,例如,我们想要决定一个三维的手撞上(拿起)一个三维的茶杯,两个物体都是由三角形网格表达的。这将如何有效地完成呢?当然,手的每个三角形都需要和茶杯的每个三角形进行相交检测。但是在这个例子里,如果两个物体隔得很远,这个算法应当快速地发现这一点,然后不再进行繁重的计算。哪怕物体已经靠的足够近了,大量的检测依然是不高效的。我们需要一个能够快速完成这个例子的算法。很容易想出碰撞检测更复杂的情节,下图显示了这样的一个复杂例子。
左图中,计算的是许多障碍物之间以及与环境的碰撞检测和响应;右图中,则更加混乱。(左图来自Crytek,右图来自Valve Corp)
第三部分描述了通用的,分层的包围盒碰撞检测算法。一个基于OBBs的特殊实现将在第四部分介绍。下面的特点应用于大多数碰撞检测系统:
○ 对于有大量多边形的模型,在相隔很远和距离很近时,它们都能完成交互速率(interactive rates???)
○ 它们能处理多边形集合。I.e,一般的没有凸面或有效邻接信息的约束的多边形模型。
○ 模型能进行刚体运动,i.e.旋转加上平移,甚至更一般类型的变形。
○ 它们提供有效的包围盒,试图把几何体集合包得更紧。小的包围盒提升了算法在判断两个物体是否相交时的表现。包围盒的创建速度也应当是较快的。
一个场景可能包含几十甚至上百个运动物体,一个好的碰撞检测系统将能很好地应对这一点。如果一个场景包含n个运动物体和m个静止物体,那么一个普通的方法将会在每一帧计算
个物体的检测。第一项对应着静止物体和运动物体碰撞的次数,下一项对应着运动物体之间的碰撞次数。最原始的方法随着m和n的增加,复杂度快速提升。在这种情况下我们需要更聪明的做法,这将在第五部分介绍。这样的一种方法通常使用这样的算法,首先检测所有可能存在的物体对物体的碰撞,然后再使用如第四部分介绍的OBB树算法来处理它们。
讨论完一系列碰撞检测之后,会有简短的部分介绍一些混杂的话题。及时的碰撞检测是一个在常数时间内进行近似碰撞检测的算法,它将在第七部分介绍。有时候我们期望得到两个物体的最短距离,这个话题将在研究了一些可变性物体后介绍。最终,碰撞响应将被简略地提及。
必须指出的是,碰撞检测的代价估计是非常困难的,因为算法是对实际碰撞场景敏感的,在所有的例子中,没有哪一种算法的表现是最好的。
为了加速这一相交检测,我们可以使用常用于计算机图形学中加速的一个技术——多层次表示法(hierarchical representation)。这个场景可以使用沿坐标轴的BSP来表达(它和K-D树一样)。BSP树在14章中有介绍,在那里它被用于视锥体裁剪。BSP树同样也可以用于加速相交检测。根据这个场景中使用了哪种类型的基元,我们使用不同的射线对象相交检测技术(看16章)。BSP树并不是唯一的用于快速找到相交的表达方式,一个包围盒层同样也是有用的。
和标准的光线追踪里我们要找到离光线最近的物体不同,我们实际上需要的是沿着射线最后的交点,有时距离可能是负的。为了避免处理射线时在两端搜寻,测试射线的原点将会后移,直到它处在环境中的包围盒外。实际上,这仅仅意味着,射线不再是从距离0处开始,而是从一个负距离开始,并且处在包围盒之外。为了处理更一般的情况,例如在隧道里开车并且和屋顶做碰撞检测,我们不得不从两个方向进行搜索。
标准的BSP树可以高效地用于线段的测试。一个线段可以表示为一个点(粒子)从p0移动到p1。这里也许会有一些相交,第一个交点(如果有的话)表达了点和BSP树中物体的相交。注意到,在这个例子中,BSP树是沿表面对齐的,而不是沿坐标系对齐的。这也就是说,树中的每个平面与场景中的墙、地板或者天花板是对应的。我们可以很容易把一个点扩展成一个从p0到p1半径为r的球体。我们不再测试线段与BSP树中结点对应的平面,而是将每个平面都沿着平面法线方向移动了距离r。这个平面调节后的排序在下图中有进一步阐述。这将针对每个碰撞查询动态产生,所以一个BSP树可以被用于任意大小的球体。假设一个平面为那么调节后的平面为
其中r的符号取决于你选择了平面的哪一侧来继续碰撞检测的搜索。假设角色在平面正半空间一侧,i.e.当
时,我们从d中减去半径r。注意这时负的半空间被认为是实心(solid)的,一个角色无法立足的地方。
对于左边,从上方看有一些几何体(蓝色),它的BSP如中间图所示。为了检测这个树与圆心在p的圆形,BSP树将向外生长圆的半径的长度,然后就可以改为检测点p和生长后的bsp树。这在右图中展示。注意角落应当是圆滑的,所以这是这个算法的一个近似。
一个球体无法很好地近似游戏中的角色。角色顶点的凸包或者包围着角色的圆柱体会有更好的效果。为了使用这些不同的包围盒,平面方程中的d需要修改成不同的样子。为了检测对BSP树移动的点集合的凸包S,我们把下面这个方程的标量加到平面方程中的d值上。
再一次指出,这个减法符号意味着角色处在平面的正半空间。点p0可以是任意适合作为参照点的点。对于球体而言,我们通常选择球体的中心。对于一个角色来说,我们通常使用接近脚的一个点,或者也许是肚脐上的一个点。有时候这个选择可以简化方程(正如选在球体中心那样)。我们和BSP树检测的就是点P0,。对于动态查询,也就是说,在一帧内角色从一个点走向了另一个点,点p0将被作为这个线段的起始点。假设角色在一帧内行走了向量w,那么这个线段的终点就是p1 = p0 + w。
圆柱体也许是更有用的,因为它检测起来更快,而且依然能够很好地近似游戏中的角色。但是,这将会涉及到更多平面方程的调整值的衍生。在这个算法中,我们一般做的是把BSP树的包围盒(球体,凸包,圆柱体,在这个例子中)的测试改为调整的BSP树中的点P0的测试。这基本上和闵可夫斯基和(Minkowski Sum)是一样的。然后,为了把这个扩展到运动物体上,我们把点p0换为从p0到目的点p1的线段的检测。
我们对一个圆柱体扩展这样一个测试,它的性质在下图中显示。参照点p0处在圆柱底面的中心。图b显示了我们想要解决的:检测圆柱体和平面。在图c中,我们移动平面使得它刚好与圆柱体接触。然后我们计算从接触点到点p0的距离e。距离e将用于图d中将平面移动到新的位置。e的值是在每一帧对每个平面动态计算的。事实上,首选被计算的是从p0到点t的一个向量,t也就是平面和圆柱的交点。这在图c中显示。接下来,这样计算e:
现在,还剩下t需要计算。t的z分量(i.e.圆柱坐标方向)非常简单:如果nz > 0 , 那么, i.e.p0的z分量。否则,
。tz的值对应着圆柱体的底部和顶部。 如果nx和ny的值都是0(e.g.对于地面或者天花板),那么我们使用圆柱盖上的任一顶点。一个自然的选择是(tx,ty)
= (px,py),也就是圆柱盖的中心。否则,对于一个不垂直的n,下面的选择将会给出圆柱盖边缘上的一点:
也就是说,我们把平面法线放置在x-y平面上,标准化它,然后放大r倍使其放置在圆柱体的边缘。
在这种方法中可能会有误差。下图显示了这样一个例子。正如看见的那样,我们可以通过使用一些额外的斜平面来解决这一问题。实际上,我们计算出了两个相邻平面的“外”角,如果这个角度大于90,我们会插入一个额外的平面。这一方法是为了提高本该是圆角的近似度。在下图中,可以看到普通的BSP树和用斜面扩展的BSP树的区别。斜平面当然可以提高准确度,但是它不会减少任何错误。
在左图中,右边的球体正确地碰撞,而左边的球体则过早地检测到了碰撞;在右图中,这通过增加了一个额外的斜平面来解决,它事实上不对应着任何真实的几何体。正如我们所见,碰撞检测在使用了这样的一个平面后已经正确了。
下面给出了这个碰撞检测算法的伪代码。它调用了BSP树的根N,它有N个正的孩子和N个负的孩子,线段由p0和p1定义。注意撞击的点(如果存在的话)将会返回一个叫做的全局变量:
HitCheckBSP(N,p0,p1)
returns ( {TRUE, FALSE} );
if(not isSolidCell(N)) return FALSE;
else if ( isSolidCell(N))
pimpact = p0
return true;
end
hit = FALSE;
if(clipLineInside(N shift out, p0, p1, &w0, &w1))
hit = hitCheckBSP(N.negativechild, w0, w1);
if(hit) p1 = pimpact
end
if(clipLineOutside ( N shift in, p0, p1, &w0, &w1))
hit = hitcheckBSP(N.posivtivechild, w0, w1);
end
return hit;
对于左图,由p0p1定义的线段被由法线n定义的平面裁剪。平面的动态调整由虚线显示。函数clipLineInside和clipLineOutside返回由w0和w1定义的线段。注意到三条线段都本应该有相同的y坐标值,但是为了更清晰的表达它们被显示成这样。对于右图,显示了一个例子,它解释了为什么直线应当被裁剪成左边的样子。BSP树中的结点A同时属于左右两个三角形。所以,有必要移走它两个方向的平面。
Melax已显示这个算法相比起不使用动态调整平面的算法要昂贵2.5到3.5倍。这是因为在找合适的调整时有过多的计算。哪怕这样的慢速问题可能看起来很严肃,Melax指出这在环境中表现得并不那么差。一帧通常花费33,00us(30fps)来计算,而它最复杂的碰撞查询花费66us.这一策划的有点在于我们只需要单一的BSP树就能检测所有角色和对象。而其替代算法则需要为每个不同的半径以及对象类型存储不同的BSP树。
[图形学] 《Real-Time Rendering》碰撞检测(一)
原文:http://blog.csdn.net/zju_fish1996/article/details/52354657