Как исправить странное вращение камеры при перемещении камеры с SDL, OpenGL в C ++ - PullRequest
1 голос
/ 11 августа 2009

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

Я надеюсь, что кто-нибудь может помочь мне понять, почему я могу двигаться и одновременно осматриваться?

main.h

#include "SDL/SDL.h"
#include "SDL/SDL_opengl.h"

#include <cmath>


#define CAMERASPEED 0.03f               // The Camera Speed



struct tVector3 // Extended 3D Vector Struct

{           

tVector3() {} // Struct Constructor

tVector3 (float new_x, float new_y, float new_z) // Init Constructor     

{ x = new_x; y = new_y; z = new_z; }

// overload + operator

tVector3 operator+(tVector3 vVector) {return tVector3(vVector.x+x, vVector.y+y, vVector.z+z);}

// overload - operator

tVector3 operator-(tVector3 vVector) {return tVector3(x-vVector.x, y-vVector.y, z-vVector.z);}

// overload * operator

tVector3 operator*(float number)     {return tVector3(x*number, y*number, z*number);}

// overload / operator

tVector3 operator/(float number)     {return tVector3(x/number, y/number, z/number);}



float x, y, z;                      // 3D vector coordinates

};



class CCamera 

{

public:



    tVector3 mPos;  

    tVector3 mView;     

    tVector3 mUp;           



    void Strafe_Camera(float speed);



    void Move_Camera(float speed);

    void Rotate_View(float speed);
    void Position_Camera(float pos_x, float pos_y,float pos_z,

                         float view_x, float view_y, float view_z,

                         float up_x,   float up_y,   float up_z);

};



void Draw_Grid();

camera.cpp

#include "main.h"

void CCamera::Position_Camera(float pos_x, float pos_y, float pos_z,
                float view_x, float view_y, float view_z, 
                float up_x, float up_y, float up_z)
{
mPos = tVector3(pos_x, pos_y, pos_z);
mView = tVector3(view_x, view_y, view_z);
mUp = tVector3(up_x, up_y, up_z);
}

void CCamera::Move_Camera(float speed)
{
tVector3 vVector = mView - mPos;

mPos.x  = mPos.x  + vVector.x * speed;

mPos.z  = mPos.z  + vVector.z * speed;

mView.x = mView.x + vVector.x * speed;

mView.z = mView.z + vVector.z * speed;
}

void CCamera::Strafe_Camera(float speed)
{
tVector3 vVector = mView - mPos;

tVector3 vOrthoVector;



vOrthoVector.x = -vVector.z;

vOrthoVector.z =  vVector.x;



mPos.x  = mPos.x  + vOrthoVector.x * speed;

mPos.z  = mPos.z  + vOrthoVector.z * speed;

mView.x = mView.x + vOrthoVector.x * speed;

mView.z = mView.z + vOrthoVector.z * speed;
}

void CCamera::Rotate_View(float speed)
{
tVector3 vVector = mView - mPos;
tVector3 vOrthoVector;

vOrthoVector.x = -vVector.z;

vOrthoVector.z =  vVector.x;


mView.z = (float)(mPos.z + sin(speed)*vVector.x + cos(speed)*vVector.z);

mView.x = (float)(mPos.x + cos(speed)*vVector.x - sin(speed)*vVector.z);

}

и код движения мыши

