《实时碰撞检测算法技术》读书笔记(五):最近点计算(上)
来源:互联网 发布:淘宝卖家开通直播条件 编辑:程序博客网 时间:2024/05/01 09:42
点到面的最近距离
一点P以及法线n定义了一个平面π,所有该平面上的点X都满足方程n·(X - P) = 0(即从点P指向X的向量垂直于n)。现令Q为空间内任意一点,则面内距Q的最近点R为点QQ在该面上的正交投影,即从顶点Q处向平面垂直(依据法线n)移动。同时,对于值t,有R = Q - tn,将该表达式代入平面方程求解t:
将t代入到R = Q - tn并得到投影点R:
从等式中不难发现t为正值,则Q位于该面的正面,t为负值则Q位于该面的背面。
当平面按照格式给出时,则有表达式。因此,一点距平面最近点的代码如下所示:
Point ClosestPtPointPlane(Point q, Plane p) { float t = (Dot(p.n, q) - p.d) / Dot(p.n, p.n); return q - t * p.n;}
如果平面方程采用单位向量,则t简化为t = (n·Q) - d。
Q至平面的有符号距离可以通过t的计算返回值得到:
float DistPointPlane(Point q, Plane p) { //return Dot(p.n, q) - p.d; if plane equation normalized (||p.n|| == 1) return (Dot(p.n, q) - p.d) / Dot(p.n, p.n);}
点至线段的最近点
令AB为两端点A、B定义的线段,给定任意一点C,该问题为:确定线段AB上距C的最近点D。可以将点C投影至AB延长线上,如果投影点P位于线段内,则点P为所求;若投影点P位于线段外部,则线段上的近C端点为最近点。
直线AB上的一点可参数化为P(t) = A+ t(B - A)。利用点积的投影性质,t可表示为C在直线上的投影,且t = (C - A)·n / ||B - A||,其中n = (B - A)/(||B - A||)即为AB方向上的单位向量。
若在线段内获取最近点,则有0<=t<=1,且在参数方程中代入t值即可得到最近点D。代码如下所示:
//Given segment ab and point c, copmutes closest point d on ab.//Also returns t for the position of d, d(t) = a + t*(b - a)void ClosestPtPointSegment(Point c, Point a, Point b, float &t, Point &d){ Vector ab = b - a; //Progect c onto ab, computing parameterized position d(t) = a + t*(b - a) t = Dot(c - a, ab) / Dot(ab, ab); //If outside segment, clamp t(and therefore d) to the closest endpoint if(t < 0.0f) t = 0.0f; if(t > 1.0f) t = 1.0f; //Compute projected position from the clamped t d = a + t * ab;}
如果除法操作成本高昂,则可预先保存非负的分母值并延迟该除法操作。优化后的代码如下:
//Given segment ab and point c, copmutes closest point d on ab.//Also returns t for the position of d, d(t) = a + t*(b - a)void ClosestPtPointSegment(Point c, Point a, Point b, float &t, Point &d){ Vector ab = b - a; //Project c onto ab, but deferring divide by Dot(ab,ab) t = Dot(c - a, ab) ; if(t <= 0.0f) { //c projects outside the [a, b] interval, on the a side;clamp to a t = 0.0f; d = a; } else { float denom = Dot(ab,ab); if(t >= denom) { //c projects outside the [a,b] interval, on the b side; clamp to b t = 1.0f; d = b; } else { //c projects inside the [a,b] interval;must do deferred divide now t = t / denom; d = a + t * ab; } }}
点到线段的距离
一点C与线段AB之间的(平方)距离可以直接获取,而无须显式计算线段AB上距C的最近点D。如前所诉,需要考查3种情形:当AC·AB <= 0 时,点A为距C的最近点且平方距离为AC·AC;当AC·AB >= AB·AB时,点B为距离C的最近点且平方距离为BC·BC;最后一种情况,当0 < AC·AB< AB·AB时,平方距离为CD·CD,其中
由于CD·CD可化简为
则点D无需计算。实现代码如下:
//Returns the squared distance between point c and segment abfloat SqDistPointSegment(Point a, Point b, Point c){ Vector ab = b - a, ac = c - a, bc = c - b; float e = Dot(ac, ab); //Handle cases where c projects outside ab if(e <= 0.0f) return Dot(ac, ac); float f = Dot(ab, ab); if(e >= f) return Dot(bc,bc); //Handle cases where c projects onto ab return Dot(ac, ac) - e* e / f;}
点至AABB的最近点
//Given point p, return the point q on or in AABB b that is closest to pvoid ClosestPtPointAABB(Point p, AABB b, Point &q) { .//For each coordinate axis, if the point coordinate value is //outside box, clamp it to the box, else keep it as is for(int i = 0; i < 3; i++) { float v = p[i]; if(v < b.min[i]) v = b.min[i]; //v = max(v, b.min[i]); if(v > b.max[i]) v = b.max[i]; //v = min(v, b.max[i]); q[i] = v; }}
在支持SMID指令的CPU体系结构中,上诉函数常实现为两条指令:max指令及其后的min指令。
点到AABB的距离
对于顶点P,当获取包围盒上的最近点Q并计算两者距离时,实际上无须显式计算最近点Q,如下代码所示(为了简化计算呢过程并避免昂贵的开方计算,代码中使用了平方距离):
//Compute the square distance between a point and an AABB bfloat SqDistPointAABB(Point p, AABB b){ float sqDist = 0.0f; for(int i = 0; i < 3; i++) { //For each axis count any excess distance outside the box extents float v = p[i]; if(v < b.min[i]) sqDist += (b.min[i] - v) * (b.min[i] - v); if(v > b.max[i]) sqDist += (v - b.max[i]) * (v - b.max[i]); } return sqDist;}
点至OBB的最近点
设定B为中心点为C的OBB,且3个正交单位向量u0、u1和u2定义了B的x、y及z轴。3个标量e0、e1、e2确定了沿各轴向的1/2轴长。在该表达方式中,所有B中的点S可记为S = C + au0 + bu1 + cu2,其中|a| <= e0, |b| <= e1, |c| <= e2。
世界坐标空间中的一点P与OBB局部坐标空间中的一点Q = (x,y,z)之间存在下列关系:P = C + xu0 + yu1 + zu2.给定一点P,其OBB空间坐标可按下列方式计算。以下只给出坐标x的推导过程:
于是将顶点P转换至OBB的局部坐标系统中,并计算OBB上距该转换点的最近点,最后将结果转换回世界坐标系统。代码如下:
//Given point p, return point q on (or in) OBB b, closest to pvoid ClosestPtPointOBB(Point p, OBB b, Point &q){ Vector d = p - b.c; //Start result at center of box; make steps from there q = b.c; //For each OBB axis... for(int i = 0; i < 3; i++) { //...project d onto that axis to get the distance //along the axis of d from the box center float dist = Dot(d, b.u[i]); //if distance farther than the box extents, clamp to the box if(dist > b.e[i]) dist = b.e[i]; if(disr < -b.e[i]) dist = -b.e[i]; //Step that distance along the axis to get world coordinate q += dist * b.u[i]; }}
点到OBB的距离
计算P与OBB上最近点之间的平方距离时,前述函数可调用如下:
//Compute the square distance between point p and OBB bfloat SqDistPointOBB(Point p, OBB b){ Point closest; ClosestPtPointOBB(p, b, closest); float sqDist = Dot(closest - p, closest - p); return sqDist;}
可以只计算平方距离而不获取最近点。
//Compute the square distance between point p and OBB bfloat SqDistPointOBB(Point p, OBB b){ Vector v = p - b.c; float sqDist = 0.0f; for(int i = 0; i < 3; i++) { //Project vector from box center to p on each axis, getting the distance //of p along that axis, and count any excess distance outside box extents float d = Dot(v, b.u[i]), excess = 0.0f; if(d < -b.e[i]) excess = d + b.e[i]; else if(d > b.e[i]) excess = d - b.e[i]; sqDist += excess * excess; } return sqDist;}
- 《实时碰撞检测算法技术》读书笔记(五):最近点计算(上)
- 《实时碰撞检测算法技术》读书笔记(六):最近点计算(下)
- 《实时碰撞检测算法技术》读书笔记(四):OBB计算
- 《实时碰撞检测算法技术》读书笔记(三):计算逼近包围球
- 《实时碰撞检测算法技术》读书笔记(二):轴对齐包围盒(AABB)的计算与更新
- 《实时碰撞检测算法技术》读书笔记(一):包围体(BV)
- 《实时碰撞检测算法技术》读书笔记(七):分离轴测试
- 《实时碰撞检测算法技术》读书笔记(八):平面与部分形体间的测试
- 《实时碰撞检测算法技术》读书笔记(九):球体与部分形体间的测试
- 《算法导论》实验五:最近点对算法(C++)
- 碰撞检测算法:点和矩形碰撞、矩形碰撞
- 碰撞检测算法:点和矩形碰撞、点和圆形碰撞、矩形碰撞、圆形碰撞
- 碰撞检测算法:点和矩形碰撞、点和圆形碰撞、矩形碰撞、圆形碰撞
- 碰撞检测算法:点和矩形碰撞、点和圆形碰撞、矩形碰撞、圆形碰撞
- 碰撞检测算法:点和矩形碰撞、点和圆形碰撞、矩形碰撞、圆形碰撞
- 碰撞检测算法:点和矩形碰撞、点和圆形碰撞、矩形碰撞、圆形碰撞
- 手游《奔跑吧?骚年!》技术分享(五):三角形碰撞检测
- 数据挖掘技术(五)——离群点检测
- leetcode Integer to Roman
- Date、Calendar和StringBuffer的综合使用
- Autorelease的疑问
- awk 常用函数!
- winpcap学习(二)
- 《实时碰撞检测算法技术》读书笔记(五):最近点计算(上)
- 作业02
- 多线程环境下使用的单例模式的实现
- protected的语法
- 编程之美2.19——区间重合判断(线段树)
- 杭电-亲和数
- mysql笔记
- 算法导论 第15章 动态规划之钢条切割
- 怎么做好一个产品