22

オブジェクトを最初にロードするとき、最大点と最小点 (x、y、z) を使用して初期 AABB を計算します。しかし、これはオブジェクト空間であり、オブジェクトは世界中を移動し、さらに重要なことに回転します。

オブジェクトが移動/回転するたびに新しい AABB を再計算するにはどうすればよいですか? これは基本的にすべてのフレームで発生します。フレームごとに新しい AABB を再計算するのは非常に集中的な操作になるのでしょうか? もしそうなら、代替手段は何ですか?

AABB を使用すると衝突検出の精度が低下することはわかっていますが、OBB よりも衝突検出コードを実装する方が簡単なので、一度に 1 つずつ実行したいと考えています。

以下の回答からいくつかの洞察を得た後の私の現在のコードは次のとおりです。

typedef struct sAxisAlignedBoundingBox {
    Vector3D bounds[8];
    Vector3D max, min;
} AxisAlignedBoundingBox;

void drawAxisAlignedBoundingBox(AxisAlignedBoundingBox box) {
    glPushAttrib(GL_LIGHTING_BIT | GL_POLYGON_BIT);

    glEnable(GL_COLOR_MATERIAL);
    glDisable(GL_LIGHTING);

    glColor3f(1.0f, 1.0f, 0.0f);

    glBegin(GL_LINE_LOOP);
        glVertex3f(box.bounds[0].x, box.bounds[0].y, box.bounds[0].z);
        glVertex3f(box.bounds[1].x, box.bounds[1].y, box.bounds[1].z);
        glVertex3f(box.bounds[2].x, box.bounds[2].y, box.bounds[2].z);
        glVertex3f(box.bounds[3].x, box.bounds[3].y, box.bounds[3].z);
    glEnd();

    glBegin(GL_LINE_LOOP);
        glVertex3f(box.bounds[4].x, box.bounds[4].y, box.bounds[4].z);
        glVertex3f(box.bounds[5].x, box.bounds[5].y, box.bounds[5].z);
        glVertex3f(box.bounds[6].x, box.bounds[6].y, box.bounds[6].z);
        glVertex3f(box.bounds[7].x, box.bounds[7].y, box.bounds[7].z);
    glEnd();

    glBegin(GL_LINE_LOOP);
        glVertex3f(box.bounds[0].x, box.bounds[0].y, box.bounds[0].z);
        glVertex3f(box.bounds[5].x, box.bounds[5].y, box.bounds[5].z);
        glVertex3f(box.bounds[6].x, box.bounds[6].y, box.bounds[6].z);
        glVertex3f(box.bounds[1].x, box.bounds[1].y, box.bounds[1].z);
    glEnd();

    glBegin(GL_LINE_LOOP);
        glVertex3f(box.bounds[4].x, box.bounds[4].y, box.bounds[4].z);
        glVertex3f(box.bounds[7].x, box.bounds[7].y, box.bounds[7].z);
        glVertex3f(box.bounds[2].x, box.bounds[2].y, box.bounds[2].z);
        glVertex3f(box.bounds[3].x, box.bounds[3].y, box.bounds[3].z);
    glEnd();

    glPopAttrib();
}

