Вращение матрицы преобразования Opengl View

Недавно я реализовал простую программу Opengl, которая составляет сцену из объектов, я применил большую часть матриц преобразования и проекции, так что я могу rotate transform & scale objects, move my camera through z & x coordinates and applied perspective projection, однако, когда дело доходит до поворота камеры, все становится странно, моя матрица поворота поскольку моя камера - это просто матрица вращения, которая равномерно вращает мир, однако, когда я вращаю мир так, чтобы смотреть вверх; +y; и когда я двигаюсь вперед, кажется, что камера не движется в том направлении, куда она смотрит; as it is the case in FPS games моя камера движется относительно мирового пространства, я знаю, что мне не хватает векторов, указывающих направления в x,y,z координатах, но я Я не могу объединить эти векторы с матрицей моей камеры (преобразование вида), большая часть учебника в Интернете либо описывает это в виде блок-схемы, либо использует обычную функцию gluLookAt (), мне действительно нужно краткое объяснение преобразований вида и, в частности, поворота камеры и как я должен реализовать это в своих матрицах, моя окончательная матрица выглядит следующим образом:

resultTransform = перспективаTrans * cameraTrans * modelTrans;

где:

перспективаTrans = применяет только преобразование перспективной проекции

cameraTrans = - это комбинация матриц поворота и трансляции, которые влияют на все объекты в сцене.

modelTrans = - это преобразование, применяемое к моделям.

Файл Matrix4X4.cpp:

#include "Matrix4X4.h"

using namespace std;


////////////////////////////////// Constructor Declerations ////////////////////////////////

Matrix4X4::Matrix4X4()
{
setIdentity();
}

Matrix4X4::Matrix4X4(float value)
{
for(int i = 0 ; i < 4; i++)
    for ( int j = 0; j < 4; j++)
        Matrix[i][j] = value;

}

/////////////////////////////////////////////////////////////////////////////////







////////////////////////////// Destructor Decleration //////////////////////////////
Matrix4X4::~Matrix4X4()
{

}

///////////////////////////////////////////////////////////////////////////////////







/////////////////////// Set Identity Matrix /////////////////////////////////////////

void Matrix4X4::setIdentity()
{
Matrix[0][0] =1;   Matrix[0][1] = 0;  Matrix[0][2] = 0;      Matrix[0][3] = 0;
Matrix[1][0] =0;   Matrix[1][1] = 1;  Matrix[1][2] = 0;      Matrix[1][3] = 0;
Matrix[2][0] =0;   Matrix[2][1] = 0;  Matrix[2][2] = 1;      Matrix[2][3] = 0;
Matrix[3][0] =0;   Matrix[3][1] = 0;  Matrix[3][2] = 0;      Matrix[3][3] = 1;


}

///////////////////////////////////////////////////////////////////////////////////






///////////////////////// Set Translation Matrix //////////////////////////////////

Matrix4X4 Matrix4X4::setTranslation(float x,float y,float z)
{


Matrix[0][0] =1;   Matrix[0][1] = 0;  Matrix[0][2] = 0;      Matrix[0][3] = x;
Matrix[1][0] =0;   Matrix[1][1] = 1;  Matrix[1][2] = 0;      Matrix[1][3] = y;
Matrix[2][0] =0;   Matrix[2][1] = 0;  Matrix[2][2] = 1;      Matrix[2][3] = z;
Matrix[3][0] =0;   Matrix[3][1] = 0;  Matrix[3][2] = 0;      Matrix[3][3] = 1;

return  *this;

}
/////////////////////////////////////////////////////////////////////////////////





////////////////////////////////////// Set Rotation Matrix     ///////////////////////////////////////////

