相机在可变重力下旋转和定向

时间:2014-04-16 16:48:01

标签: c++ camera orientation physics

我正在尝试为第一人称实现相机控制器,基于鼠标外观的OpenGL相机。当摄像机始终正常定向时(摄像机向上矢量=世界Y轴),这是一个简单的问题。但是,我在使用可以无缝使用任何方向的相机正常工作时遇到了麻烦。目的是让玩家在整个星球上移动。另外一个要求是方向相对于方向保持相同,因为相机的方向改变。一个例子是,如果你在一个行星上行走,方向相对于地面保持不变,所以当你从一个杆子“沿着”向下“向下”时,方向也会自动旋转。

到目前为止,我已经尝试了许多不同的方法来实现这一点,但正如我所看到的,应该有两种不同的方法来实现这一点。第一种是基于来自世界轴的偏航角和俯仰角进行常规相机旋转,然后通过相机方向变换所得到的外观方向以获得最终外观方向。第二种方法是基于计算的向上和向右矢量以偏航和俯仰角旋转摄像机。向上矢量很容易;这只是方向。我没有得到任何正确的矢量我发现它可以正常工作。

好的,这是这两种方法的代码。

常用代码

// m_orientation calculated from planet center to current position
m_horizontal += horizontal;
m_vertical += vertical;

while (m_horizontal > TWO_PI) {
    m_horizontal -= TWO_PI;
}

while (m_horizontal < -TWO_PI) {
    m_horizontal += TWO_PI;
}

if (m_vertical > MAX_VERTICAL) {
    m_vertical = MAX_VERTICAL;
}
else if (m_vertical < -MAX_VERTICAL) {
    m_vertical = -MAX_VERTICAL;
}

// code from either implementation

m_view = glm::lookAt(m_position, m_position + m_direction, m_orientation);

第一种方法有偏航,绕世界轴倾斜然后变换

// check for m_orientation != WORLD_UP...
glm::vec3 axis = glm::normalize(glm::cross(WORLD_UP, m_orientation));
float angle_degrees = acosf(m_orientation.y) * RADS_TO_DEGREES;
glm::mat4 trans = glm::rotate(glm::mat4(), angle_degrees, axis);

// can also be determined with two rotation matrices about world axes, end result is identical
m_direction = glm::vec3(cosf(m_vertical) * sinf(m_horizontal),
                        sinf(m_vertical),
                        cosf(m_vertical) * cosf(m_horizontal));
m_direction = glm::vec3(trans * glm::vec4(m_direction));

第二种方法关于适当的向上和向右矢量的偏航和投球

m_right = ??? // tried literally everything
glm::mat4 yaw = glm::rotate(glm::mat4(), m_horizontal, m_orientation);
glm::mat4 pitch = glm::rotate(glm::mat4(), m_vertical, m_right);
glm::mat4 trans = yaw * pitch;
m_direction = glm::vec3(trans[2]); // z axis
好的,所以这就是问题所在。第一种方法几乎完美地工作,但是在行星的南极附近(在方向的〜15度范围内=(0,-1,0),效果会越来越强),相机会自动向南极旋转方向改变。因此,如果相机方向没有变化,靠近南极,相机工作正常。方向的任何变化都会导致相机向南极旋转。方向变化越大,相机旋转的越多。现在我尝试从世界轴相机旋转中移除俯仰或偏航,并且仅在包括俯仰计算时出现此效果。只有偏航,然后相机表现完美(缺少任何音高控制)。据我所知,我从常规上升=(0,1,0)到当前方向的转换是不正确的。对此有何帮助?

现在另一种做事情的方式似乎有点正确,但我还没有找到一个好的向量。我尝试过的所有东西都会导致水平和垂直运动的奇怪行为。最明显的解决方案是,前一帧的方向和当前方向的交叉乘积产生正确的向量不起作用。有关正确矢量的任何建议吗?

我也很高兴看到这个问题完全不同的解决方案。我知道这是可能的,但没有多少搜索给了我一个很好的解决方案。非常感谢提前。

编辑1:为回应PawełStawarz而尝试了更多的东西

导致相机方向错误和鼠标移动怪异。我确保我的矩阵乘法顺序正确。我也试过了转置。