void calculateAxisAlignedBoundingBox(GLMmodel *model, float matrix[16]) {
    AxisAlignedBoundingBox box;
    float dimensions[3];

    // This will give me the absolute dimensions of the object
    glmDimensions(model, dimensions);

    // This calculates the max and min points in object space
    box.max.x = dimensions[0] / 2.0f, box.min.x = -1.0f * box.max.x;
    box.max.y = dimensions[1] / 2.0f, box.min.y = -1.0f * box.max.y;
    box.max.z = dimensions[2] / 2.0f, box.min.z = -1.0f * box.max.z;

    // These calculations are probably the culprit but I don't know what I'm doing wrong
    box.max.x = matrix[0] * box.max.x + matrix[4] * box.max.y + matrix[8] * box.max.z + matrix[12];
    box.max.y = matrix[1] * box.max.x + matrix[5] * box.max.y + matrix[9] * box.max.z + matrix[13];
    box.max.z = matrix[2] * box.max.x + matrix[6] * box.max.y + matrix[10] * box.max.z + matrix[14];
    box.min.x = matrix[0] * box.min.x + matrix[4] * box.min.y + matrix[8] * box.min.z + matrix[12];
    box.min.y = matrix[1] * box.min.x + matrix[5] * box.min.y + matrix[9] * box.min.z + matrix[13];
    box.min.z = matrix[2] * box.min.x + matrix[6] * box.min.y + matrix[10] * box.min.z + matrix[14];

    /* NOTE: If I remove the above calculations and do something like this:

             box.max = box.max + objPlayer.position;
             box.min = box.min + objPlayer.position;

             The bounding box will move correctly when I move the player, the same does not
             happen with the calculations above. It makes sense and it's very simple to move
             the box like this. The only problem is when I rotate the player, the box should
             be adapted and increased/decreased in size to properly fit the object as a AABB.
    */

    box.bounds[0] = Vector3D(box.max.x, box.max.y, box.min.z);
    box.bounds[1] = Vector3D(box.min.x, box.max.y, box.min.z);
    box.bounds[2] = Vector3D(box.min.x, box.min.y, box.min.z);
    box.bounds[3] = Vector3D(box.max.x, box.min.y, box.min.z);
    box.bounds[4] = Vector3D(box.max.x, box.min.y, box.max.z);
    box.bounds[5] = Vector3D(box.max.x, box.max.y, box.max.z);
    box.bounds[6] = Vector3D(box.min.x, box.max.y, box.max.z);
    box.bounds[7] = Vector3D(box.min.x, box.min.y, box.max.z);

    // This draw call is for testing porpuses only
    drawAxisAlignedBoundingBox(box);
}

void drawObjectPlayer(void) {
    static float mvMatrix[16];

    if(SceneCamera.GetActiveCameraMode() == CAMERA_MODE_THIRD_PERSON) {
        objPlayer.position = SceneCamera.GetPlayerPosition();
        objPlayer.rotation = SceneCamera.GetRotationAngles();

        objPlayer.position.y += -PLAYER_EYE_HEIGHT + 0.875f;

        /* Only one of the two code blocks below should be active at the same time
           Neither of them is working as expected. The bounding box doesn't is all
           messed up with either code. */

        // Attempt #1
        glPushMatrix();
            glTranslatef(objPlayer.position.x, objPlayer.position.y, objPlayer.position.z);
            glRotatef(objPlayer.rotation.y + 180.0f, 0.0f, 1.0f, 0.0f);
            glCallList(gameDisplayLists.player);
            glGetFloatv(GL_MODELVIEW_MATRIX, mvMatrix);
        glPopMatrix();

        // Attempt #2
        glPushMatrix();
            glLoadIdentity();
            glTranslatef(objPlayer.position.x, objPlayer.position.y, objPlayer.position.z);
            glRotatef(objPlayer.rotation.y + 180.0f, 0.0f, 1.0f, 0.0f);
            glGetFloatv(GL_MODELVIEW_MATRIX, mvMatrix);
        glPopMatrix();

        calculateAxisAlignedBoundingBox(objPlayer.model, mvMatrix);
    }
}

しかし、それはうまくいきません...何が間違っていますか?

4

5 に答える 5

20

変換された AABB の AABB を再計算するだけです。これは、8 つの頂点 (8 つの頂点 - 行列の乗算) の変換と、8 つの頂点間の比較を意味します。

したがって、初期化時に、モデル空間で AABB を計算します。モデルの各頂点の x、y、z ごとに、xmin、xmax、ymin、ymax などをチェックします。

フレームごとに、新しい変換行列を生成します。OpenGL では、これは glLoadIdentity に続いて glTransform/Rotate/Scale (古い API を使用している場合) で行われます。lmmilewski が言ったように、これはモデル マトリックスです。

この変換行列をもう一度計算します (たとえば、glmを使用して、OpenGL の外部で)。glGetを使用して、OpenGL の結果の行列を取得することもできます。

AABB の 8 つの頂点のそれぞれにこの行列を掛けます。行列とベクトルの乗算には glm を使用します。変換された AABB を (ワールド空間で) 取得します。おそらく回転しました(軸が整列していません)。

