【问题标题】:LWJGL - Problems implementing 'roll' in a 6DOF Camera using quaternions and a translation matrixLWJGL - 使用四元数和平移矩阵在 6DOF 相机中实现“滚动”的问题
【发布时间】:2011-10-14 18:47:55
【问题描述】:

我在这个问题上花了几个星期,似乎找不到合适的解决方案,需要一些建议。

我正在使用 LWJGL/Java 创建一个 Camera 类,并且正在使用四元数来处理方位(偏航)、俯仰和滚动旋转。我希望这台相机能够处理 3D 空间中的所有 6 度运动并滚动。轴承、俯仰和滚动都是四元数。我将它们乘以“变化”四元数,并从中创建一个平移矩阵。我把它放在一个浮点缓冲区中,然后将模型视图矩阵乘以包含旋转矩阵的缓冲区。

我可以让轴承和俯仰旋转正常工作,但是当我实施滚动时,我遇到了问题。主要是,围绕 Z 轴旋转(滚动)似乎不起作用。当我“滚动”相机时,它似乎围绕全局 Z 轴而不是本地相机方向轴滚动。根据我乘以四元数的顺序,我通常可以让 3 个中的 2 个工作,但我不能让它们一起工作。

由于它们都是独立工作的,我假设我的定向方法有问题,我将它们组合并构建了一个旋转矩阵。我在粘贴整个类时遇到问题,所以这里是与轮换相关的方法和声明:

private final static float DEGTORAD = (float)(Math.PI/180);    

//Eye - position of the camera in the 3D world.
private Vector3f eye;

//Camera axis vectors, calculated each time reorient() is called.
//Initialized to global x, y, and z axis initially.
private Vector3f up;
private Vector3f right;
private Vector3f direction;

//Angles of rotation (in degrees)    
private float pitchAngle;
private float bearingAngle;
private float rollAngle;

private Quaternion pitch;
private Quaternion bearing;
private Quaternion roll;

private FloatBuffer viewMatrixBuffer = BufferUtils.createFloatBuffer(16);
private Quaternion currentOrientation;

...

/**
 * Change the bearing (yaw)
 * @param bearing delta in degrees
 */
public void bearing(float bearingDelta){
    bearingAngle += bearingDelta;
    if(bearingAngle > 360){
        bearingAngle -= 360;
    }else if(bearingAngle < 0){
        bearingAngle += 360;
    }
    bearing.setFromAxisAngle(new Vector4f(0f, 1f, 0f, bearingAngle * DEGTORAD));
    bearing.normalise();
}

/**
 * Change the pitch
 * @param pitch delta in degrees
 */
public void pitch(float pitchDelta){
    pitchAngle += pitchDelta;
    if(pitchAngle > 360){
        pitchAngle -= 360;
    }else if(pitchAngle < 0){
        pitchAngle += 360;
    }
    pitch.setFromAxisAngle(new Vector4f(1f, 0f, 0f, pitchAngle * DEGTORAD));
    pitch.normalise();
}

/**
 * @param initialRoll
 */
public void roll(float initialRoll) {
    rollAngle += initialRoll;
    if(rollAngle > 360){
        rollAngle -= 360;
    }else if(rollAngle < 0){
        rollAngle += 360;
    }
    roll.setFromAxisAngle(new Vector4f(0, 0, 1, rollAngle * DEGTORAD));
    roll.normalise();
}

/**
 * Change direction to focus on a certain point in the world
 * @param eye
 */
public void lookThrough(){
    reorient();
    GL11.glMultMatrix(viewMatrixBuffer);
}    

public void reorient(){
    //Multiply in order: bearing, pitch, roll.  Non-commutative!
    Quaternion change = new Quaternion();
    Quaternion.mul(bearing, pitch, change);
    Quaternion.mul(roll, change, change);
    // orient the camera...
    Matrix4f rotationMatrix = getRotationMatrix(change);

    //Get the looking direction
    direction.x = rotationMatrix.m20;
    direction.y = rotationMatrix.m21;
    direction.z = rotationMatrix.m22;

    //Set the position
    rotationMatrix.m30 = eye.x;
    rotationMatrix.m31 = eye.y;
    rotationMatrix.m32 = eye.z;
    rotationMatrix.m33 = 1;

    rotationMatrix.invert();
    rotationMatrix.store(viewMatrixBuffer);

    viewMatrixBuffer.rewind();

    Vector3f.cross(new Vector3f(0,1,0), direction, null).normalise(right);
    Vector3f.cross(right, direction, null).normalise(up);               
}

