OpenGL Matrix Camera 控件,局部旋转无法正常工作

Posted

技术标签:

【中文标题】OpenGL Matrix Camera 控件,局部旋转无法正常工作【英文标题】:OpenGL Matrix Camera controls, local rotation not functioning properly 【发布时间】:2013-02-06 11:28:33 【问题描述】:

所以我试图弄清楚如何手动创建一个相机类,为相机转换创建一个本地框架。我创建了一个基于 OpenGL SuperBible 的 GLFrame 类的播放器对象。

我将键盘键映射到 MoveUp、MoveRight 和 MoveForward 函数,并将水平和垂直鼠标移动映射到 xRot 变量和 rotateLocalY 函数。这样做是为了创建一个 FPS 风格的相机。

然而,问题在于 RotateLocalY。翻译工作正常,垂直鼠标移动也是如此,但水平移动以一种奇怪的方式缩小或放大我的所有对象。除了缩放之外,旋转似乎也将自身限制为 180 度,并围绕世界原点 (0.0) 而不是我的玩家的本地位置旋转。

我认为缩放与规范化向量有关,但 GLframe 类(我用作参考)从未规范化任何向量,并且该类工作得很好。规范化我的大部分向量只解决了缩放问题,所有其他问题仍然存在,所以我认为一段代码导致了所有这些问题?

我似乎无法弄清楚问题出在哪里,我将在此处发布所有适当的代码以及显示缩放的屏幕截图。

播放器对象

Player::Player()

    location[0] = 0.0f; location[1] = 0.0f; location[2] = 0.0f;
    up[0] = 0.0f; up[1] = 1.0f; up[2] = 0.0f;
    forward[0] = 0.0f; forward[1] = 0.0f; forward[2] = -1.0f;


// Does all the camera transformation. Should be called before scene rendering!
void Player::ApplyTransform()

    M3DMatrix44f cameraMatrix;
    this->getTransformationMatrix(cameraMatrix);

    glRotatef(xAngle, 1.0f, 0.0f, 0.0f);
    glMultMatrixf(cameraMatrix);


void Player::MoveForward(GLfloat delta)

    location[0] += forward[0] * delta;
    location[1] += forward[1] * delta;
    location[2] += forward[2] * delta;


void Player::MoveUp(GLfloat delta)

    location[0] += up[0] * delta;
    location[1] += up[1] * delta;
    location[2] += up[2] * delta;


void Player::MoveRight(GLfloat delta)

    // Get X axis vector first via cross product
    M3DVector3f xAxis;
    m3dCrossProduct(xAxis, up, forward);

    location[0] += xAxis[0] * delta;
    location[1] += xAxis[1] * delta;
    location[2] += xAxis[2] * delta;


void Player::RotateLocalY(GLfloat angle)

    // Calculate a rotation matrix first
    M3DMatrix44f rotationMatrix;
    // Rotate around the up vector
    m3dRotationMatrix44(rotationMatrix, angle, up[0], up[1], up[2]); // Use up vector to get correct rotations even with multiple rotations used.

    // Get new forward vector out of the rotation matrix
    M3DVector3f newForward;
    newForward[0] = rotationMatrix[0] * forward[0] + rotationMatrix[4] * forward[1] + rotationMatrix[8] * forward[2]; 
    newForward[1] = rotationMatrix[1] * forward[1] + rotationMatrix[5] * forward[1] + rotationMatrix[9] * forward[2];
    newForward[2] = rotationMatrix[2] * forward[2] + rotationMatrix[6] * forward[1] + rotationMatrix[10] * forward[2];

    m3dCopyVector3(forward, newForward);


