c++ - 在OpenGL中实现边界球碰撞

标签 c++ opengl collision-detection

我了解包围球碰撞的基本原理,但是实施它会使我有些困惑。

如果我在数组中定义了两个多维数据集:cube1 []和cube2 [],每个数组由组成每个三角形的GLfloat组成。那我该如何首先计算每个立方体的中心点?以及如何获得围绕此球体的半径?

需要什么数学来计算?

编辑:为了进一步澄清我的问题。假设我有一个使用以下数组定义的多维数据集:

GLfloat cube[] = {
     2.0f,  3.0f, -4.0f, // triangle 1, top right
     3.0f,  3.0f, -4.0f,
     2.0f,  2.0f, -4.0f, // bottom right

     3.0f,  3.0f, -4.0f, // triangle 2, back face top left
     3.0f,  2.0f, -4.0f, // bottom left
     2.0f,  2.0f, -4.0f,

     2.0f,  3.0f, -3.0f, // triangle 1, front face top left
     2.0f,  2.0f, -3.0f, // bottom left
     3.0f,  3.0f, -3.0f, // Bottom right

     3.0f,  3.0f, -3.0f, // triangle 2, front face
     2.0f,  2.0f, -3.0f,
     3.0f,  2.0f, -3.0f, // Bottom right

     2.0f,  3.0f, -3.0f, // triangle 1, top face
     3.0f,  3.0f, -3.0f,
     2.0f,  3.0f, -4.0f,
     3.0f,  3.0f, -4.0f, // triangle 2, top face
     2.0f,  3.0f, -4.0f,
     3.0f,  3.0f, -3.0f,

     2.0f,  2.0f, -3.0f, // triangle 1, bottom face
     2.0f,  2.0f, -4.0f,
     3.0f,  2.0f, -3.0f,
     3.0f,  2.0f, -4.0f, // triangle 2, bottom face
     3.0f,  2.0f, -3.0f, // Bottom Right.
     2.0f,  2.0f, -4.0f,

     2.0f,  2.0f, -4.0f, // triangle 1, left face
     2.0f,  2.0f, -3.0f,
     2.0f,  3.0f, -4.0f,
     2.0f,  3.0f, -4.0f, // triangle 2, left face
     2.0f,  2.0f, -3.0f,
     2.0f,  3.0f, -3.0f,

     3.0f,  2.0f, -4.0f, // triangle 1, right face
     3.0f,  3.0f, -4.0f,
     3.0f,  2.0f, -3.0f,
     3.0f,  3.0f, -4.0f, // triangle 2, right face
     3.0f,  3.0f, -3.0f,
     3.0f,  2.0f, -3.0f,

};


给定这个立方体,每次立方体平移时,我需要获取中心点并对其进行跟踪。我相信我已经这样做了,但也感谢您提供有关此方法是否正确的帮助:

// Calculate initial center of the shape
 glm::vec3 corner1 = glm::vec3(2.0f,  3.0f, -4.0f);
 glm::vec3 corner2 = glm::vec3(2.0f,  2.0f, -4.0f);
 glm::vec3 corner3 = glm::vec3(3.0f,  3.0f, -4.0f);
 glm::vec3 corner4 = glm::vec3(3.0f,  2.0f, -4.0f);
 glm::vec3 corner5 = glm::vec3(2.0f,  3.0f, -3.0f);
 glm::vec3 corner6 = glm::vec3(2.0f,  2.0f, -3.0f);
 glm::vec3 corner7 = glm::vec3(3.0f,  3.0f, -3.0f);
 glm::vec3 corner8 = glm::vec3(3.0f,  2.0f, -3.0f);

GLfloat x = (corner1.x + corner2.x + corner3.x + corner4.x + corner5.x + corner6.x+ corner7.x + corner8.x)/8;
GLfloat y = (corner1.y + corner2.y + corner3.y + corner4.y + corner5.y + corner6.y+ corner7.y + corner8.y)/8;
GLfloat z = (corner1.z + corner2.z + corner3.z + corner4.z + corner5.z + corner6.z+ corner7.z + corner8.z)/8;
center = glm::vec4(x, y, z, 1.0f);