Matrix4X4 Matrix4X4::setRotation(float x,float y,float z)
{
Matrix4X4 xRot;
Matrix4X4 yRot;
Matrix4X4 zRot;

x = (float)x * 3.14/ 180.0;
y = (float)y * 3.14/ 180.0;
z = (float)z * 3.14/ 180.0;



xRot.Matrix[0][0] =1;         xRot.Matrix[0][1] = 0;        xRot.Matrix[0][2] = 0;            xRot.Matrix[0][3] = 0;
xRot.Matrix[1][0] =0;         xRot.Matrix[1][1] = cosf(x);  xRot.Matrix[1][2] = -sinf(x);   xRot.Matrix[1][3] = 0;
xRot.Matrix[2][0] =0;         xRot.Matrix[2][1] = sinf(x);  xRot.Matrix[2][2] = cosf(x);    xRot.Matrix[2][3] = 0;
xRot.Matrix[3][0] =0;         xRot.Matrix[3][1] = 0;        xRot.Matrix[3][2] = 0;          xRot.Matrix[3][3] = 1;

yRot.Matrix[0][0] = cosf(y);  yRot.Matrix[0][1] = 0;        yRot.Matrix[0][2] = -sinf(y);   yRot.Matrix[0][3] = 0;
yRot.Matrix[1][0] =0;         yRot.Matrix[1][1] = 1;        yRot.Matrix[1][2] = 0;          yRot.Matrix[1][3] = 0;
yRot.Matrix[2][0] = sinf(y);  yRot.Matrix[2][1] = 0;        yRot.Matrix[2][2] = cosf(y);    yRot.Matrix[2][3] = 0;
yRot.Matrix[3][0] =0;         yRot.Matrix[3][1] = 0;        yRot.Matrix[3][2] = 0;          yRot.Matrix[3][3] = 1;

zRot.Matrix[0][0] = cosf(z);  zRot.Matrix[0][1] = -sinf(z); zRot.Matrix[0][2] = 0;          zRot.Matrix[0][3] = 0;
zRot.Matrix[1][0] = sinf(z);  zRot.Matrix[1][1] = cosf(z);  zRot.Matrix[1][2] = 0;          zRot.Matrix[1][3] = 0;
zRot.Matrix[2][0] =0;         zRot.Matrix[2][1] = 0;        zRot.Matrix[2][2] = 1;          zRot.Matrix[2][3] = 0;
zRot.Matrix[3][0] =0;         zRot.Matrix[3][1] = 0;        zRot.Matrix[3][2] = 0;          zRot.Matrix[3][3] = 1;


return (zRot * yRot * xRot) ;

}

////////////////////////////////////////////////////////////////////////////////////////////////////






//////////////////////////////////////// Set Scale Matrix //////////////////////////////////////////

