4

錐台カリングを実装し、錐台平面との交差の境界ボックスをチェックしています。錐台の更新を一時停止する機能を追加しました。これにより、錐台のカリングが正しく機能しているかどうかを確認できます。一時停止した後に振り向くと、後ろには何もレンダリングされず、左側と右側には何もレンダリングされず、期待どおりに先細りになります。クリップの距離 (ファー プレーン) を超えても、それらはレンダリングされますが、フラスタムの更新またはバウンディング ボックスのチェック コードに問題があるのか​​、それとも間違ったマトリックスを使用しているのか、または何を使用しているのかわかりません。距離を 3000.0f の射影行列に入れると、バウンディング ボックスがまだ錐台内にあることが示されますが、そうではありません。

モデルビュー マトリックスを作成する場所は次のとおりです。

projectionMatrix = glm::perspective(newFOV, 4.0f / 3.0f, 0.1f, 3000.0f);

viewMatrix = glm::mat4(1.0);
viewMatrix = glm::scale(viewMatrix, glm::vec3(1.0, 1.0, -1.0));
viewMatrix = glm::rotate(viewMatrix, anglePitch, glm::vec3(1.0, 0.0, 0.0));
viewMatrix = glm::rotate(viewMatrix, angleYaw, glm::vec3(0.0, 1.0, 0.0));
viewMatrix = glm::translate(viewMatrix, glm::vec3(-x, -y, -z));

modelViewProjectiomMatrix = projectionMatrix * viewMatrix;

Z 方向に -1 でスケーリングする理由は、レベルが DirectX でレンダリングされるように設計されているため、Z 方向を逆にするためです。

錐台を更新する場所は次のとおりです。

void CFrustum::calculateFrustum()
{
    glm::mat4 mat = camera.getModelViewProjectionMatrix();

    // Calculate the LEFT side
    m_Frustum[LEFT][A] = (mat[0][3]) + (mat[0][0]);
    m_Frustum[LEFT][B] = (mat[1][3]) + (mat[1][0]);
    m_Frustum[LEFT][C] = (mat[2][3]) + (mat[2][0]);
    m_Frustum[LEFT][D] = (mat[3][3]) + (mat[3][0]);

    // Calculate the RIGHT side
    m_Frustum[RIGHT][A] = (mat[0][3]) - (mat[0][0]);
    m_Frustum[RIGHT][B] = (mat[1][3]) - (mat[1][0]);
    m_Frustum[RIGHT][C] = (mat[2][3]) - (mat[2][0]);
    m_Frustum[RIGHT][D] = (mat[3][3]) - (mat[3][0]);

    // Calculate the TOP side
    m_Frustum[TOP][A] = (mat[0][3]) - (mat[0][1]);
    m_Frustum[TOP][B] = (mat[1][3]) - (mat[1][1]);
    m_Frustum[TOP][C] = (mat[2][3]) - (mat[2][1]);
    m_Frustum[TOP][D] = (mat[3][3]) - (mat[3][1]);

    // Calculate the BOTTOM side
    m_Frustum[BOTTOM][A] = (mat[0][3]) + (mat[0][1]);
    m_Frustum[BOTTOM][B] = (mat[1][3]) + (mat[1][1]);
    m_Frustum[BOTTOM][C] = (mat[2][3]) + (mat[2][1]);
    m_Frustum[BOTTOM][D] = (mat[3][3]) + (mat[3][1]);

    // Calculate the FRONT side
    m_Frustum[FRONT][A] = (mat[0][3]) + (mat[0][2]);
    m_Frustum[FRONT][B] = (mat[1][3]) + (mat[1][2]);
    m_Frustum[FRONT][C] = (mat[2][3]) + (mat[2][2]);
    m_Frustum[FRONT][D] = (mat[3][3]) + (mat[3][2]);

    // Calculate the BACK side
    m_Frustum[BACK][A] = (mat[0][3]) - (mat[0][2]);
    m_Frustum[BACK][B] = (mat[1][3]) - (mat[1][2]);
    m_Frustum[BACK][C] = (mat[2][3]) - (mat[2][2]);
    m_Frustum[BACK][D] = (mat[3][3]) - (mat[3][2]);

    // Normalize all the sides
    NormalizePlane(m_Frustum, LEFT);
    NormalizePlane(m_Frustum, RIGHT);
    NormalizePlane(m_Frustum, TOP);
    NormalizePlane(m_Frustum, BOTTOM);
    NormalizePlane(m_Frustum, FRONT);
    NormalizePlane(m_Frustum, BACK);
}