使用以下功能检查翻译情况:

void Cube::Translate(double x, double y, double z)
{
// Translation matrix for cube.
glm::mat4 cubeTransMatrix = glm::mat4();
cubeTransMatrix = glm::translate(cubeTransMatrix, glm::vec3(x, y, z));
//center = cubeTransMatrix * center;
//Move the cube
for(int i = 0; i < sizeof(cube) / sizeof(GLfloat); i+=3){
        glm::vec4 vector = glm::vec4(cube[i], cube[i+1], cube[i+2], 1.0f);
        glm::vec4 translate = cubeTransMatrix*vector;
        glm::vec4 translateCenter = cubeTransMatrix*center;
        center.x = translateCenter[0];
        center.y = translateCenter[1];
        center.z = translateCenter[2];
        cube[i] = translate[0];
        cube[i+1] = translate[1];
        cube[i+2] = translate[2];

    }
}

最佳答案

形状的中心点可以通过多种方式来计算,具体取决于您要考虑的“中心”。但是,对于一个立方体,通常将中心计算视为其点的平均值,这相对简单:只需将所有向量相加并除以8,即可获得所有角的坐标平均值。立方体的网格,可能有更多的顶点,但是对于简单的立方体,情况并非如此。

如果您自己无权访问顶点(您已加载了网格,或者正在使用默认的多维数据集,GLUT内置的东西等),则需要跟踪该多维数据集的变换。我可能建议为每个多维数据集使用“局部”位置矢量或局部转换矩阵。

在OpenGL中,矩阵应以列为主,因此在进行任何全局转换后,最右边一列中的前3个值应为您在世界坐标中的位置。

检测冲突几乎容易得多(一旦您超过了整个“预测何时将发生冲突”部分,如果您是我的话,我就不会为第一个实现而担心)。球是简单的形状,检测两个球是否相交甚至更简单。您需要做的就是找到两个球体碰撞器之间的平方距离,并将其与半径的平方进行比较。

如果两个平方半径的总和大于两个球体之间的距离,则它们相交。否则,他们不会。

只是为了说明此计算实际上有多么简单,我在这里向您展示:

float r0sqr = sphere0.radius * sphere0.radius;
float r1sqr = sphere1.radius * sphere1.radius;

float distX = sphere0.position.x - sphere1.position.x;
float distY = sphere0.position.y - sphere1.position.y;
float distZ = sphere0.position.z - sphere1.position.z;

// Since we already need to square these, we won't need to take the absolute value
// to accurately compare them to the radii
distX *= distX;
distY *= distY;
distZ *= distZ;

float sqrDist = (distX+distY+distZ)

if((r0sqr + r1sqr) > sqrDist)
{
    // They intersect
}
else
{
    // They do not intersect
}


一旦检测到碰撞,就假设您要使球体成为刚体碰撞体,将它们彼此远离是非常简单的。只需取两个球的相交距离即可。为了提高效率,我们应该对先前的代码进行一些修改,但是:

// Since we already need to square these, we won't need to take the absolute value
// to accurately compare them to the radii
float distSqrX = distX * distX;
float distSqrY = distY * distY;
float distSqrZ = distZ * distZ;

float sqrDist = (distSqrX+distSqrY+distSqrZ);


完成此操作后,我们可以计算该碰撞的其余分辨率。我们将以一种非常简单的方式进行操作(假设两个对象都没有质量,并且没有影响要计算)。

float totalRadius = sphere0.radius + sphere1.radius;// the sum of the two spheres' radii
float dist = sqrt(sqrDist);// the actual distance between the two shapes' centers         
float minMovement = (totalRadius - dist);// the difference between the total radius and the actual distance tells us how far they intersect.

minMovement /= dist;// Divide by the distance to "scale" this movement so we can "scale" our distance vector (distX, distY, and distZ)