Matrix4X4 Matrix4X4::setScale(float x,float y,float z)
{


Matrix[0][0] =x;   Matrix[0][1] = 0;  Matrix[0][2] = 0;      Matrix[0][3] = 0;
Matrix[1][0] =0;   Matrix[1][1] = y;  Matrix[1][2] = 0;      Matrix[1][3] = 0;
Matrix[2][0] =0;   Matrix[2][1] = 0;  Matrix[2][2] = z;      Matrix[2][3] = 0;
Matrix[3][0] =0;   Matrix[3][1] = 0;  Matrix[3][2] = 0;      Matrix[3][3] = 1;

return *this;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////





///////////////////////////////// Set Perspective Projection ///////////////////////////////////////

void Matrix4X4::setPerspective(float fov,float aRatio,float zNear,float zFar)
{


fov = (fov/2) * 3.14 / 180.0;
float tanHalfFOV = tanf(fov);
float zRange = zNear - zFar;


 Matrix[0][0] =1.0f / (tanHalfFOV * aRatio);   Matrix[0][1] = 0;                  Matrix[0][2] = 0;                         Matrix[0][3] = 0;
 Matrix[1][0] =0;                              Matrix[1][1] = 1.0f / tanHalfFOV;  Matrix[1][2] = 0;                         Matrix[1][3] = 0;
 Matrix[2][0] =0;                              Matrix[2][1] = 0;                  Matrix[2][2] = (-zNear - zFar)/ zRange;   Matrix[2][3] = 2* zFar * zNear / zRange;
 Matrix[3][0] =0;                              Matrix[3][1] = 0;                  Matrix[3][2] = 1;                         Matrix[3][3] = 0;



}
/////////////////////////////////////////////////////////////////////////////////////////////////////////





////////////////////////////////////// Getters & Setters ////////////////////////////////////////////

float * Matrix4X4::getMat()
{
return (float *) Matrix;
}


float Matrix4X4::getMember(int x, int y) const
{
return Matrix[x][y];
}


void Matrix4X4::setMat(int row,int col,float value)
{
Matrix[row][col] = value;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////// (*) Operator Overload //////////////////////////////////////

Matrix4X4 operator * (const Matrix4X4 & lhs,const Matrix4X4 & rhs)
{

Matrix4X4 result;

    for(int i = 0 ; i < 4; i++)
        for ( int j = 0; j < 4; j++)
            result.setMat(i, j,  lhs.getMember(i,0) * rhs.getMember(0, j) +
                            lhs.getMember(i,1) * rhs.getMember(1, j) +
                            lhs.getMember(i,2) * rhs.getMember(2, j) +
                            lhs.getMember(i,3) * rhs.getMember(3, j));


        return result;
}
//////////////////////////////////////////////////////////////////////////////////////////////////

Код преобразования, который я использую в своем основном блоке:

        SDL_PumpEvents();

        for (int x = 0; x< 256; x++)
        {
            if (state[x] == 1 )
            {
                if(x  == 26)
                    tranForward -= 0.001;
                if (x == 22)
                    tranForward += 0.001;
                if (x == 4)
                    tranRight += 0.0009;
                if (x == 7)
                    tranRight -= 0.0009;

                if (x == 82)
                    lookUp += 0.02;
                if (x == 81)
                    lookUp -= 0.02;
                if (x == 80)
                    lookRight -= 0.02;
                if (x == 79)
                    lookRight += 0.02;
            }
        }





        modelTrans =  Translation.setTranslation(0, 0, 5) * Scale.setScale(0.5, 0.5, 0.5);
        camTrans   =  Rotation.setRotation(lookUp, lookRight, 0) * Translation.setTranslation(tranRight, 0, tranForward);
        Projection.setPerspective(70, win.getWidth()/win.getHeight(), 0.1, 1000);


        result =  Projection * camTrans * modelTrans;




       glUniformMatrix4fv(uniformloc, 1, GL_TRUE, result.getMat());

person BulBul    schedule 22.04.2014    source источник
comment
Не видя конкретного кода, я могу предположить, что вращение и трансляция вашей камеры происходят в неправильном порядке. Выполнение поворота с последующим перемещением не имеет такого же эффекта, как выполнение перемещения с последующим вращением.   -  person user3256930    schedule 23.04.2014
comment
@ user3256930 Я добавил свой класс матрицы и код умножения преобразования, я использую собственный класс матрицы с перегрузкой оператора.   -  person BulBul    schedule 23.04.2014


Ответы (2)


Умножение матриц не имеет тех же правил, что и скалярное умножение, и в вашем случае A * B НЕ равно B * A при умножении матриц. Если остальная часть кода в порядке, ваше решение может просто поворачиваться

result =  Projection * camTrans * modelTrans;

в

result =  Projection * (modelTrans * camTrans);

Всегда следите за порядком умножения и скобками, когда имеете дело с чем-либо, кроме скалярных значений.

В общем, когда вы комбинируете матрицу сдвига и вращения, вам нужно думать в собственной пространственной системе координат матрицы, что означает, что вы играете в FPS:

Умножение rotation*translation означает, что объект будет сначала вращаться, а затем перемещаться, что означает, что положение объекта будет зависеть от уже примененного поворота, а поворот на 180 градусов будет перемещать объект назад с точки зрения третьего вида.

Умножение translation*rotation означает, что объект сначала будет перемещаться, а затем вращаться, что означает, что фактически он будет перемещен в одном и том же направлении независимо от поворота, только направление, куда обращен объект, будет изменено матрицей вращения.

Просто хороший пример, если вы хотите представить движение Земли вокруг Солнца (Земля вращается вокруг Солнца, вращаясь вокруг собственной оси, находящейся на некотором радиусе):

    Matrix4X4 orbitRotation; //rotation matrix for where in orbit the object is
    Matrix4X4 objectRotation; //object rotation around its own axis
    Matrix4X4 orbitRadius; //object orbit radius

    Matrix4X4 result = (orbitRotation*orbitRadius)*objectRotation;
person Matic Oblak    schedule 23.04.2014
comment
Я думаю, что это не так, я пробовал все комбинации порядка умножения, проблема все еще существует, я думаю, что она связана с матрицей поворота камеры, которая не знает, где находятся верхний, передний и боковой векторы, поэтому преобразование из мирового пространства в пространство камеры не удается. - person BulBul; 23.04.2014
comment
Зачем ставить матрицу на каждый вход, нужно ее обновлять. Попробуйте сделать результат = результат * camTrans * modelTrans; а затем при установке вызова матрицы Projection * result. Также эти параметры tranForward и подобные должны быть удалены, они не должны увеличиваться, но фиксироваться в зависимости от ввода (например, длины пути перетаскивания мыши). - person Matic Oblak; 24.04.2014
comment
Облак, я сделал, как вы сказали, у него какое-то странное поведение, и объект исчезает сразу после того, как я удерживаю кнопку w (Forward), есть ли у вас хороший пример или онлайн-руководство, которое включает такой вид вращения для преобразования вида, я имею в виду матрицу поворота, которая использует вращение Эйлера без использования кватернионов, или, по крайней мере, вы можете провести меня через простой код, любая помощь будет очень признательна, заранее спасибо. - person BulBul; 24.04.2014
comment
Извините, но я всегда работаю с базовыми векторами и строю из них матрицу. В этом случае каждый объект имеет 3 вектора ВПЕРЕД, ВВЕРХ, ПОЛОЖЕНИЕ (а ВПРАВО - это произведение ВПЕРЕД и ВВЕРХ). Таким образом, вы всегда можете напрямую вызвать просмотр или, скорее, просто применить эти 4 векторные координаты к каждой строке (ВПРАВО, ВВЕРХ, ВПЕРЕД, ПОЛОЖЕНИЕ в порядке строк). Тогда все вращения и переводы очень просты. Движение вперед - POSITION + = FORWARD * inputScale, остальные такие же. Затем вращения просто вращают 2 базовых вектора вокруг 3-го (вращение ВПЕРЕД и ВПРАВО вокруг ВВЕРХ повернется вправо). - person Matic Oblak; 24.04.2014
comment
Я немного изменил код в соответствии с вашим предложением, и он сработал, вы можете проверить мой ответ / решение, которое я написал, и можете добавить его к своему, чтобы люди могли его увидеть, спасибо за вашу помощь. - person BulBul; 24.04.2014

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

      for (int x = 0; x< 256; x++)
        {
            if (state[x] == 1 )
            {
                if(x  == 26)
                    tranForward = -0.001;
                if (x == 22)
                    tranForward = 0.001;
                if (x == 4)
                    tranRight = 0.0009;
                if (x == 7)
                    tranRight = -0.0009;

                if (x == 82)
                    lookUp = 0.02;
                if (x == 81)
                    lookUp = -0.02;
                if (x == 80)
                    lookRight = -0.02;
                if (x == 79)
                    lookRight = 0.02;
            }
        }

camTrans   =   Rotation.setRotation(lookUp, lookRight, 0) * Translation.setTranslation(tranRight, 0, tranForward);

        result =   camTrans * result;

        modelTrans = Projection * result;



        tranForward = 0.0;
        tranRight   = 0.0;
        lookUp      = 0.0;
        lookRight   = 0.0;

       glUniformMatrix4fv(uniformloc, 1, GL_TRUE, modelTrans.getMat());

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

person BulBul    schedule 24.04.2014