最後に、バウンディング ボックスをチェックします。

bool CFrustum::BoxInFrustum( float x, float y, float z, float x2, float y2, float z2)
{
    // Go through all of the corners of the box and check then again each plane
    // in the frustum.  If all of them are behind one of the planes, then it most
    // like is not in the frustum.
    for(int i = 0; i < 6; i++ )
    {
        if(m_Frustum[i][A] * x  + m_Frustum[i][B] * y  + m_Frustum[i][C] * z  + m_Frustum[i][D] > 0)  continue;
        if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y  + m_Frustum[i][C] * z  + m_Frustum[i][D] > 0)  continue;
        if(m_Frustum[i][A] * x  + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z  + m_Frustum[i][D] > 0)  continue;
        if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z  + m_Frustum[i][D] > 0)  continue;
        if(m_Frustum[i][A] * x  + m_Frustum[i][B] * y  + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0)  continue;
        if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y  + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0)  continue;
        if(m_Frustum[i][A] * x  + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0)  continue;
        if(m_Frustum[i][A] * x2 + m_Frustum[i][B] * y2 + m_Frustum[i][C] * z2 + m_Frustum[i][D] > 0)  continue;

        // If we get here, it isn't in the frustum
        return false;
    }

    // Return a true for the box being inside of the frustum
    return true;
}
4

3 に答える 3

1

私も同じ問題を抱えていました。

Vinny Rose さんの回答を参考に、正規化された平面を作成する関数を確認したところ、エラーが見つかりました。

これは、間違った計算をコメントアウトした修正版です。

plane plane_normalized(float A, float B, float C, float D) {
    // Wrong, this is not a 4D vector
    // float nf = 1.0f / sqrtf(A * A + B * B + C * C + D * D);

    // Correct
    float nf = 1.0f / sqrtf(A * A + B * B + C * C);

    return (plane) {{
        nf * A,
        nf * B,
        nf * C,
        nf * D
    }};
}

私の推測では、あなたのNormalizePlane関数は似たようなことをしていると思います。

正規化のポイントは、簡単な半空間テストを行えるように平面をヘッセ正規形にすることです。4 次元ベクトルの場合と同じように平面を正規化すると、法線方向 [A、B、C] は正しくなりますが、オフセット D は正しくありません。

上、下、左、右の平面に対してポイントをテストすると、原点を通過し、近い平面が気付かないほど近くにある可能性があるため、正しい結果が得られると思います。(境界球のテストは失敗します。)

正しい正規化を復元すると、フラスタム カリングは期待どおりに機能しました。

于 2014-03-27T21:14:15.320 に答える
1

特に射影行列の設定方法に関して、いくつかのことに気付きました。まず、何らかのラッパーまたは変な API を使用していない限り、gluProject は値を返しません。gluLookAt はより頻繁に使用されます。

次に、スケール、回転、および変換関数がモデル ビュー マトリックスを変更することを目的としていると仮定すると、それらの順序を逆にする必要があります。OpenGL は実際にはオブジェクトを動かしません。代わりに、原点を効果的に移動し、<0,0,0> の新しい定義を使用して各オブジェクトをレンダリングします。したがって、レンダリングしたい場所に「移動」し、必要に応じて軸を回転させてから、グリッドを伸ばします。

クリッピングの問題に関しては、glClipPlane()をよく見てください。他のすべてがほとんど機能するが、丸めエラーがあるように思われる場合は、perspective(,,,) 関数のニア クリッピング プレーンを 0.1 から 1.0 に変更してみてください (値が小さいほど、Z バッファーが混乱する傾向があります)。