void Player::getTransformationMatrix(M3DMatrix44f matrix)

    // Get Z axis (Z axis is reversed with camera transformations)
    M3DVector3f zAxis;
    zAxis[0] = -forward[0];
    zAxis[1] = -forward[1];
    zAxis[2] = -forward[2];

    // Get X axis
    M3DVector3f xAxis;
    m3dCrossProduct(xAxis, up, zAxis);

    // Fill in X column in transformation matrix
    m3dSetMatrixColumn44(matrix, xAxis, 0); // first column
    matrix[3] = 0.0f; // Set 4th value to 0

    // Fill in the Y column
    m3dSetMatrixColumn44(matrix, up, 1); // 2nd column
    matrix[7] = 0.0f;

    // Fill in the Z column
    m3dSetMatrixColumn44(matrix, zAxis, 2); // 3rd column
    matrix[11] = 0.0f;

    // Do the translation
    M3DVector3f negativeLocation; // Required for camera transform (right handed OpenGL system. Looking down negative Z axis)
    negativeLocation[0] = -location[0];
    negativeLocation[1] = -location[1];
    negativeLocation[2] = -location[2];
    m3dSetMatrixColumn44(matrix, negativeLocation, 3); // 4th column
    matrix[15] = 1.0f;

播放器对象标题

class Player

public:
    //////////////////////////////////////
    // Variables
    M3DVector3f location;
    M3DVector3f up;
    M3DVector3f forward;
    GLfloat xAngle; // Used for FPS divided X angle rotation (can't combine yaw and pitch since we'll also get a Roll which we don't want for FPS)
    /////////////////////////////////////
    // Functions
    Player();
    void ApplyTransform();
    void MoveForward(GLfloat delta);
    void MoveUp(GLfloat delta);
    void MoveRight(GLfloat delta);
    void RotateLocalY(GLfloat angle); // Only need rotation on local axis for FPS camera style. Then a translation on world X axis. (done in apply transform)

private:
    void getTransformationMatrix(M3DMatrix44f matrix);
;

应用转换

// Clear screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

// Apply camera transforms
player.ApplyTransform();


// Set up lights
...

// Use shaders
...

// Render the scene
RenderScene();

// Do post rendering operations
glutSwapBuffers();

和鼠标

float mouseSensitivity = 500.0f;

float horizontal = (width / 2) - mouseX;
float vertical = (height / 2) - mouseY;

horizontal /= mouseSensitivity;
vertical /= (mouseSensitivity / 25);

player.xAngle += -vertical;
player.RotateLocalY(horizontal);

glutWarpPointer((width / 2), (height / 2));

【问题讨论】:

您好,您是使用 glut/freeglut/sfml/sdl 等作为添加功能的方式,还是手动编写所有代码?另外,您确定要使用 RotateLocalY 吗?这将意味着,例如,当您向前行驶时,您将在向前行驶时向侧面看。更多信息会很方便,例如您的 RotateLocalY 函数的定义,然后希望这将是一个简单的问题,让您可以轻松地工作:) 您还需要确保您的矩阵乘法在正确的时间完成/地方,否则你也可能会出现奇怪的行为。 我正在使用 GLUT,但由于我尝试手动编写所有代码(例如,不使用 gluLookAt 函数),这不会是问题。我不确定我是否理解你在前进时侧身看? RotateLocalY 函数围绕向上矢量旋转(在我的例子中,向上矢量“当前”没有改变),因此它始终围绕正 Y 轴旋转,给人一种水平环视的印象。 你的矩阵数学的顺序是这样的:scale * point_translation * rotation * object_translation *projection matrix 甚至scale * rotation * translation* projection matrix?我的 goto 文章的简单提醒是this wikipedia link 在我的渲染函数中,我首先调用“ApplyTransform”函数进行转换,然后进行所有渲染。在 applyTransform 函数中,我同时执行所有步骤。旋转和平移转换为 4x4 矩阵,然后通过“glMultMatrix()”与 ModelView 矩阵相乘。所以可能没有任何顺序,因为它都是同时倍增的? 能否请您发布应用转换功能的主要部分? 【参考方案1】:

