Я пытаюсь реализовать модель камеры в Delphi/OpenGL после описания, данного в OpenGL SuperBible. Камера имеет положение, вектор вперед и вектор вверх. Перемещение камеры, кажется, работает нормально, но когда я пытаюсь повернуть камеру в соответствии с прямым вектором, я теряю из виду свой объект.

function TCamera.GetCameraOrientation: TMatrix4f;
var
  x, z: T3DVector;
begin
  z := T3DVector.Create(-FForward.X, -FForward.y, -FForward.z);
  x := T3DVector.Cross(z, FUp);

  result[0, 0] := x.X;
  result[1, 0] := x.Y;
  result[2, 0] := x.Z;
  result[3, 0] := 0;

  result[0, 1] := FUp.X;
  result[1, 1] := FUp.Y;
  result[2, 1] := FUp.Z;
  result[3, 1] := 0;

  result[0, 2] := z.x;
  result[1, 2] := z.y;
  result[2, 2] := z.z;
  result[3, 2] := 0;

  result[0, 3] := 0;
  result[1, 3] := 0;
  result[2, 3] := 0;
  result[3, 3] := 1;
end;

procedure TCamera.ApplyTransformation;
var
  cameraOrient: TMatrix4f;
  a, b, c: TMatrix4f;
begin
  cameraOrient := getcameraOrientation;
  glMultMatrixf(@cameraOrient);
  glTranslatef(-FPosition.x, -FPosition.y, -FPosition.z);
end;

Учитывая положение (0, 0, -15), прямой вектор (0 0 1) и восходящий вектор (0 1 0), я ожидал получить матрицу идентичности из метода getCameraOrientation, но вместо этого я получаю

(1, 0,  0, 0)
(0, 1,  0, 0)
(0, 0, -1, 0)
(0, 0,  0, 1)

Если я изменю прямой вектор на (0 0 -1), я получу следующую матрицу:

(-1, 0, 0, 0)
( 0, 1, 0, 0)
( 0, 0, 1, 0)
( 0, 0, 0, 1)

После вызова glMultMatrix() и glTranslate() функция glGet() дает мне следующую GL_MODELVIEW_MATRIX:

( 1, 0,  0, 0)
( 0, 1,  0, 0)
( 0, 0, -1, 0)
( 0, 0, 15, 1)

Я бы ожидал, что 15 будут в столбце 4, строке 3, а не в столбце 3, строке 4.

Может ли кто-нибудь увидеть, где я ошибаюсь?

РЕДАКТИРОВАТЬ: исходный код из OpenGL SuperBible:

    inline void GetCameraOrientation(M3DMatrix44f m)
        {
        M3DVector3f x, z;

        // Make rotation matrix
        // Z vector is reversed
        z[0] = -vForward[0];
        z[1] = -vForward[1];
        z[2] = -vForward[2];

        // X vector = Y cross Z 
        m3dCrossProduct(x, vUp, z);

        // Matrix has no translation information and is
        // transposed.... (rows instead of columns)
        #define M(row,col)  m[col*4+row]
           M(0, 0) = x[0];
           M(0, 1) = x[1];
           M(0, 2) = x[2];
           M(0, 3) = 0.0;
           M(1, 0) = vUp[0];
           M(1, 1) = vUp[1];
           M(1, 2) = vUp[2];
           M(1, 3) = 0.0;
           M(2, 0) = z[0];
           M(2, 1) = z[1];
           M(2, 2) = z[2];
           M(2, 3) = 0.0;
           M(3, 0) = 0.0;
           M(3, 1) = 0.0;
           M(3, 2) = 0.0;
           M(3, 3) = 1.0;
        #undef M 
        }

    inline void ApplyCameraTransform(bool bRotOnly = false)    
        {
        M3DMatrix44f m;

        GetCameraOrientation(m);

        // Camera Transform   
        glMultMatrixf(m);

        // If Rotation only, then do not do the translation
        if(!bRotOnly)
            glTranslatef(-vOrigin[0], -vOrigin[1], -vOrigin[2]);
        }
2
Vegar 24 Июл 2009 в 14:08

2 ответа

Учитывая ваш код getcameraOrientation, результирующая матрица совершенно очевидна: forward = (0, 0, 1) дает z = (0, 0, -1), что соответствует 3-й строке матрицы. Перекрестное произведение z = (0, 0, -1) и FUp = (0, 1, 0) дает x = (1, 0, 0), что соответствует первой строке матрицы. Вторая строка - это просто копия FUp, а 4-я строка просто исправлена.

Я на самом деле не понимаю, чего вы хотите добиться, но когда вы поворачиваете камеру, вы явно теряете из виду свой объект. В реальном мире если посмотреть в точку и повернуть голову - то же самое. Вы пробовали поменять порядок перевода и вращения?

2
Uwe Raabe 24 Июл 2009 в 20:00
Он должен иметь возможность вращаться вокруг вектора движения вперед, не теряя из виду свой объект.
 – 
Nosredna
24 Июл 2009 в 20:02
Я не вращаюсь вокруг прямого вектора, я вращаюсь по прямому вектору. В моем случае, когда вверх идет прямо по оси Y, это будет то же самое, что и вращение вокруг оси Y. поскольку движение вперед идет прямо по оси Z, никакого вращения быть не должно. Но есть, поэтому я теряю след своего объекта...
 – 
Vegar
24 Июл 2009 в 20:37
2
Ну, результирующая матрица в вашем первом случае возвращает ось z. Поскольку вы «смотрите» на положительную ось z, ее возврат будет просто «оглядываться назад». Так почему же z вычисляется из отрицательного прямого вектора, а не является его копией?
 – 
Uwe Raabe
25 Июл 2009 в 16:15
Я предполагаю, что настоящая проблема в том, что я не понимаю, что я здесь делаю... :-/ Добавил исходный код из OpenGL SuperBible в начальный пост.
 – 
Vegar
26 Июл 2009 в 14:11
Если вы не понимаете, что делаете, попробуйте использовать оболочку вокруг OpenGL, например GLScene.
 – 
Uwe Raabe
26 Июл 2009 в 15:24

Матрица идентичности

Я не уверен, почему SuperBible предлагает использовать (-FForward.X, -FForward.y, -FForward.z) для создания вектора Z. Если вы уберете знаки минус, вы получите матрицу идентичности, которую вы ожидаете, когда ваш прямой вектор равен (0, 0, 1).

Если вы хотите сохранить знаки минус и хотите, чтобы прямой вектор (0, 0, -1) создавал единичную матрицу, вам нужно изменить векторное произведение с Cross(z, FUp) на Cross(FUp, z), так как OpenGL использует правостороннюю систему координат. См. Перекрестный продукт.

15 не в том месте

Я согласен с вами, что ожидаю, что матрица перевода будет выглядеть так:

(1, 0, 0, x)
(0, 1, 0, y)
(0, 0, 1, z)
(0, 0, 0, 1)

Однако обратите внимание, что OpenGL хранит свою матрицу в порядке столбцов, а не в порядке строк, поэтому, когда вы glGet матрицу представления модели, она будет отображаться в следующем порядке:

(m[0],  m[4],  m[8], m[12])
(m[1],  m[5],  m[9], m[13])
(m[2],  m[6], m[10], m[14])
(m[3],  m[7], m[11], m[15])

Если вы думали, что это было в порядке строк, то это может быть причиной путаницы.

1
Incredulous Monk 29 Июл 2009 в 09:37