なじみのない構文がたくさんあるので、何らかのラッパーを使用していると思います。しかし、ここに私が使用している私自身の GL プロジェクトからの (Qt) コードの一部があります。役立つかもしれません、わかりません:

//This gets called during resize, as well as once during initialization
void GLWidget::resizeGL(int width, int height) {
  int side = qMin(width, height);
  padX = (width-side)/2.0;
  padY = (height-side)/2.0;
  glViewport(padX, padY, side, side);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(60.0, 1.0, 1.0, 2400.0);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

//This fragment gets called at the top of every paint event:
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glPushMatrix();

  glLightfv(GL_LIGHT0, GL_POSITION, FV0001);

  camMain.stepVars();

  gluLookAt(camMain.Pos[0],camMain.Pos[1],camMain.Pos[2],
            camMain.Aim[0],camMain.Aim[1],camMain.Aim[2],   
            0.0,1.0,0.0);

  glPolygonMode(GL_FRONT_AND_BACK, drawMode);

//And this fragment represents a typical draw event
void GLWidget::drawFleet(tFleet* tIn) {
  if (tIn->firstShip != 0){
    glPushMatrix();

    glTranslatef(tIn->Pos[0], tIn->Pos[1], tIn->Pos[2]);
    glRotatef(tIn->Yaw, 0.0, 1.0, 0.0);
    glRotatef(tIn->Pitch,0,0,1);

    drawShip(tIn->firstShip);

    glPopMatrix();
  }
}

私は、あなたが GL に慣れていないことを前提に取り組んでいるので、少し衒学的に外れた場合は申し訳ありません。

于 2012-09-02T08:21:32.580 に答える
0

私が考えていることは次のとおりです。ファー プレーンは正しく定義されていますが、私のテストでは、そのプレーンの D 値が小さすぎます。そのため、オブジェクトはファー プレーンの正しい側にあるものとして受け入れられています。これは、数学によってファー プレーンが実際には必要以上に遠くにあることが強制されているためです。

別のアプローチを試してください: (http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-extracting-the-planes/)

float tang = tanf(fov * PI / 360.0f);
float nh = near * tang; // near height
float nw = nh * aspect; // near width
float fh = far * tang; // far height
float fw = fh * aspect; // far width

glm::vec3 p,nc,fc,X,Y,Z,Xnw,Ynh;

//camera position
p = glm::vec3(viewMatrix[3][0],viewMatrix[3][1],viewMatrix[3][2]);

// the left vector
glm::vec3 X = glm::vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);
// the up vector
glm::vec3 Y = glm::vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);
// the look vector
glm::vec3 Z = glm::vec3(viewMatrix[0][2], viewMatrix[1][2], viewMatrix[2][2]);

nc = p - Z * near; // center of the near plane
fc = p - Z * far; // center of the far plane

// the distance to get to the left or right edge of the near plane from nc
Xnw = X * nw;
// the distance to get to top or bottom of the near plane from nc
Ynh = Y * nh;
// the distance to get to the left or right edge of the far plane from fc
Xfw = X * fw;
// the distance to get to top or bottom of the far plane from fc
Yfh = Y * fh;

ntl = nc + Ynh - Xnw; // "near top left"
ntr = nc + Ynh + Xnw; // "near top right" and so on
nbl = nc - Ynh - Xnw;
nbr = nc - Ynh + Xnw;

ftl = fc + Yfh - Xfw;
ftr = fc + Yfh + Xfw;
fbl = fc - Yfh - Xfw;
fbr = fc - Yfh + Xfw;

m_Frustum[TOP] = planeWithPoints(ntr,ntl,ftl);
m_Frustum[BOTTOM] = planeWithPoints(nbl,nbr,fbr);
m_Frustum[LEFT] = planeWithPoints(ntl,nbl,fbl);
m_Frustum[RIGHT] = planeWithPoints(nbr,ntr,fbr);
m_Frustum[FRONT] = planeWithPoints(ntl,ntr,nbr);
m_Frustum[BACK] = planeWithPoints(ftr,ftl,fbl);