老实说,我认为您正在采取一种复杂的方法来解决您的问题。有很多方法可以创建相机。我最喜欢的是使用 R3-Vector 和 Quaternion,但您也可以使用 R3-Vector 和两个浮点数(俯仰和偏航)。

两个角度的设置很简单:

glLoadIdentity();
glTranslatef(-pos[0], -pos[1], -pos[2]);
glRotatef(-yaw, 0.0f, 0.0f, 1.0f);
glRotatef(-pitch, 0.0f, 1.0f, 0.0f);

现在最棘手的部分是移动相机。您必须按照以下方式做一些事情:

flaot ds = speed * dt;
position += tranform_y(pich, tranform_z(yaw, Vector3(ds, 0, 0)));

如何进行转换,我必须查一下,但您可以使用 rotation matrix 来查找它

旋转是微不足道的,只需在俯仰和偏航值中加减即可。

我喜欢使用四元数作为方向,因为它是通用的,因此您有一个独立于任何运动方案的相机(任何实体)。在这种情况下,您的相机看起来像这样:

class Camera

public:
   // lots of stuff omitted

   void setup();

   void move_local(Vector3f value);

   void rotate(float dy, float dz);

private:
    mx::Vector3f position;
    mx::Quaternionf orientation;
;

然后设置代码无耻地使用gluLookAt;你可以用它制作一个转换矩阵,但我从来没有让它正常工作。

void Camera::setup()

    // projection related stuff

    mx::Vector3f eye     = position;
    mx::Vector3f forward = mx::transform(orientation, mx::Vector3f(1, 0, 0));
    mx::Vector3f center  = eye + forward;
    mx::Vector3f up      = mx::transform(orientation, mx::Vector3f(0, 0, 1));
    gluLookAt(eye(0), eye(1), eye(2), center(0), center(1), center(2), up(0), up(1), up(2));

在局部帧中移动相机也很简单:

void Camera::move_local(Vector3f value) 

    position += mx::transform(orientation, value);

旋转也是直接向前的。

void Camera::rotate(float dy, float dz)

    mx::Quaternionf o = orientation;
    o = mx::axis_angle_to_quaternion(horizontal, mx::Vector3f(0, 0, 1)) * o;
    o = o * mx::axis_angle_to_quaternion(vertical, mx::Vector3f(0, 1, 0));   
    orientation = o;

(无耻塞):

如果你问我使用什么数学库,它是mathex。我写的...

【讨论】:

提出的要点:四元数特别好,因为它们避免了Gimbal lock:) 当然你也可以使用 R3x3 矩阵来定位。我喜欢将位置、方向和比例保持为单独的值。这可以防止错误进入其他值,例如缩放时的方向倾斜。 另一方面;如果您构建具有两个角度的“FPS”相机,您根本不会获得万向节锁定,因为您将俯仰角限制为 -180 - 180。 Rioki,感谢您的详细描述!真的很有用:)。我同意您的解决方案确实是相机系统的一个很好的解决方案,并且我知道我的操作的复杂性。我试图在转换矩阵中手动执行此操作的原因是我想了解这是如何完成的,复习我的数学和对 3D 转换的理解。但是,我目前被当前的实施困住了。我知道,对于最好和最容易使用的相机系统,我最终应该坚持使用四元数,但现在我仍然想了解它是如何在矩阵中完成的。 这是我在进行手动矩阵数学运算时使用的基本顺序:translaterotate (y)rotate (x) 然后scale 然后乘以包含查看格式的矩阵,即模型视图矩阵、投影矩阵等

以上是关于OpenGL Matrix Camera 控件,局部旋转无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL如何向上移动相机然后回到网格上

安卓自定义View进阶-Matrix Camera

安卓自定义View进阶-Matrix Camera

[Android]android.graphics.Camera实现图像的旋转缩放,配合Matrix实现图像的倾斜

相机矩阵(Camera Matrix)

50自定义View练习高仿小米时钟-使用Camera和Matrix实现3D效果