簡體   English   中英

在OpenGL中實現邊界球碰撞

[英]Implementing bounding sphere collision in OpenGL

我理解邊界球碰撞的基本原理,但實施它讓我感到困惑。

如果我在數組中定義了兩個立方體:cube1 []和cube2 [],每個數組由組成每個三角形的GLfloats組成。 那我怎么能先計算每個立方體的中心點呢? 以及如何獲得圍繞此球體的半徑?

計算這個需要什么數學?

編輯:為我的問題提供更多說明。 假設我使用以下數組定義了一個多維數據集:

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的邊長都是(sqrt(3)*sqrt(2))/2倍(對於碰撞,給出大約1.4倍的准確結果。

您可以選擇最適合您需求的產品。 第一個具有最佳的“角落”檢測,但是他最差的“面部”檢測,第二個具有最佳的“面部”檢測和最差的“角落”檢測,而第三個具有或多或少的第一個“平均”二,如果所有情況都是可能的話,給它最“可靠”的准確性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM