如何在 c++ 中使用 sdl、opengl 移动相机时修复奇怪的相机旋转

发布于 2024-07-30 06:11:43 字数 4676 浏览 3 评论 0原文

我有一个从网上阅读而来的相机对象,它可以处理向前和向后移动、向左和向右扫射,甚至用鼠标环顾四周。 但是,当我向任何方向移动并尝试环顾四周时,它会到处跳,但是当我不移动并环顾四周时,它就很好。

我希望有人能帮我弄清楚为什么我可以同时移动和环顾四周?

main.hcamera.cpp

#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();

和mousemotion

#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;
    }
}
}

I have a camera object that I have put together from reading on the net that handles moving forward and backward, strafe left and right and even look around with the mouse. But when I move in any direction plus try to look around it jumps all over the place, but when I don't move and look around its fine.

I'm hoping someone can help me work out why I can move and look around at the same time?

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);

}

and the mousemotion code

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;
    }
}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

歌入人心 2024-08-06 06:11:43

我不完全确定你在上面做什么。

就我个人而言,我只允许使用简单的 4x4 矩阵。 任何实施都可以。 要旋转,简单地说,需要使用鼠标 x 和 y 的变化作为欧拉输入来围绕 y 和 x 轴旋转。 互联网上有很多可用的代码可以为您完成此操作。

其中一些矩阵库不会为您提供“MoveForward()”函数。 如果是这样的话,那没关系,继续前进就很容易了。 第三列(如果您使用行主矩阵,则为行)是您的前向向量。 提取它。 对其进行标准化(无论如何它确实应该标准化,因此可能不需要这一步)。 将其乘以您希望向前移动的距离,然后将其添加到该位置(第四列/行)。

现在这是奇怪的部分。 视图矩阵是一种特殊类型的矩阵。 上面的矩阵定义了视图空间。 如果您将当前模型矩阵乘以该矩阵,您将不会得到您期望的答案。 因为你希望对其进行变换,使相机位于原点。 因此,您需要有效地撤消相机转换,以将事物重新定向到上面定义的视图。 为此,您需要将模型矩阵乘以视图矩阵的

您现在已经在正确的视图空间中定义了一个对象。

这是我非常简单的相机课程。 它不处理您描述的功能,但希望能给您一些关于如何设置类的想法(请注意,我使用行专业,即 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;
}

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

I'm not entirely sure what you are doing above.

Personally I would just allow a simple 4x4 matrix. Any implementation will do. To rotate you, simply, need to rotate using the change of mouse x and y as euler inputs for rotation around the y and x axes. There is lots of code available all over the internet that will do this for you.

Some of those matrix libraries won't provide you with a "MoveForward()" function. If this is the case its ok, moving forward is pretty easy. The third column (or row if you are using row major matrices) is your forward vector. Extract it. Normalise it (It really should be normalised anyway so this step may not be needed). Multiply it by how much you wish to move forward and then add it to the position (the 4th column/row).

Now here is the odd part. A view matrix is a special type of matrix. The matrix above defines the view space. If you multiply your current model matrix by this matrix you will not get the answer you expect. Because you wish to transform it such that the camera is at the origin. As such you need to, effectively, undo the camera transformation to re-orient things to the view defined above. To do this you multiply your model matrix by the inverse of the view matrix.

You now have an object defined in the correct view space.

This is my very simple camera class. It does not handle the functionality you describe but hopefully will give you a few ideas on how to set up the class (Be warned, I use row major, ie DirectX style, matrices).

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;
}

/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
烧了回忆取暖 2024-08-06 06:11:43

我同意戈兹的观点。 如果你想表示仿射变换,例如旋转+平移,你需要使用 同质 4x4 矩阵

假设行主要表示 那么如果没有缩放或剪切,您的 4x4 矩阵表示以下内容:
第 0 行到第 2 行:本地坐标系的三个基本向量(即 x、y、z)
第 3 行:从原点的当前平移

因此要沿着本地 x 向量移动,正如 Goz 所说,因为你可以假设它是一个单位向量
如果没有缩放/剪切,您只需将其乘以移动步长( +ve 或 -ve ),然后将所得向量添加到矩阵中的第 4 行
因此,举一个简单的例子,从原点开始,将本地框架设置为世界框架,那么您的矩阵将看起来像这样

1 0 0 0 <--- x 单位向量
0 1 0 0 <--- y 单位向量
0 0 1 0 <--- z 单位向量
0 0 0 1 <--- 平移向量

就大多数游戏摄像机的工作方式而言,轴映射如下:
x轴<=> 相机向左/向右平移
y轴<=> 相机向上/向下平移
z轴<=> 相机放大/缩小

因此,如果我旋转整个参考系来观察一个新点 LookAt,那么当 Goz 放入他的 BaseCamera 重载构造函数代码时,您将构造一个新的本地坐标系并将其设置到您的矩阵中( mCameraMat.Set( vLat, vUp, vDir, vPos ) 通常所做的所有操作都是设置矩阵的这四行,即 VLat 为第 0 行、vUp 第 1 行、vDir 第 2 行和 vPos 第 3 行)

然后放大/缩小将变成 row 3 = row 2 * stepval

再次正如 Goz 正确指出的那样,然后您需要将其转换回世界空间,这是通过乘以视图矩阵的逆来完成的

I agree with Goz. You need to use homegenous 4x4 matrices if you want to represent affine transformations such as rotate + translate

Assuming row major representation then if there is no scaling or shearing, your 4x4 matrix represents the following:
Rows 0 to 2 : The three basis vectors of your local co-ordinate system ( i.e x,y,z )
Row 3 : the current translation from the origin

So to move along your local x vector, as Goz says, because you can assume it's a unit vector
if there is no scale/shear you just multiply it by the move step ( +ve or -ve ) then add the resultant vector onto Row 4 in the matrix
So taking a simple example of starting at the origin with your local frame set to world frame then your matrix would look something like this

1 0 0 0 <--- x unit vector
0 1 0 0 <--- y unit vector
0 0 1 0 <--- z unit vector
0 0 0 1 <--- translation vector

In terms of a way most game cameras work then the axes map like this:
x axis <=> Camera Pan Left/Right
y axis <=> Camera Pan Up/Down
z axis <=> Camera Zoom In/Out

So if I rotate my entire frame of reference to say look at a new point LookAt then as Goz puts in his BaseCamera overloaded constructor code, you then construct a new local co-ordinate system and set this into your matrix ( all mCameraMat.Set( vLat, vUp, vDir, vPos ) does typically is set those four rows of the matrix i.e VLat would be row 0, vUp row 1, vDir row 2 and vPos row 3 )

Then to zoom in/out would just become row 3 = row 2 * stepval

Again as Goz, rightly points out, you then need to transform this back into world-space and this is done by multiplying by the inverse of the view matrix

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文