m_view = glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), m_direction, m_up);
m_view = trans * m_view; //trans is rotation from orientation=(0,1,0) to orientation=m_orientation

导致与之前相同的问题,相机自动向南极旋转。垂直鼠标旋转也不正确,导致相机进入圆圈。

m_view = glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), m_direction, m_up);
m_view = trans * m_view;
m_direction = glm::vec3(m_view[2]);

m_view = glm::lookAt(m_position, m_direction + m_position, m_orientation);

编辑2:使用RIGHT向量方法,方向之间没有转换,效果更好一些。但是,它会导致相机偏航剧烈振荡,并且音高接近垂直(距垂直方向至少5度)。另外,音高的运动范围不是通过方向调整的,所以例如在行星的一侧,垂直运动被限制在你前面直接向你(〜(0,1,0)到〜 (0,-1,0))。

glm::mat4 yaw = glm::rotate(glm::mat4(), m_horizontal * ONEEIGHTY_PI, m_orientation);
glm::mat4 pitch = glm::rotate(glm::mat4(), m_vertical * -ONEEIGHTY_PI, m_right);

glm::mat4 cam = pitch * yaw;

m_right = glm::vec3(cam[0]);
m_up = glm::vec3(cam[1]);
m_direction = glm::vec3(cam[2]);

m_view = glm::lookAt(m_position, m_direction + m_position, m_up);
m_vp = m_perspective * m_view;

2 个答案:

答案 0 :(得分:0)

如果我们保持简单,使用标准方法:

  1. 将相机移动到当前播放器位置
  2. 将其旋转到玩家正在看的位置
  3. 相机旋转描述如下:

    • UP向量,它是从(p0x,p0y,p0z)开始的归一化向量(其中p0是行星中心的位置)并且经过(p1x,p1y,p1z)(其中p1描述了玩家目前所处的位置,
    • 右矢量是垂直于向量的向量垂直于玩家正在看的方向 - LOOK向量(在他正向前看的情况下 - 垂直于他的方向)。

    由于UP向量可以直接从玩家当前位置进行计算,因此您必须获得LOOK和RIGHT向量。两者都是corresponding other vectors的交叉产品。

    另请注意,允许玩家向上/向下看并平移他的头,可以(并且可能会)实际上改变UP向量。

答案 1 :(得分:0)

解决了它。需要一个不同的转变。请参阅here以获得相当不错的解释。

glm::mat4 trans;
float factor = 1.0f;
float real_vertical = vertical;
m_horizontal += horizontal;
m_vertical += vertical;

while (m_horizontal > TWO_PI) {
    m_horizontal -= TWO_PI;
}

while (m_horizontal < -TWO_PI) {
    m_horizontal += TWO_PI;
}

if (m_vertical > MAX_VERTICAL) {
    m_vertical = MAX_VERTICAL;
}
else if (m_vertical < -MAX_VERTICAL) {
    m_vertical = -MAX_VERTICAL;
}

glm::quat world_axes_rotation = glm::angleAxis(m_horizontal * ONEEIGHTY_PI, glm::vec3(0.0f, 1.0f, 0.0f));
world_axes_rotation = glm::normalize(world_axes_rotation);
world_axes_rotation = glm::rotate(world_axes_rotation, m_vertical * ONEEIGHTY_PI, glm::vec3(1.0f, 0.0f, 0.0f));

m_pole = glm::normalize(m_pole - glm::dot(m_orientation, m_pole) * m_orientation);

glm::mat4 local_transform;

local_transform[0] = glm::vec4(m_pole.x, m_pole.y, m_pole.z, 0.0f);
local_transform[1] = glm::vec4(m_orientation.x, m_orientation.y, m_orientation.z, 0.0f);
glm::vec3 tmp = glm::cross(m_pole, m_orientation);
local_transform[2] = glm::vec4(tmp.x, tmp.y, tmp.z, 0.0f);
local_transform[3] = glm::vec4(m_position.x, m_position.y, m_position.z, 1.0f);

world_axes_rotation = glm::normalize(world_axes_rotation);
m_view = local_transform * glm::mat4_cast(world_axes_rotation);
m_direction = -1.0f * glm::vec3(m_view[2]);
m_up = glm::vec3(m_view[1]);
m_right = glm::vec3(m_view[0]);

m_view = glm::inverse(m_view);