float mvmtX = distX * minMovement * 0.5f;// The minimum movement on the x-axis to resolve the collision
float mvmtY = distY * minMovement * 0.5f;// The minimum movement on the y-axis to resolve the collision
float mvmtZ = distZ * minMovement * 0.5f;// The minimum movement on the z-axis to resolve the collision

// For the sake of simplicity, we'll just have them "pop" out of each other, and won't
// be doing any interpolation to "smooth" the spheres' interaction.
//
// However, to ensure that we move the correct collider in the correct direction, we 
// need to see which one is on which side of the other, along the three axes.
if(sphere0.position.x < sphere1.position.x)
{
    sphere0.position.x -= mvmtX;
    sphere1.position.x += mvmtX;
}
else
{
    sphere0.position.x += mvmtX;
    sphere1.position.x -= mvmtX;
}

// Repeat this process for the other two axes
if(sphere0.position.y < sphere1.position.y)
{
    sphere0.position.y -= mvmtY;
    sphere1.position.y += mvmtY;
}
else
{
    sphere0.position.y += mvmtY;
    sphere1.position.y -= mvmtY;
}

if(sphere0.position.z < sphere1.position.z)
{
    sphere0.position.z -= mvmtZ;
    sphere1.position.z += mvmtZ;
}
else
{
    sphere0.position.z += mvmtZ;
    sphere1.position.z -= mvmtZ;
}


最后,可以通过以下三种方式之一来计算球体的适当半径,以获得对立方体的碰撞检测所需的效果:

使用外接球体(球体“接触”立方体的角),半径公式为sqrt(3)*edgeLength*0.5。您将获得一个“反应过度”的碰撞检测系统,该系统将检测到由于半径能够延伸到盒子的各个角落而在立方体体积之外的合理距离的碰撞。最大的误差点将在立方体的一个面的中心,在此处球体将以立方体的边长1/sqrt(3)乘以立方体的过度延伸。

第二种方法是使用内切球体,其中球体与立方体的面相切(球体“接触”每个立方体面的中心),并且半径计算为edgeLength*0.5。同样,会有错误,但是这个错误实际上往往会出现更多错误,因为它将在8点处“反应不足”,而不是像上一个那样在6点处“反应过度”。每个角的“反应不足”距离(立方体的角与球体表面上最近的点之间的距离)与上一个反应过度的距离相同,大约为1/sqrt(3)倍边长。

最后一种方法,也是最精确的方法,是计算球体使其与立方体的边缘相切。该半径的公式为edgeLength/sqrt(2)。该球体将“接触”立方体每个边缘的中心,每个面都将“高估”,而每个角点将“低估”。但是,高估/低估的距离要小得多,并且通常可以容忍得多,在任何点上距边长“应该”的距离仅是侧面长度的(sqrt(3)*sqrt(2))/2倍(对于a而言,得出的准确度要高出大约1.4倍)碰撞)。

您可以选择最适合您的需求。第一个具有最佳的“角”检测,但他的“面部”检测最差,第二个具有最佳的“脸”检测和最差的“角”检测,而第三个具有或多或少的第一个“平均值”第二,如果可能的话,使其具有最“可靠”的准确性。

关于c++ - 在OpenGL中实现边界球碰撞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22882380/

相关文章:

c++ - 如何防止 Box2D 中传感器对象上的隧道效应

javascript - Phaser 复杂碰撞

c++ - 使用不带参数列表的类模板(未声明的标识符)

c++ - 将 std::istream::sentry 与 std::istream 一起使用

c++ - GCC 4.1.1 不使用 tr1::unordered_map 编译一些代码

c++ - 使用 FBO 绘制到渲染缓冲区

c++ - 将按引用传递的变量分配给结构成员时会发生什么

c++ - 调整窗口大小时如何使用鼠标单击获取图像坐标?

python - 使用 OpenGL 在 Python 中进行 3d 绘图

Java Circle-Circle 碰撞检测