あなたのアルゴリズムはおそらく軸に沿ったものでしか機能しないので、あなたの質問です。したがって、変換された境界ボックスの境界ボックスを取得することにより、変換されたモデルの新しい境界ボックスを近似します。

新しい AABB の各頂点の x、y、z ごとに、xmin、xmax、ymin、ymax などをチェックします。これにより、クリッピング アルゴリズムで使用できるワールド空間 AABB が得られます。

これは最適ではありません (AABB に関して)。多くの空のスペースが得られますが、パフォーマンスに関しては、メッシュ全体の AABB を再計算するよりもはるかに優れています。


変換マトリックスに関しては、drawObjectPlayer で:

gLLoadIdentity();
glTranslatef(objPlayer.position.x, objPlayer.position.y, objPlayer.position.z);
glRotatef(objPlayer.rotation.y + 180.0f, 0.0f, 1.0f, 0.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, mvMatrix);
// Now you've got your OWN Model Matrix (don't trust the GL_MODELVIEW_MATRIX flag : this is a workaround, and I know what I'm doing ^^ )

gLLoadIdentity(); // Reset the matrix so that you won't make the transformations twice
gluLookAt( whatever you wrote here earlier )
glTranslatef(objPlayer.position.x, objPlayer.position.y, objPlayer.position.z);
glRotatef(objPlayer.rotation.y + 180.0f, 0.0f, 1.0f, 0.0f);
// Now OpenGL is happy, he's got his MODELVIEW matrix correct ( gluLookAt is the VIEW part; Translate/Rotate is the MODEL part
glCallList(gameDisplayLists.player); // Transformed correcty

それ以上説明することはできません...コメントで述べたように、あなたはそれを2回しなければなりませんでした。ところで、OpenGL 3 では、これらの問題や醜い回避策はありません。なぜなら、自分の行列について完全に責任を負うからです。OpenGL 2 で同等:

glm::mat4 ViewMatrix = glm::LookAt(...);
glm::mat4 ModelMatrix = glm::rotate() * glm::translate(...);
// Use ModelMatrix for whatever you want
glm::mat4 ModelViewMatrix = ViewMatrix * ModelMatrix;
glLoadMatrix4fv( &ModelViewMatrix[0][0] ); // In OpenGL 3 you would use an uniform instead

ずっときれいですよね?

于 2011-05-19T09:19:11.097 に答える
2

これを行うには、すべての頂点をループし、世界での位置を計算し (modelview を乗算)、すべてのオブジェクト内の最小/最大頂点座標を見つける必要があります (初めて計算するときと同様)。

再計算する必要がないように、AABB を少しスケーリングできます - 係数 sqrt(2) で拡大するだけで十分です - 回転したオブジェクトは常に AABB に収まります。

また、どの方向に回転させるか (?) の質問もあります。常に 1 つである場合、その方向にのみ AABB を拡大できます。

必要に応じて、AABB の代わりに境界球を使用できます。次に、回転を気にせず、スケーリングは問題になりません。

于 2011-05-19T06:37:13.153 に答える
1

AABB @ Stack Overflowでの以前の応答を引用するには:

「悲しいことに、キャラクターがローテーションする場合は、AABB を再計算する必要があります。..

シュクルメデル


回答者の提案と私の提案は、AABB が機能するようになったら指向性バウンディング ボックスを実装することです。また、メッシュの一部の aabb を作成して、オブジェクトごとに 1 つの巨大なボックスよりも高い精度で衝突検出をファッジできることに注意してください。

于 2011-05-19T04:28:10.567 に答える
-6

GPU を使用しないのはなぜですか? 今日、私はいくつかのフレームをレンダリングすることで、この問題の解決策を提案しました。

  1. 一時的にカメラをオブジェクトの上、その上に置き、オブジェクトに向けます。
  2. ライトなどを使用せずに、オブジェクトのみをレンダリングします。
  3. 正投影も使用します。
  4. 次に、フレーム バッファを読み取ります。黒いピクセルの行と列は、モデルが存在しないことを意味します。白いピクセルにヒット - モデル AABB 境界の 1 つにヒットします。

これがすべての場合の解決策ではないことはわかっていますが、事前の知識があれば、これは非常に効率的です。

画面外でのレンダリングについては、こちらを参照してください。

于 2016-04-17T21:17:14.240 に答える