Vector3f、Quaternion 和 Matrix4f 都是 LWJGL 类,不是定制的。

所以我的问题是,给定代表轴承、俯仰和滚动的 3 个四元数,我如何修改 ModelView 矩阵以准确表示这些旋转?

编辑:我觉得这非常接近。请参阅 RiverC 评论中的要点链接。在旋转了这么多度之后,视图在滚动时恢复正常之前跳了很多。它的要点在那里,但它仍然有点偏离。

【问题讨论】:

    标签: java matrix-multiplication lwjgl quaternions rotational-matrices


    【解决方案1】:

    你的乘法顺序不对。

    对于两个旋转 q1 和 q2,如果 q2 跟随 q1(因为旋转通常是非交流性的),则乘以 q2*q1。

    在诸如 FPS 控件之类的万向节样式系统中,优先顺序始终是偏航、俯仰、滚动。这将建议以下数学:

    roll * pitch * yaw
    

    作为一个 java 点,我建议不要为每次更新都创建新的四元数,但无论如何

    change = Quaternion.mul(Quaternion.mul(roll, pitch, change), yaw, change);
    

    如果您查看代码,第三个四元数只会被结果覆盖,因此无需在每帧/更新时重置或重新创建它。

    这种旋转顺序的事情令人困惑,但如果您查看四元数的 Wiki 页面,这是一般规则。

    【讨论】:

    • 感谢您的更新。有一段时间没有接触过这段代码了,所以我正在重新开始使用 LWJGl。我想我很接近。在进行 gsimard 建议的更改和更改之后,我越来越接近了。但是,当偏航(问题中的方位)为 0 时,俯仰很好。当 yaw 为 180 时,pitch 反转(pitch up 将相机向下移动)。此外,旋转约 20 度后,旋转变得零星。如果我继续旋转它会变平。所以有些事情还是不太对劲。我会把代码放在github上,今晚晚些时候更新这个问题。我想它几乎就在那里。再次感谢。
    • 为什么要在创建旋转矩阵后对其进行处理?您应该能够将 Mat4 直接发送到 Shader 并使用它来乘以场景的顶点,而无需修改它。我的意思是,代码的“//设置方向”部分。似乎您正试图通过乘法来使用它来抵消相机,但根据我的经验,这已经被击中了。尝试删除它。
    • 认为我最初这样做的原因是,我可以将方向矢量与眼睛矢量相加,以生成与世界中的物体交互的矢量。或多或少的“挑选”向量。另外,我解决了音高翻转的问题。我交换了方位和俯仰的顺序,所以该行显示为Quaternion.mul(Quaternion.mul(roll, bearing, change), pitch, change); 仍然试图弄清楚为什么滚动一定量后滚动会变得不稳定。
    • 在进行一些重构并做出您和 gsimard 建议的更改之后,这是关于 gist 的完整课程:gist.github.com/bsjohnson/26cbd6d01583c4cbc871
    • 另外,m30 - m33 的设置是在玩家移动角色(向前、向后、扫射等)时平移摄像机的位置,如果有更好的方法将此应用于旋转矩阵我非常愿意更改它。
    【解决方案2】:

    我知道这是旧的,但无论如何请允许我猜测。问题正是你自己说的:

    当我“滚动”相机时,它似乎围绕全局 Z 轴而不是本地相机方向轴滚动。

    它这样做是因为您要求它围绕向量 (0,0,1) 滚动,即全局 Z 轴。

    这就是单位四元数的作用:它们通过 w 标量 ( w = cos(角度/2))。

    如果我理解你想要做什么,那就是滚动你的相机,就像从左向右倾斜你的头一样,那么你应该围绕你的方向向量构建一个滚动四元数:

     roll.setFromAxisAngle(new Vector4f(direction.x, direction.y, direction.z, rollAngle * DEGTORAD));
    

    我假设您的方向向量已标准化,或者 LWJGL 在调用 setFromAxisAngle 时知道如何处理非单一轴向量。

    【讨论】:

    • 感谢您的更新。我做了这个改变,它很接近,但是我仍然有一些奇怪的行为。我用 RiverC 建议的那个做了这个改变,并在那里的评论中解释了新的问题。我今晚回家后会更新更多细节。再次感谢,感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-06
    • 2012-10-11
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 2013-12-26
    相关资源
    最近更新 更多