GJK算法陷入了不同Voronoi区域情况的循环中
GJK algorithm gets stuck in a loop of different Voronoi Region cases
我目前正在尝试实现中介绍的简化GJK算法https://mollyrocket.com/849进入我的C++游戏。
然而,我在第二和第三维度上遇到了奇怪的行为:算法有时(当一秒钟调用多次时,这种情况很常见)会陷入一个循环。例如,调试消息一次又一次地将以下内容打印到std::cout:
3ACxAB
4AB
3ABxAC
4ABD
4AD
如果你看一下我的代码,你会发现这些行代表了算法允许的情况。例如,3ACxAB意味着单纯形当前是一个三角形,原点在面的voronoi区域,在叉积ACxAB[/em>的方向上(可以解释为三角形的"上方"或"下方")。情况4AB意味着单纯形是四面体,原点在边AB的voronoi区域。
A总是新添加的点。在代码中,A始终是simplex
的最大索引。(^simplex[1]`如果它是一条线,2如果是三角形,3在a的情况下)四面体。
即使经过几天的错误搜索(我发现了一些,但还有一个或多个),算法也不会起作用。
你看到代码中有问题吗?因为我和我的两个朋友都不这样做。
附言:我没有复制凯西视频中的任何计算(例如方向向量的叉积)。看完之后,我自己下定决心,所以潜在的问题可能就在这里,尤其是在第三维度,凯西故意没有谈论。
我的支持功能:
//hullA/B: convex hull of A resp. B; baseA/B: location of A/B
Vector3f gjkSupport(Vector3f direction,
std::vector<GLfloat> hullA, std::vector<GLfloat> baseA,
std::vector<GLfloat> hullB, std::vector<GLfloat> baseB) {
//Initialize
GLfloat maxDotP = -std::numeric_limits<GLfloat>::max();
Vector3f furthestPointA, furthestPointB;
//Get furthest point in given direction out of hullA by getting the maximum dot
//product of the direction vector and a hull vertex's position vector
for (GLuint i = 0; i < hullA.size(); i += 3) {
Vector3f current (hullA[i]+baseA[0], hullA[i+1]+baseA[1], hullA[i+2]+baseA[2]);
// * = dot product
GLfloat dotP = direction * current;
if (dotP > maxDotP) {
maxDotP = dotP;
furthestPointA = current;
}
}
maxDotP = -std::numeric_limits<GLfloat>::max();
//Get furthest point in negative of the given direction out of hullB
for (GLuint i = 0; i < hullB.size(); i += 3) {
Vector3f current (hullB[i]+baseB[0], hullB[i+1]+baseB[1], hullB[i+2]+baseB[2]);
GLfloat dotP = -direction * current;
if (dotP > maxDotP) {
maxDotP = dotP;
furthestPointB = current;
}
}
//Furthest Minkowski Difference point is difference of d*A[i]-(-d)*B[j]
return furthestPointA - furthestPointB;
}
我的单纯形函数:
bool gjkSimplex(std::vector<Vector3f> &simplex, Vector3f &direction) {
GLuint simplexSize = simplex.size();
std::cout << simplexSize;
switch (simplexSize) {
//If the simplex is a line segment
case 2:
//Point is closest feature
if ((simplex[0]-simplex[1])*-simplex[1] < 0) {
std::cout << "A";
simplex = {simplex[1]};
//direction = A0
direction = -simplex[1];
//Line is closest feature
} else {
std::cout << "AB";
//direction = AB x (A0 x AB)
// ^ = cross product
direction = (simplex[0]-simplex[1]) ^ ((-simplex[1]) ^ (simplex[0]-simplex[1]));
}
break;
//If the simplex is a triangle
case 3:
//Point is closest feature
if ((simplex[0]-simplex[2])*(-simplex[2]) < 0 && (simplex[1]-simplex[2])*(-simplex[2]) < 0) {
std::cout << "A";
//direction = A0
direction = -simplex[2];
simplex = {simplex[1]};
//Line to second-latest point is closest feature
} else if ((((simplex[0]-simplex[2])^(simplex[1]-simplex[2]))^(simplex[1]-simplex[2]))*-simplex[2] > 0) {
std::cout << "AB";
//direction = AB x (A0 x AB)
direction = (simplex[1]-simplex[2]) ^ ((-simplex[2]) ^ (simplex[1]-simplex[2]));
simplex = {simplex[1], simplex[2]};
//Line to oldest point is closest feature
} else if (((simplex[0]-simplex[2])^((simplex[0]-simplex[2])^(simplex[1]-simplex[2])))*-simplex[2] > 0) {
std::cout << "AC";
//direction = AC x (A0 x AC)
direction = (simplex[0]-simplex[2]) ^ ((-simplex[2]) ^ (simplex[0]-simplex[2]));
simplex = {simplex[0], simplex[2]};
//Face is closest feature
} else {
//Origin is in direction AC x AB
if (((simplex[1]-simplex[2]) ^ (simplex[0]-simplex[2])) * (-simplex[2]) < 0) {
std::cout << "ACxAB";
//direction = AC x AB
direction = (simplex[0]-simplex[2]) ^ (simplex[1]-simplex[2]);
//origin is in direction AB x AC (other side of the face)
} else {
std::cout << "ABxAC";
//direction = AB x AC
direction = (simplex[1]-simplex[2]) ^ (simplex[0]-simplex[2]);
simplex = {simplex[1], simplex[0], simplex[2]};
}
}
break;
//If the simplex is a tetrahedron
case 4:
//Newest point is closest feature
if ((simplex[0]-simplex[3])*(-simplex[3]) < 0 && (simplex[1]-simplex[3])*(-simplex[3]) < 0 &&
(simplex[2]-simplex[3])*(-simplex[3]) < 0) {
std::cout << "A";
//direction = A0
direction = -simplex[3];
simplex = {simplex[3]};
//Edge between newest and second-newest point is closest feature
} else if ((((simplex[2]-simplex[3]) ^ ((simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3]))) * (-simplex[2]) < 0) &&
((((simplex[1]-simplex[3]) ^ (simplex[0]-simplex[3])) ^ (simplex[2]-simplex[3])) * (-simplex[2]) < 0)) {
std::cout << "AB";
//direction = AB x (A0 x AB)
direction = (simplex[2]-simplex[3]) ^ ((-simplex[3]) ^ (simplex[2]-simplex[3]));
simplex = {simplex[2], simplex[3]};
//Edge between newest and third-newest vertex is closest feature
} else if ((((simplex[1]-simplex[3]) ^ ((simplex[0]-simplex[3]) ^ (simplex[1]-simplex[3]))) * (-simplex[2]) < 0) &&
((((simplex[0]-simplex[3]) ^ (simplex[2]-simplex[3])) ^ (simplex[1]-simplex[3])) * (-simplex[2]) < 0)) {
std::cout << "AC";
//direction = AC x (A0 x AC)
direction = (simplex[1]-simplex[3]) ^ ((-simplex[3]) ^ (simplex[1]-simplex[3]));
simplex = {simplex[1], simplex[3]};
//Edge between newest and oldest point is closest feature
} else if ((((simplex[0]-simplex[3]) ^ ((simplex[2]-simplex[3]) ^ (simplex[0]-simplex[3]))) * (-simplex[2]) < 0) &&
((((simplex[2]-simplex[3]) ^ (simplex[1]-simplex[3])) ^ (simplex[0]-simplex[3])) * (-simplex[2]) < 0)) {
std::cout << "AD";
//direction = AD x (A0 x AD)
direction = (simplex[0]-simplex[3]) ^ ((-simplex[3]) ^ (simplex[0]-simplex[3]));
simplex = {simplex[0], simplex[3]};
//Face between the three newest points is closest feature
} else if (((simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3])) * (-simplex[3]) > 0) {
std::cout << "ABC";
//direction = AC x AB (outer normal of face)
direction = (simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3]);
simplex = {simplex[1], simplex[3], simplex[2]};
//Face between newest, second-newest and oldest point is closest feature
} else if (((simplex[2]-simplex[3]) ^ (simplex[0]-simplex[3])) * (-simplex[3]) > 0) {
std::cout << "ABD";
//direction = AB x AD
direction = (simplex[2]-simplex[3]) ^ (simplex[0]-simplex[3]);
simplex = {simplex[0], simplex[2], simplex[3]};
//Face between newest, second-oldest and oldest point is closest feature
} else if (((simplex[0]-simplex[3]) ^ (simplex[1]-simplex[3])) * (-simplex[3]) > 0) {
std::cout << "ACD";
//direction = AD x AC
direction = (simplex[0]-simplex[3]) ^ (simplex[1]-simplex[3]);
simplex = {simplex[0], simplex[3], simplex[1]};
//Origin is encased by simplex
} else {
//Collision detected
std::cout << "ABCD";
return true;
}
break;
default:
direction = {1,1,1};
simplex = {};
break;
}
std::cout << "n";
return false;
};
GJK主回路:
//Narrow Phase collision function using GJK
bool SolidObject::collidesWith(SolidObject *object) {
//Initialize by using an arbitrary direction
Vector3f direction (1,1,1);
std::vector<Vector3f> simplex;
Vector3f point = gjkSupport(direction,
this->meshes[0].getConvexHull(), this->base, object->meshes[0].getConvexHull(), object->base);
simplex = {point};
//Set direction to the negative of the resulting point
direction = -point;
bool originInSimplex = false;
while (!originInSimplex) {
//Get furthest point in new direction
point = gjkSupport(direction,
this->meshes[0].getConvexHull(), this->base, object->meshes[0].getConvexHull(), object->base);
//The furthest point in the negative direction is not in the opposing octant
// => no collision
if (point*direction < 0) {
return false;
}
//Add point to the simplex
simplex.push_back(point);
//Update simplex and direction, and return whether the simplex contains the origin
originInSimplex = gjkSimplex(simplex, direction);
}
std::cout << "n";
return true;
}
在三角形情况下:
//Point is closest feature
if ((simplex[0]-simplex[2])*(-simplex[2]) < 0 && (simplex[1]-simplex[2])*(-simplex[2]) < 0) {
std::cout << "A";
//direction = A0
direction = -simplex[2];
simplex = {simplex[1]};
}
应该是
simplex = {simplex[2]};
在四面体情况下:
所有的边缘检查都使用simplex[2]
执行点生成,但它们应该使用最新的点simplex[3]
。
我认为你的第一次边缘检查在第二种情况下使用了错误的面,所以不是
if ((((simplex[2]-simplex[3]) ^ ((simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3]))) * (-simplex[2]) < 0) &&
((((simplex[1]-simplex[3]) ^ (simplex[0]-simplex[3])) ^ (simplex[2]-simplex[3])) * (-simplex[2]) < 0)) {
std::cout << "AB";
//direction = AB x (A0 x AB)
direction = (simplex[2]-simplex[3]) ^ ((-simplex[3]) ^ (simplex[2]-simplex[3]));
simplex = {simplex[2], simplex[3]};
}
应该是
if ((((simplex[2]-simplex[3]) ^ ((simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3]))) * (-simplex[3]) < 0) &&
((((simplex[2]-simplex[3]) ^ (simplex[0]-simplex[3])) ^ (simplex[2]-simplex[3])) * (-simplex[3]) < 0)) {
std::cout << "AB";
//direction = AB x (A0 x AB)
direction = (simplex[2]-simplex[3]) ^ ((-simplex[3]) ^ (simplex[2]-simplex[3]));
simplex = {simplex[2], simplex[3]};
}
第二次边缘检查的第二个条件也是如此,它应该是:
((((simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3])) ^ (simplex[1]-simplex[3])) * (-simplex[3]) < 0)
第三次边缘检查的第二个条件:
((((simplex[0]-simplex[3]) ^ (simplex[1]-simplex[3])) ^ (simplex[0]-simplex[3])) * (-simplex[3]) < 0)
我还修复了三角形检查的输出simplexe。输入单纯形的最后一点应始终是输出单纯形的最后点。此外,点的顺序应一致,并与计算的方向相匹配。
以下是完整的固定功能:
bool gjkSimplex(std::vector<Vector3f> &simplex, Vector3f &direction) {
GLuint simplexSize = simplex.size();
std::cout << simplexSize;
switch (simplexSize) {
//If the simplex is a line segment
case 2:
//Point is closest feature
if ((simplex[0]-simplex[1])*-simplex[1] < 0) {
std::cout << "A";
//direction = A0
direction = -simplex[1];
simplex = {simplex[1]};
//Line is closest feature
} else {
std::cout << "AB";
//direction = AB x (A0 x AB)
// ^ = cross product
direction = (simplex[0]-simplex[1]) ^ ((-simplex[1]) ^ (simplex[0]-simplex[1]));
}
break;
//If the simplex is a triangle
case 3:
//Point is closest feature
if ((simplex[0]-simplex[2])*(-simplex[2]) < 0 && (simplex[1]-simplex[2])*(-simplex[2]) < 0) {
std::cout << "A";
//direction = A0
direction = -simplex[2];
simplex = {simplex[2]};
//Line to second-latest point is closest feature
} else if ((((simplex[0]-simplex[2])^(simplex[1]-simplex[2]))^(simplex[1]-simplex[2]))*-simplex[2] > 0) {
std::cout << "AB";
//direction = AB x (A0 x AB)
direction = (simplex[1]-simplex[2]) ^ ((-simplex[2]) ^ (simplex[1]-simplex[2]));
simplex = {simplex[1], simplex[2]};
//Line to oldest point is closest feature
} else if (((simplex[0]-simplex[2])^((simplex[0]-simplex[2])^(simplex[1]-simplex[2])))*-simplex[2] > 0) {
std::cout << "AC";
//direction = AC x (A0 x AC)
direction = (simplex[0]-simplex[2]) ^ ((-simplex[2]) ^ (simplex[0]-simplex[2]));
simplex = {simplex[0], simplex[2]};
//Face is closest feature
} else {
//Origin is in direction AC x AB
if (((simplex[1]-simplex[2]) ^ (simplex[0]-simplex[2])) * (-simplex[2]) < 0) {
std::cout << "ACxAB";
//direction = AC x AB
direction = (simplex[0]-simplex[2]) ^ (simplex[1]-simplex[2]);
//origin is in direction AB x AC (other side of the face)
} else {
std::cout << "ABxAC";
//direction = AB x AC
direction = (simplex[1]-simplex[2]) ^ (simplex[0]-simplex[2]);
simplex = {simplex[1], simplex[0], simplex[2]};
}
}
break;
//If the simplex is a tetrahedron
case 4:
//Newest point is closest feature
if ((simplex[0]-simplex[3])*(-simplex[3]) < 0 && (simplex[1]-simplex[3])*(-simplex[3]) < 0 &&
(simplex[2]-simplex[3])*(-simplex[3]) < 0) {
std::cout << "A";
//direction = A0
direction = -simplex[3];
simplex = {simplex[3]};
//Edge between newest and second-newest point is closest feature
} else if ((((simplex[2]-simplex[3]) ^ ((simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3]))) * (-simplex[3]) < 0) &&
((((simplex[2]-simplex[3]) ^ (simplex[0]-simplex[3])) ^ (simplex[2]-simplex[3])) * (-simplex[3]) < 0)) {
std::cout << "AB";
//direction = AB x (A0 x AB)
direction = (simplex[2]-simplex[3]) ^ ((-simplex[3]) ^ (simplex[2]-simplex[3]));
simplex = {simplex[2], simplex[3]};
//Edge between newest and third-newest vertex is closest feature
} else if ((((simplex[1]-simplex[3]) ^ ((simplex[0]-simplex[3]) ^ (simplex[1]-simplex[3]))) * (-simplex[3]) < 0) &&
((((simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3])) ^ (simplex[1]-simplex[3])) * (-simplex[3]) < 0)) {
std::cout << "AC";
//direction = AC x (A0 x AC)
direction = (simplex[1]-simplex[3]) ^ ((-simplex[3]) ^ (simplex[1]-simplex[3]));
simplex = {simplex[1], simplex[3]};
//Edge between newest and oldest point is closest feature
} else if ((((simplex[0]-simplex[3]) ^ ((simplex[2]-simplex[3]) ^ (simplex[0]-simplex[3]))) * (-simplex[3]) < 0) &&
((((simplex[0]-simplex[3]) ^ (simplex[1]-simplex[3])) ^ (simplex[0]-simplex[3])) * (-simplex[3]) < 0)) {
std::cout << "AD";
//direction = AD x (A0 x AD)
direction = (simplex[0]-simplex[3]) ^ ((-simplex[3]) ^ (simplex[0]-simplex[3]));
simplex = {simplex[0], simplex[3]};
//Face between the three newest points is closest feature
} else if (((simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3])) * (-simplex[3]) > 0) {
std::cout << "ABC";
//direction = AC x AB (outer normal of face)
direction = (simplex[1]-simplex[3]) ^ (simplex[2]-simplex[3]);
simplex = {simplex[1], simplex[2], simplex[3]};
//Face between newest, second-newest and oldest point is closest feature
} else if (((simplex[2]-simplex[3]) ^ (simplex[0]-simplex[3])) * (-simplex[3]) > 0) {
std::cout << "ABD";
//direction = AB x AD
direction = (simplex[2]-simplex[3]) ^ (simplex[0]-simplex[3]);
simplex = {simplex[2], simplex[0], simplex[3]};
//Face between newest, second-oldest and oldest point is closest feature
} else if (((simplex[0]-simplex[3]) ^ (simplex[1]-simplex[3])) * (-simplex[3]) > 0) {
std::cout << "ACD";
//direction = AD x AC
direction = (simplex[0]-simplex[3]) ^ (simplex[1]-simplex[3]);
simplex = {simplex[0], simplex[1], simplex[3]};
//Origin is encased by simplex
} else {
//Collision detected
std::cout << "ABCD";
return true;
}
break;
default:
direction = {1,1,1};
simplex = {};
break;
}
std::cout << "n";
return false;
};
只是猜测:
case 2:
//Point is closest feature
if ((simplex[0]-simplex[1])*-simplex[1] < 0) {
std::cout << "A";
simplex = {simplex[1]};
//direction = A0
direction = -simplex[1];
//Line is closest feature
在设置simplex
之前,是否应该先设置direction
?否则,您将尝试访问长度为1的向量的第二个元素。
此外,您编写的函数gjkSupport()
接受std::vector
对象,而不是std::vector&
对象。这可能会降低性能,因为每次调用函数时都会复制构建向量。
- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 函数中堆分配的效果与缺少堆分配的情况
- 在未初始化映射的情况下,将值插入到映射的映射中
- 是默认情况下分配给char数组常量的值
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 如何在不产生任何垃圾的情况下获得C中的像素
- 在已经使用Git的情况下减少编译时间
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 如何在没有信号的情况下从C++执行QML插槽
- 如何在不知道向量大小的情况下输入向量内部的向量?
- 如何针对特定情况调试和修复此双自由内存损坏问题
- 为什么在某些情况下不写入此文件?
- 多个线程/进程是否可以在不同步的情况下同时从/写入文件的非重叠区域?
- 默认情况具有哪个区域设置
- 试图了解C++#pragma Arduino 中区域的使用情况
- 为什么在重载区域函数时在波纹管代码的情况下会出现歧义
- Qt GUI C++ (Android) - 如何在不触摸滚动条的情况下在滚动区域之外滚动
- Iostream中包含的Ctype方法默认情况下在程序中的固定区域设置中工作吗
- GJK算法陷入了不同Voronoi区域情况的循环中