球与三角形的动态碰撞测试
DX为我们提供了射线与三角形式碰撞检测函数
CONST D3DXVECTOR3 * p0,
CONST D3DXVECTOR3 * p1,
CONST D3DXVECTOR3 * p2,
CONST D3DXVECTOR3 * pRayPos,
CONST D3DXVECTOR3 * pRayDir,
FLOAT * pU,
FLOAT * pV,
FLOAT * pDist
);
但是如果我们用一个半径一定的球体按一定方向发射, 如何求出球将会什么时侯碰到三形形的什么位置, 碰撞点的法向如何, 球将会向何方继续运动, 或者结果球不会与三形形相撞. 作为连续碰撞检测来讲, 球体与三角形的连续碰撞检测是一个最基本的最核心的算法. 而连续碰撞检测算法也较我们常用的静态相交测试要费事的多. 在两篇外文文档中管这种检测叫"sweep test".
而介绍这种碰撞检测的资料十分少. 网上仅能找到的有价值的是下面两篇资料.
1) Generic Collision Detection for Games Using Ellipsoids
Paul Nettle
June 13, 2000
(Revised October 5, 2000)
2)Improved Collision detection and Response
Kasper Fauerby
kasper@peroxide.dk
http://www.peroxide.dk
25th July 2003
第一篇文档是最老的一篇文档, 我当时按其内容实现了其算法, 但是我在测试算法时总是能感觉球体在和三角形边缘碰撞时计算不正确, 因此做了很多检测, 最后确定是第一篇文档的算法本身出了问题, 从网上相关信息搜索来看仅能看到GameDev论坛上有人提出相似的凝问(http://www.gamedev.net/community/forums/topic.asp?key=featart&uid=1026&forum_id=35&Topic_Title=General+Collision+Detection+for+Games+Using+Ellipsoids), 其它信息没了. 经过不断搜索相关资料终于发现了上面的第二篇文档, 上面也说明是第一篇文档的增强形算法,
解决了上篇文档中的三形形边缘和球体的碰撞检测错误.
至此经过我按第二篇文档的方法测试, 证明其算法正确.
这儿简单说明下算法原理, 具体可以阅读上面两篇文档.
为了和DX的射线和三角形相交的函数配套,我们仿照它的函数原型写出我们的函数原型
Vec3D & pos,
Vec3D & normal)
最基本的相交算法, 球体和平面的相交算法. 见下图:
(图片取自上面的文档)
我们利用这个算法可以求出如果球体和三形形平面想撞,并且相撞点在三形形内部的话,其碰撞点就是我们最终要求的点。
求点是否在三角形内部有好几种方法。
一是检测所有点是否在三角形各条边同一侧
{
Vec3D ab = b - a;
Vec3D d1 = pos1 - a;
Vec3D d2 = pos2 - a;
Vec3D cross1;
Vector3Cross(ab, d1, cross1);
Vec3D cross2;
Vector3Cross(ab, d2, cross2);
} // 判断是否点位于各条边的同一侧 BOOL pointInTriangle( const Vec3D & pos, const Vec3D & posA, const Vec3D & posB, const Vec3D & posC)
{ if ( SameSide(pos, posA, posB, posC) && SameSide(pos, posB, posA, posC) && SameSide(pos, posC, posA, posB) ) return TRUE; return FALSE;
}
二是利用面积测试点是事位于三角形内部
{
Vec3D d1 = pos2 - pos1;
Vec3D d2 = pos3 - pos1;
Vec3D cross;
Vector3Cross(d1, d2, cross);
} // 利用面积测试点是事位于三角形内部 BOOL pointInTriangle2( const Vec3D & pos, const Vec3D & posA, const Vec3D & posB, const Vec3D & posC)
{ double triArea = triangleArea(posA, posB, posC); double area = triangleArea(pos, posA, posB);
area += triangleArea(pos, posA, posC);
area += triangleArea(pos, posB, posC); double epsilon = 0.0001 ; if (fabs(triArea - area) < epsilon)
{ return TRUE;
} return FALSE;
}
如果碰撞点不在三角形内部,我们就要检查碰撞点会不会在三角形的三个顶点和三条边上,并且取出最早的一个碰撞点做我们的返回值.
球体和点的碰撞检测和球体和线段的碰撞撞检测我们都采用解一元二次方程的方法来解决.
一元二次方程我们用如下函数来计算
{ // Check if a solution exists float determinant = b * b - 4.0f * a * c; // If determinant is negative it means no solutions. if (determinant < 0.0f ) return false ; // 有一段代码中添加了这块 if (a == 0.0f ) return false ; // calculate the two roots: (if determinant == 0 then x1==x2 let us disregard that slight optimization) float sqrtD = sqrt(determinant); float r1 = ( - b - sqrtD) / ( 2 * a); float r2 = ( - b + sqrtD) / ( 2 * a); // Sort so x1 <= x2 if (r1 > r2)
{ float temp = r2;
r2 = r1;
r1 = temp;
} // Get lowest root: if (r1 > 0 && r1 < maxR)
{
root = r1; return true ;
} // It is possible that we want x2 - this can happen if x1 < 0 if (r2 > 0 && r2 < maxR)
{
root = r2; return true ;
} // No (valid) solutions return false ;
}
下面是球体和点的碰撞检测函数
B = 2 * (velocity * (basePoint - p))
C = (p - basePoint).length()^2 - 1 */ BOOL sphereIntersectPoint( const Vec3D & p, const Vec3D & start, const Vec3D & end, float radius, float & t,
Vec3D & collisionPoint)
{ // some commonly used terms: Vec3D velocity = end - start;
Vec3D base = start; float velocitySquaredLength = velocity.lengthSquared(); float a, b, c; // Params for equation float newT; bool foundCollison = false ; // For each vertex or edge a quadratic equation have to // be solved. We parameterize this equation as // a*t^2 + b*t + c = 0 and below we calculate the // parameters a,b and c for each test. // Check against points: a = velocitySquaredLength;
b = 2.0f * (Vector3Dot(velocity, ( base - p)));
c = (p - base ).lengthSquared() - radius * radius; if (getLowestRoot(a, b, c, t, newT))
{
t = newT;
foundCollison = true ;
collisionPoint = p;
} return foundCollison;
}
下面是球体和线段的碰撞检测函数
Vec3D & collisionPoint)
{ bool foundCollison = false ;
Vec3D velocity
= end - start; float velocitySquaredLength = velocity.lengthSquared();Vec3D edge
= p2 - p1;Vec3D baseToVertex = p1 - start; float edgeSquaredLength = edge.lengthSquared(); float edgeDotVelocity = Vector3Dot(edge, velocity); float edgeDotBaseToVertex = Vector3Dot(edge, baseToVertex); float a, b, c; float newT; // Calculate parameters for equation a = edgeSquaredLength * - velocitySquaredLength + edgeDotVelocity * edgeDotVelocity;
b = edgeSquaredLength * ( 2 * Vector3Dot(velocity, baseToVertex)) - 2.0f * edgeDotVelocity * edgeDotBaseToVertex;
c = edgeSquaredLength * (radius * radius - baseToVertex.lengthSquared()) + (edgeDotBaseToVertex * edgeDotBaseToVertex); // Does the swept sphere collide against infinite edge? if (getLowestRoot(a,b,c, t, newT))
{ // Check if intersection is within line segment: float f = ( edgeDotVelocity * newT - edgeDotBaseToVertex ) / edgeSquaredLength; if (f >= 0.0 && f <= 1.0 )
{ // intersection took place within segment. t = newT;
foundCollison = true ;
collisionPoint = p1 + f * edge;
}
} return foundCollison;
}
最后给出综合各种检测的结果
Vec3D & pos,
Vec3D & normal)
{
Vec3D sphereIntersectionPoint;
Vec3D planeIntersectionPoint;
Vec3D polygonIntersectionPoint;
BOOL embedInPlane = FALSE;
Vec3D velocity = end - start;
Vec3D dir = velocity.normalize();
Plane triPlane(v0, v1, v2);
Vec3D n
{ if (fabs(dStart) >= radius) return FALSE;
} // 剔除背向的三角形 if (dStart < 0 )
{ return FALSE;
} if (dStart <= radius)
{
embedInPlane = TRUE; // Is the plane embedded // Calculate the plane intersection point planeIntersectionPoint = start - dStart * n;
} else { // // 运动起点和终点是否在三角形同一侧,如果是则不与此三角形相交 // float dEnd = triPlane.getDistance(end); if ( dEnd > radius)
{ return FALSE;
} // Calculate the sphere intersection point sphereIntersectionPoint = start - radius * n; // Calculate the plane intersection point Ray r(sphereIntersectionPoint, dir); float planedist = r.intersectPlane( & triPlane); // Are we traveling away from this polygon? if (planedist < 0.0f ) return FALSE; // Calculate the plane intersection point planeIntersectionPoint = sphereIntersectionPoint + planedist * dir;
} // 检查三角形面碰撞点是否在三角形内部 BOOL bIntersectionPointInTriangle = pointInTriangle(planeIntersectionPoint, v0, v1, v2); if (bIntersectionPointInTriangle)
{ if ( ! embedInPlane)
{
dist = (planeIntersectionPoint - sphereIntersectionPoint).length();
pos = start + dist * dir;
normal = (pos - planeIntersectionPoint).normalize(); return TRUE;
} else {
dist = 0 ;
pos = start;
normal = n; return TRUE;
}
} else { float t = 999999 ;
BOOL bIntersect = FALSE; // 检测三个顶点是否和球体相交 bIntersect |= sphereIntersectPoint(v0, start, end, radius, t, polygonIntersectionPoint);
bIntersect |= sphereIntersectPoint(v1, start, end, radius, t, polygonIntersectionPoint);
bIntersect |= sphereIntersectPoint(v2, start, end, radius, t, polygonIntersectionPoint); // 三条边是否和球体相交 bIntersect |= sphereIntersectEdge(v0, v1, start, end, radius, t, polygonIntersectionPoint);
bIntersect |= sphereIntersectEdge(v1, v2, start, end, radius, t, polygonIntersectionPoint);
bIntersect |= sphereIntersectEdge(v2, v0, start, end, radius, t, polygonIntersectionPoint); if (bIntersect)
{
dist = t * ( (end - start).length() );
pos = start + dist * dir;
normal = (pos - polygonIntersectionPoint ).normalize(); return TRUE;
}
} return FALSE;
}
好了,总算完成了,看到了么,静态检测一个非常简单的测试算法,改成连续碰撞检测会有多麻烦,这就是为什么大多数物理引擎都采用静态碰撞检则的原因,当然这种检测不
不用过于担心其效率,因为大多数计算在发现球体根本不和三形形平面相撞时已经不再进行下面的复杂计算了,这个算法是逐级深入检测,也即是在最坏的情况下,从平面检测
一直到检测完所有顶点和线段 .而这种情况并不常见.所以不用过于担心这个函数的调用开销.
至于还有什么不明白的可以搜索上面的文档,慢慢研究.
作者:Perit
转载于:https://www.cnblogs.com/Perit/articles/1759781.html
球与三角形的动态碰撞测试相关推荐
- OpenGL.ES在Android上的简单实践:10-曲棍球(拖动物体、碰撞测试)
OpenGL.ES在Android上的简单实践:10-曲棍球(拖动物体.碰撞测试) 1.让木槌跟随手指移动 继续上一篇文章9的内容.既然可以测试木槌是否被触碰了,我们将继续努力下去:当我们来回拖动木槌 ...
- matlab三角形旋转动态,新手向!用WebGL写一个旋转的动态三角形,总共分三步!!(注释超详细!!)...
html部分还是比较简单,引入的matrix.js是矩阵变换的一些方法,网上有很多,大家可以搜一搜(我的线性代数知识已经还给我的高数老师了,最近考虑着手捡起来) demo 下面是demo.js的代码 ...
- android 动态添加颜色,Android绘制一个三角形并且可动态改变颜色
方法一: 这种方法的三角形角度没法控制,因为其实是矩形旋转. android:fromDegrees="45" android:pivotX="135%" an ...
- OpenGL中的轨迹球问题
OpenGL鼠标轨迹球(Trackball)原理 什么是鼠标轨迹球 类似AutoCAD里的"动态观察",三维模型都是要投影到二维的屏幕上才能显示给用户,而用户如果想观察一下三维模型 ...
- [虚幻引擎][UE][UE5]在UE中画一个线框球,网格连接画球,高阶画球并操控
[虚幻引擎][UE][UE5]在UE中画一个线框球,网格连接画球,高阶画球并操控 1.材质法 2.绘制调试球体 3.网格连接 蓝图项目链接: 4.高阶画球并操控 蓝图项目链接: 1.材质法 缺点:是实 ...
- OpenGL鼠标轨迹球
转自:http://www.lubanren.net/weblog/post/283.html 什么是鼠标轨迹球 类似AutoCAD里的"动态观察",三维模型都是要投影到二维的屏幕 ...
- OpenGL之渲染大小球自转和公转的效果
效果展示 整体思路与步骤拆分 渲染绘制绿色方格地板部分: 渲染绘制中心位置的红色大球,并实现其自转: 渲染绘制随机位置的50个静态小球和一个围绕红色大球公转的动态蓝色球: 渲染实现动态蓝色球绕红色大球 ...
- Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点
1字体的基础知识 本地化的常用手段是文本翻译,而文本翻译除了上一篇文章中提到的翻译质量之外,还需要从技术手段上支持多语言在游戏中的显示. 在电子产品上展示语言或者文本,最常用的方式就是提供一个字体文件 ...
- 点云综述一稿 点云硬件、点云软件、点云处理算法、点云应用以及点云的挑战与展望
经过一周的综述撰写,深感点云算法应用之浩瀚,只能仰仗前辈们的文章作一些整理: 点云硬件: 点云获取技术可分为接触式扫描仪.激光雷达.结构光.三角测距(Triangulation).以及立体视觉等多种. ...
最新文章
- 可交互的对抗网络如何增强人类创造力?
- win7如何添加开机启动程序(开机就自动运行打开)
- java:迭代器Iterator
- LSTM拟合正弦曲线代码(转载)
- 26Java中的多态
- f ajax event,f:ajax onevent不能使用预定义函数,但可以使用内联函数
- java自用代码(包括:新建单线程、创建文件夹及文件、map转为json并将json写入txt、文件剪切或改名)...
- FPN相关问题学习记录
- XP命令合集(开始→运行→输入的命令集锦开始→运行→输入的命令集锦)
- word的使用(三)
- 【bzoj 入门OJ】[NOIP 热身赛]Problem C: 星球联盟(并查集)
- devc 能优化吗_Devc 、一元多项式的加法、减法、乘法的实现
【问题描述】
设有 联合开发网 - pudn.com...
- 传感器采取船舶的_电容式传感器工作原理与电容式传感器行业应用
- Activity的生命周期
- python requests 由于目标计算机积极拒绝,无法连接
- cocos creator pc web端 全屏
- 小米note2鸿蒙ROM,小米最新刷机包rom下载_奇兔rom市场
- 将光标从下划线变成竖线的方法
- 操作系统第七、八章习题
- day11【网络编程】