// Normalize all the sides
NormalizePlane(m_Frustum, LEFT);
NormalizePlane(m_Frustum, RIGHT);
NormalizePlane(m_Frustum, TOP);
NormalizePlane(m_Frustum, BOTTOM);
NormalizePlane(m_Frustum, FRONT);
NormalizePlane(m_Frustum, BACK);

次に、planeWithPoints は次のようになります。

planeWithPoints(glm::vec3 a, glm::vec3 b, glm::vec3 c){
    double A = a.y * (b.z - c.z) + b.y * (c.z - a.z) + c.y * (a.z - b.z);
    double B = a.z * (b.x - c.x) + b.z * (c.x - a.x) + c.z * (a.x - b.x);
    double C = a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y);
    double D = -(a.x * (b.y * c.z - c.y * b.z) + b.x * (c.y * a.z - a.y * c.z) + c.x * (a.y * b.z - b.y * a.z));
    return glm::vec4(A,B,C,D);
}

上記のいずれもテストしませんでした。ただし、必要な場合は、元のリファレンスがあります。

前の回答: 行列が 2D 配列で表される場合、OpenGL および GLSL 行列は列優先の順序で格納およびアクセスされます。これは、GLSL 標準に準拠しているため、GLM にも当てはまります。

錐台の作成を次のように変更する必要があります。

// Calculate the LEFT side (column1 + column4)
m_Frustum[LEFT][A] = (mat[3][0]) + (mat[0][0]);
m_Frustum[LEFT][B] = (mat[3][1]) + (mat[0][1]);
m_Frustum[LEFT][C] = (mat[3][2]) + (mat[0][2]);
m_Frustum[LEFT][D] = (mat[3][3]) + (mat[0][3]);

// Calculate the RIGHT side (-column1 + column4)
m_Frustum[RIGHT][A] = (mat[3][0]) - (mat[0][0]);
m_Frustum[RIGHT][B] = (mat[3][1]) - (mat[0][1]);
m_Frustum[RIGHT][C] = (mat[3][2]) - (mat[0][2]);
m_Frustum[RIGHT][D] = (mat[3][3]) - (mat[0][3]);

// Calculate the TOP side (-column2 + column4)
m_Frustum[TOP][A] = (mat[3][0]) - (mat[1][0]);
m_Frustum[TOP][B] = (mat[3][1]) - (mat[1][1]);
m_Frustum[TOP][C] = (mat[3][2]) - (mat[1][2]);
m_Frustum[TOP][D] = (mat[3][3]) - (mat[1][3]);

// Calculate the BOTTOM side (column2 + column4)
m_Frustum[BOTTOM][A] = (mat[3][0]) + (mat[1][0]);
m_Frustum[BOTTOM][B] = (mat[3][1]) + (mat[1][1]);
m_Frustum[BOTTOM][C] = (mat[3][2]) + (mat[1][2]);
m_Frustum[BOTTOM][D] = (mat[3][3]) + (mat[1][3]);

// Calculate the FRONT side (column3 + column4)
m_Frustum[FRONT][A] = (mat[3][0]) + (mat[2][0]);
m_Frustum[FRONT][B] = (mat[3][1]) + (mat[2][1]);
m_Frustum[FRONT][C] = (mat[3][2]) + (mat[2][2]);
m_Frustum[FRONT][D] = (mat[3][3]) + (mat[2][3]);

// Calculate the BACK side (-column3 + column4)
m_Frustum[BACK][A] = (mat[3][0]) - (mat[2][0]);
m_Frustum[BACK][B] = (mat[3][1]) - (mat[2][1]);
m_Frustum[BACK][C] = (mat[3][2]) - (mat[2][2]);
m_Frustum[BACK][D] = (mat[3][3]) - (mat[2][3]);

于 2012-09-04T14:35:24.207 に答える