【发布时间】:2020-09-25 17:50:39
【问题描述】:
我一直在调整和修改我的代码以针对三角形测试 AABB,但我不确定我做错了什么。我正在使用分离轴定理,因为我相信这是用于检测 AABB 和三角形之间碰撞的最佳且唯一的方法(如果我错了或有更好/更快的方法,请纠正我)。目前它在发生碰撞时没有检测到任何东西。
代码比较小,每帧都调用isAABBIntersectingTriangle,希望懂数学或者算法的人能帮帮我。该函数将边界框 min 和 max 作为两个 3D 向量 (glm::vec3) 和三角形顶点 (tri1, tri2, tri3) 中的 3 个作为 glm::vec3s。这些坐标都在世界空间中(AABB 坐标仅通过位置和比例进行平移,三角形通过位置旋转和比例进行平移)
bool SATTriangleAABBCheck(glm::vec3 axis, glm::vec3 bboxMin, glm::vec3 bboxMax, glm::vec3 tri1, glm::vec3 tri2, glm::vec3 tri3)
{
//Dot triangle vertices
float triVert1 = glm::dot(axis, tri1);
float triVert2 = glm::dot(axis, tri2);
float triVert3 = glm::dot(axis, tri3);
float triMin = glm::min(glm::min(triVert1, triVert2), triVert3);
float triMax = glm::max(glm::max(triVert1, triVert2), triVert3);
//Dot cube vertices
float v1 = glm::dot(axis, glm::vec3(bboxMin.x, bboxMin.y, bboxMin.z));
float v2 = glm::dot(axis, glm::vec3(bboxMax.x, bboxMax.y, bboxMax.z));
float v3 = glm::dot(axis, glm::vec3(bboxMax.x, bboxMax.y, bboxMin.z));
float v4 = glm::dot(axis, glm::vec3(bboxMax.x, bboxMin.y, bboxMax.z));
float v5 = glm::dot(axis, glm::vec3(bboxMax.x, bboxMin.y, bboxMin.z));
float v6 = glm::dot(axis, glm::vec3(bboxMin.x, bboxMax.y, bboxMax.z));
float v7 = glm::dot(axis, glm::vec3(bboxMin.x, bboxMin.y, bboxMax.z));
float v8 = glm::dot(axis, glm::vec3(bboxMin.x, bboxMax.y, bboxMin.z));
float aabbMin = glm::min(glm::min(glm::min(glm::min(glm::min(glm::min(glm::min(v1, v2), v3), v4), v5), v6), v7) ,v8);
float aabbMax = glm::max(glm::max(glm::max(glm::max(glm::max(glm::max(glm::max(v1, v2), v3), v4), v5), v6), v7), v8);
if ((triMin < aabbMax && triMin > aabbMin) || (triMax < aabbMax && triMax > aabbMin))
return true;
if ((aabbMin < triMax && aabbMin > triMin) || (aabbMax < triMax && aabbMax > triMin))
return true;
return false;
}
glm::vec3 CalcSurfaceNormal(glm::vec3 tri1, glm::vec3 tri2, glm::vec3 tri3)
{
glm::vec3 u = tri2 - tri1;
glm::vec3 v = tri3 - tri1;
glm::vec3 nrmcross = glm::normalize(glm::cross(u, v));
return nrmcross;
}
bool isAABBIntersectingTriangle(glm::vec3 bboxMin, glm::vec3 bboxMax, glm::vec3 tri1, glm::vec3 tri2, glm::vec3 tri3)
{
//AABB face normals
glm::vec3 axis1(1, 0, 0);
glm::vec3 axis2(0, 1, 0);
glm::vec3 axis3(0, 0, 1);
//Triangle face normal
glm::vec3 axis4 = CalcSurfaceNormal(tri1, tri2, tri3);
//Edge normals
glm::vec3 e1 = tri2 - tri1;
glm::vec3 e2 = tri3 - tri1;
glm::vec3 e3 = tri3 - tri2;
glm::vec3 e4 = glm::vec3(bboxMax.x, bboxMax.y, bboxMax.z) - glm::vec3(bboxMin.x, bboxMax.y, bboxMax.z);
glm::vec3 e5 = glm::vec3(bboxMax.x, bboxMax.y, bboxMax.z) - glm::vec3(bboxMax.x, bboxMin.y, bboxMax.z);
glm::vec3 e6 = glm::vec3(bboxMax.x, bboxMax.y, bboxMax.z) - glm::vec3(bboxMax.x, bboxMax.y, bboxMin.z);
//Cross products of each edge
glm::vec3 axis5 = glm::normalize(glm::cross(e1, e4));
glm::vec3 axis6 = glm::normalize(glm::cross(e1, e5));
glm::vec3 axis7 = glm::normalize(glm::cross(e1, e6));
glm::vec3 axis8 = glm::normalize(glm::cross(e2, e4));
glm::vec3 axis9 = glm::normalize(glm::cross(e2, e5));
glm::vec3 axis10 = glm::normalize(glm::cross(e2, e6));
glm::vec3 axis11 = glm::normalize(glm::cross(e3, e4));
glm::vec3 axis12 = glm::normalize(glm::cross(e3, e5));
glm::vec3 axis13 = glm::normalize(glm::cross(e3, e6));
//If no overlap on all axes
if (!SATTriangleAABBCheck(axis1, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis2, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis3, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis4, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis5, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis6, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis7, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis8, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis9, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis10, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis11, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis12, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
if (!SATTriangleAABBCheck(axis13, bboxMin, bboxMax, tri1, tri2, tri3)) return false;
return true;
}
这也是主循环中的代码,它计算三角形的世界位置,然后调用函数,我不认为这里有什么问题,因为它可能被检查得最多:
glm::vec3 tri1 = glm::vec3(entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[0]].position.x * entities[0]->scale, entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[0]].position.y * entities[0]->scale, entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[0]].position.z * entities[0]->scale);
glm::vec3 tri2 = glm::vec3(entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[1]].position.x * entities[0]->scale, entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[1]].position.y * entities[0]->scale, entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[1]].position.z * entities[0]->scale);
glm::vec3 tri3 = glm::vec3(entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[2]].position.x * entities[0]->scale, entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[2]].position.y * entities[0]->scale, entities[0]->model.meshes[0].vertices[entities[0]->model.meshes[0].indices[2]].position.z * entities[0]->scale);
//Translate these tris by the model matrix
glm::mat4 mat(1.0f);
mat = glm::translate(mat, glm::vec3(entities[0]->xPos, entities[0]->yPos, entities[0]->zPos));
mat = glm::rotate(mat, glm::radians(entities[0]->xRot), glm::vec3(1, 0, 0));
mat = glm::rotate(mat, glm::radians(entities[0]->yRot), glm::vec3(0, 1, 0));
mat = glm::rotate(mat, glm::radians(entities[0]->zRot), glm::vec3(0, 0, 1));
mat = glm::scale(mat, glm::vec3(entities[0]->scale, entities[0]->scale, entities[0]->scale));
glm::vec4 tri11 = mat * glm::vec4(tri1.x, tri1.y, tri1.z, 1.0f);
glm::vec4 tri22 = mat * glm::vec4(tri2.x, tri2.y, tri2.z, 1.0f);
glm::vec4 tri33 = mat * glm::vec4(tri3.x, tri3.y, tri3.z, 1.0f);
if (isAABBIntersectingTriangle(entities[3]->bboxMin, entities[3]->bboxMax, glm::vec3(tri11.x, tri11.y, tri11.z), glm::vec3(tri22.x, tri22.y, tri22.z), glm::vec3(tri33.x, tri33.y, tri33.z)))
{
std::cout << "AABB Tri collision\n";
}
【问题讨论】:
-
构建一个好的minimal reproducible example (MRE) 的一部分是提供输入。在您的情况下,选择一个应该相交的三角形和 AAB 可能很有用,然后使用这些输入单步执行您的代码以查看哪个条件触发了
return false。 (您可以在测试程序中执行此操作,而不是在每帧调用此函数的程序中执行此操作。)接下来,手动进行计算以查看结果应该是什么。 (我有没有提到你应该选择简单的输入?;)) -
啊,你更新了你的代码示例。这是获取 MRE 数据的一种相当复杂的方法。更简单的东西怎么样,比如
glm::vec4 tri11 = glm::vec4(4.0f, 3.0f, 2.0f, 1.0f);或您决定使用的任何数字?请记住,我们一次测试一个,而不是一次测试您的整个程序。现在的焦点是isAABBIntersectingTriangle()。 -
是的,所以在代码中(我只是在底部对其进行了编辑)您可以看到我只针对特定 AABB 测试网格中的单个三角形。事实上,我什至在空间中绘制了精确的三角形和 AABB(边界框本身),以确保我可以看到传入的碰撞坐标的精确坐标。在特定位置,它不会失败。在某些地方,它没有通过第一次测试(三角形的面法线测试),在某些情况下,它通过了面法线测试和一些轴测试。
-
我只是在坐标中打孔,这样一个三角形面就与 AABB 相交,只有面。什么也没发生,我稍微修改了其中一个三角形坐标,它就开始了(改变了一个 y 坐标,所以一个顶点或一条边在里面),然后它就开始了。改变它,使三角形的顶点和两个顶部边缘相交,没有打印出来。似乎它在我的实际 SAT 代码中是一个问题。它应该首先通过检测面部来启动。不确定接下来要尝试什么,我已经把 SAT 代码弄乱了很多
-
if (CollisionHelper::isAABBIntersectingTriangle(glm::vec3(-1, 0, -1), glm::vec3(-2, 1, -2), glm::vec3(-3.0f, -0.5f, -1.5f), glm::vec3(0, -0.5f, -1.5f), glm::vec3(-1.5f, 1.5f, -1.5f))) { std::cout << "AABB Tri collision\n"; }是新代码,仅用于显示我正在使用的坐标(首先是 AABB 最小值和最大值,然后是 3 个三角形顶点坐标)
标签: c++ math collision-detection game-development separating-axis-theorem