void processEvents()
{
int mid_x = screen_width  >> 1;

int mid_y = screen_height >> 1;
int mpx = event.motion.x;
int mpy = event.motion.y;

float angle_y  = 0.0f;

float angle_z  = 0.0f;

while(SDL_PollEvent(&event))
{
    switch(event.type)
    {
        case SDL_MOUSEMOTION:
            if( (mpx == mid_x) && (mpy == mid_y) ) return;



            // Get the direction from the mouse cursor, set a resonable maneuvering speed

            angle_y = (float)( (mid_x - mpx) ) / 1000; //1000

            angle_z = (float)( (mid_y - mpy) ) / 1000; //1000


            // The higher the value is the faster the camera looks around.

            objCamera.mView.y += angle_z * 2;



            // limit the rotation around the x-axis

            if((objCamera.mView.y - objCamera.mPos.y) > 8)  objCamera.mView.y = objCamera.mPos.y + 8;

            if((objCamera.mView.y - objCamera.mPos.y) <-8)  objCamera.mView.y = objCamera.mPos.y - 8;


            objCamera.Rotate_View(-angle_y);
            SDL_WarpMouse(mid_x, mid_y);
            break;

        case SDL_KEYUP:
            objKeyb.handleKeyboardEvent(event,true);
            break;
        case SDL_KEYDOWN:
            objKeyb.handleKeyboardEvent(event,false);
            break;

        case SDL_QUIT:
            quit = true;
            break;

        case SDL_VIDEORESIZE:
            screen = SDL_SetVideoMode( event.resize.w, event.resize.h, screen_bpp, SDL_OPENGL | SDL_HWSURFACE | SDL_RESIZABLE | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE );
            screen_width = event.resize.w;
            screen_height = event.resize.h;
            init_opengl();
            std::cout << "Resized to width: " << event.resize.w << " height: " << event.resize.h << std::endl;
            break;

        default:
            break;
    }
}
}

Ответы [ 2 ]

3 голосов
/ 11 августа 2009

Я не совсем уверен, что вы делаете выше.

Лично я бы просто допустил простую матрицу 4х4. Подойдет любая реализация. Чтобы повернуть вас, просто нужно повернуть, используя смену мыши x и y в качестве входов Эйлера для вращения вокруг осей y и x. В интернете есть много кода, который сделает это за вас.

Некоторые из этих библиотек матриц не предоставляют вам функцию MoveForward (). Если это так, то двигаться вперед довольно легко. Третий столбец (или строка, если вы используете основные матрицы строк) - это ваш прямой вектор. Извлеките это. Нормализовать его (в любом случае его действительно следует нормализовать, поэтому этот шаг может не понадобиться). Умножьте его на то, насколько вы хотите двигаться вперед, а затем добавьте его к позиции (4-й столбец / строка).

Теперь вот странная часть. Матрица представления - это особый тип матрицы. Приведенная выше матрица определяет пространство просмотра. Если вы умножите текущую матрицу модели на эту матрицу, вы не получите ожидаемого ответа. Потому что вы хотите преобразовать его так, чтобы камера находилась в начале координат. Таким образом, вам необходимо эффективно отменить преобразование камеры, чтобы переориентировать объекты на вид, определенный выше. Для этого вы умножаете матрицу вашей модели на обратную матрицы вида.

Теперь у вас есть объект, определенный в правильном пространстве просмотра.

Это мой очень простой класс камеры. Он не поддерживает описанную вами функциональность, но, надеюсь, даст вам несколько идей о том, как настроить класс (будьте осторожны, я использую мажорные строки, то есть стиль DirectX, матрицы).

BaseCamera.h:

#ifndef BASE_CAMERA_H_
#define BASE_CAMERA_H_

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

#include "Maths/Vector4.h"
#include "Maths/Matrix4x4.h"

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

class BaseCamera
{
protected:
    bool                    mDirty;
    MathsLib::Matrix4x4     mCameraMat;
    MathsLib::Matrix4x4     mViewMat;
public:
    BaseCamera();
    BaseCamera( const BaseCamera& camera );
    BaseCamera( const MathsLib::Vector4& vPos, const MathsLib::Vector4& vLookAt );
    BaseCamera( const MathsLib::Matrix4x4& matCamera );

    bool IsDirty() const;
    void SetDirty();

    MathsLib::Matrix4x4&        GetOrientationMatrix();
    const MathsLib::Matrix4x4&  GetOrientationMatrix() const;

    MathsLib::Matrix4x4&        GetViewMatrix();
};

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

inline MathsLib::Matrix4x4& BaseCamera::GetOrientationMatrix()
{
    return mCameraMat;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

inline const MathsLib::Matrix4x4& BaseCamera::GetOrientationMatrix() const
{
    return mCameraMat;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

inline bool BaseCamera::IsDirty() const
{
    return mDirty;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

inline void BaseCamera::SetDirty()
{
    mDirty = true;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

#endif

BaseCamera.cpp:

#include "Render/stdafx.h"

#include "BaseCamera.h"

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

BaseCamera::BaseCamera() :
    mDirty( true )
{
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

BaseCamera::BaseCamera( const BaseCamera& camera ) :
    mDirty( camera.mDirty ),
    mCameraMat( camera.mCameraMat ),
    mViewMat( camera.mViewMat )
{
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

BaseCamera::BaseCamera( const MathsLib::Vector4& vPos, const MathsLib::Vector4& vLookAt ) :
    mDirty( true )
{
    MathsLib::Vector4 vDir  = (vLookAt - vPos).Normalise();
    MathsLib::Vector4 vLat  = MathsLib::CrossProduct( MathsLib::Vector4( 0.0f, 1.0f, 0.0f ), vDir ).Normalise();
    MathsLib::Vector4 vUp   = MathsLib::CrossProduct( vDir, vLat );//.Normalise();

    mCameraMat.Set( vLat, vUp, vDir, vPos );    
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

BaseCamera::BaseCamera( const MathsLib::Matrix4x4& matCamera ) :
    mDirty( true ),
    mCameraMat( matCamera )
{
}

    /*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/

MathsLib::Matrix4x4& BaseCamera::GetViewMatrix()
{
    if ( IsDirty() )
    {
        mViewMat    = mCameraMat.Inverse();
        mDirty      = false;
    }
    return mViewMat;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
1 голос
/ 11 августа 2009

Я согласен с Гозом. Вам нужно использовать гомогенные матрицы 4x4 , если вы хотите представлять аффинные преобразования, такие как rotate + translate

Предполагая мажорное представление строки , тогда, если нет масштабирования или сдвига, ваша матрица 4x4 представляет собой следующее:
Строки от 0 до 2: три базисных вектора вашей локальной системы координат (т. Е. X, y, z)
Строка 3: текущий перевод из источника

Итак, чтобы двигаться по вашему локальному вектору x, как говорит Гоз, вы можете предположить, что это единичный вектор если нет масштаба / сдвига, просто умножьте его на шаг перемещения (+ ve или -ve), затем добавьте результирующий вектор в строку 4 в матрице Итак, если взять простой пример, начиная с источника с вашим локальным фреймом, установленным на мировой фрейм, то ваша матрица будет выглядеть примерно так

1 0 0 0 <--- x единичный вектор <br> 0 1 0 0 <--- y единичный вектор <br> 0 0 1 0 <--- z единичный вектор <br> 0 0 0 1 <--- вектор перевода </p>

По принципу работы большинства игровых камер карты осей выглядят следующим образом:
ось x <=> Камера панорамирования влево / вправо
ось y <=> Камера панорамирование вверх / вниз
Ось z <=> Увеличение / уменьшение камеры

Так что, если я поверну всю свою систему отсчета, чтобы сказать: посмотрите на новую точку LookAt, тогда, когда Гоз вставит код перегруженного конструктора BaseCamera, вы создадите новую локальную систему координат и установите ее в своей матрице (все 1023 * обычно устанавливает эти четыре строки матрицы, т.е. VLat будет строкой 0, строкой vUp 1, строкой vDir 2 и строкой vPos 3)

Тогда для увеличения / уменьшения просто станет строка 3 = строка 2 * stepval

Опять же, как правильно заметил Гоз, вам нужно преобразовать это обратно в мировое пространство, и это делается путем умножения на обратную матрицу вида

...