从2D操纵杆获取3D旋转轴和角度

时间:2012-12-20 02:19:15

标签: java android opengl-es libgdx

我在左下角的屏幕上有一个2D虚拟操纵杆。我有一个在原点(0,0,0)以3D绘制的球体......我的相机可以围绕球体运行。我正在尝试使用操纵杆移动相机,但不知道我该怎么做。我需要从操纵杆创建一个轴角度旋转并更新摄像机角度,使用四元数来表示它的方向。这是我现在拥有的:

我的相机旋转存储为四元数:

// The current rotation
public Quaternion rotation = new Quaternion();

// The temp quaternion for the new rotation
private Quaternion newRotation = new Quaternion();

通过以下方法更新旋转和相机:

// Update the camera using the current rotation
public void update(boolean updateFrustum)
{
    float aspect = camera.viewportWidth / camera.viewportHeight;
    camera.projection.setToProjection(Math.abs(camera.near), Math.abs(camera.far), camera.fieldOfView, aspect);
    camera.view.setToLookAt(camera.position, tmp.set(camera.position).add(camera.direction), camera.up);

    // Rotate the current view matrix using our rotation quaternion
    camera.view.rotate(rotation);

    camera.combined.set(camera.projection);
    Matrix4.mul(camera.combined.val, camera.view.val);

    if (updateFrustum)
    {
        camera.invProjectionView.set(camera.combined);
        Matrix4.inv(camera.invProjectionView.val);
        camera.frustum.update(camera.invProjectionView);
    }
}

public void updateRotation(float axisX, float axisY, float axisZ, float speed)
{
    // Update rotation quaternion
    newRotation.setFromAxis(Vector3.tmp.set(axisX, axisY, axisZ), ROTATION_SPEED * speed * MathHelper.PIOVER180);
    rotation.mul(newRotation);

    // Update the camera
    update(true);
}

我目前正在调用updateRotation()

// Move the camera
if (joystick.isTouched)
{
    // Here is where I'm having trouble...
    // Get the current axis of the joystick to rotate around
    tmpAxis = joystick.getAxis();
    axisX = tmpAxis.X;
    axisY = tmpAxis.Y;

    // Update the camera with the new axis-angle of rotation

    // joystick.getSpeed() is just calculating the distance from 
    // the start point to current position of the joystick so that the 
    // rotation will be slower when closer to where it started and faster 
    // as it moves toward its max bounds

    controller.updateRotation(axisX, axisY, axisZ, joystick.getSpeed());
}

来自getAxis()类的当前Joystick方法:

public Vector2d getAxis()
{
    Vector2d axis = new Vector2d(0.0f, 0.0f);
    float xOffset = 0;
    float yOffset = 0;
    float angle = getAngle();

    // Determine x offset
    xOffset = 1f;

    // Determine y offset
    yOffset = 1f;

    // Determine positive or negative x offset
    if (angle > 270 || angle < 90)
    {
        // Upper left quadrant
        axis.X = xOffset;
    }
    else
    {
        axis.X = -xOffset;
    }

    // Determine positive or negative y offset
    if (angle > 180 && angle < 360)
    {
        // Upper left quadrant
        axis.Y = yOffset;
    }
    else
    {
        axis.Y = -yOffset;
    }

    return axis;
}

3 个答案:

答案 0 :(得分:2)

我只使用3个向量来定义摄像机:位置,向前,向上(右=交叉(向前,向上))。那么对于你的情况,你总是期待(0,0,0)所以只需在输入上更新这3个向量,如下所示:

上/下:

right = cross(forward, up);
posititon = normalized(position + up*(input*zoomFactor)) * length(position); //zoomFactor might be useful if you are looking from close to reduce the move but is optional
forward = normalized((0,0,0)-position);
up = cross(right, forward);//should already be normalized Note: might be cross(forward, right)

左/右:

right = cross(forward, up);
position = normalized(position + right*(input*zoomFactor)) * length(position);
forward = normalized((0,0,0)-position);
right = cross(forward, up); //you do need to update it
up = cross(right, forward); //Note: might be cross(forward, right)

向左/向右倾斜:

up = normalize(up + right*input); //no zoom factor needed here

放大/缩小:

position = position + forward*input;//in your case you might just want to set it to some percentage: position = position + forward*(length(position) * (input));

这种方法应该只适用于小输入值,通常就是这种情况。相对于输入的角度变化将是alpha = atan(输入),因此如果输入是无穷大,则角度将改变90度。但是您可以轻松保存/加载状态,您可以手动设置摄像机位置和伴随矢量。所以你拥有调用“lookAt”所需的一切。

答案 1 :(得分:2)

您可能希望根据其当前旋转状态更新旋转,而不是仅仅旋转固定的全局巴黎轴(向上和向右)我的意思是,在起始位置,例如相机朝向(0,0, 1)vector:

  • 对象位于(0,0,0)
  • cam位于(0,0,-2)
  • cam dir指向(0,0,1)

如果将操纵杆向上推90º,相机应该:

  • 移至(0,2,0)位置
  • cam dir现在指向(0,-1,0)

如果您现在将操纵杆向右推90度,相机应如下所示:

  • cam at pos(2,0,0)
  • cam dir vector(-1,0,0)

话虽如此,您可以通过让相机处于固定位置并旋转相机所期望的物体来实现相同目的。

一种简单的方法是生成一对四元数,每个轴旋转一个。但是,您要旋转的轴随着对象的旋转而变化。如果相机向上旋转向下看向物体中心,则四元数应定义为围绕右轴(1,0,0)旋转x度,以便相机向上旋转。一旦物体旋转使相机看起来像是向上,物体“向上”矢量不再是全局向上,而是由普氏四元数旋转的“全局向上矢量”。

方法:

1-对于每个帧,通过使用当前对象旋转旋转全局向上和向右矢量来计算局部向上和向右矢量。

2-获得本地向上和向右矢量后,请阅读操纵杆翻译量。

3-创建两个围绕局部向量旋转的新四元数。将两个四元数相乘,结果应与当前对象四元数一起应用(相乘)。

4-将最终的四元数转换为矩阵,并将其用作当前的模型视图矩阵。

修改

使用ShadingZen类的一些代码(我不使用libGDX):

class Camera{
static Vector3 mRight = new Vector3(1.f, 0.f, 0.f);
static Vector3 mUp = new Vector3(0.f, 1.f, 0.f);
static Vector3 mFront = new Vector3(0.f, 0.f, 1.f); // Maybe your front is (0, 0, -1) or (0, 1, 0)

Vector3 mLocalRight; = new Vector3(1.f, 0.f, 0.f);
Vector3 mLocalUp = new Vector3(0.f, 1.f, 0.f);
Vector3 mLocalFront = new Vector3(0.f, 0.f, 1.f);

Quaternion mRotation = new Quaternion(0.f, 0.f, 0.f, 1.f); // Identity rotation
Vector3 mCameraInitialPos = ...

...

/**
 * Compute local axis vectors given the current camera rotation 
 * tickDeltaTime is useful to prevent "jumps" in movement 

*/
private void updateRotation(){
    // Get local (rotated) vectors (current local axis)
    mLocalRight = mRotation.mul(mRight); // Rotate mRight using mRotation quaternion
    mLocalUp = mRotation.mul(mUp); 
    mLocalFront = mRotation.mul(mFront);

    Quaternion rotationAroundRightAxis = new Quaternion(mLocalRight, mJoystickAmmountY*tickDeltaTime);
    Quaternion rotationAroundUpAxis = new Quaternion(mLocalUp, mJoystickAmmountX*tickDeltaTime);

    // Chain rotations
    mRotation = mRotation.mul(rotationAroundRightAxis.mul(rotationAroundUpAxis));

    // Now mRotation contains this step or tick's rotation ammount nad past rotations
    mCameraPos = mRotation.mul(mCameraInitialPos);
}

public void update(boolean updateFrustum)
{
    updateRotation();

    float aspect = camera.viewportWidth / camera.viewportHeight;
    camera.projection.setToProjection(Math.abs(camera.near), Math.abs(camera.far), camera.fieldOfView, aspect);
    camera.view.setToLookAt(mCameraPos, mLocalFront, mLocalUp); // This probably computes your view matrix

    // Not sure if libGDX needs this or calculates internally
    camera.combined.set(camera.projection);
    Matrix4.mul(camera.combined.val, camera.view.val);

    if (updateFrustum)
    {
        camera.invProjectionView.set(camera.combined);
        Matrix4.inv(camera.invProjectionView.val);
        camera.frustum.update(camera.invProjectionView);
    }
}
}

答案 2 :(得分:1)

我会给你一些思考的提示。可能不是一个完整的答案,但它应该有所帮助!

1-是否真的有必要拥有数学上完美的棒?我的意思是,你可以从中心检测位移,并从中计算出你的角度。例如Stick以[x,y]为中心,用户手指位于[x + a,y + b],然后您的旋转角度可以是func(a),func(b),其中func()是您定义的函数(几乎总是凭经验)。例如:

   angleX = (a/30) % 360; //you should consider maximum displacements may be

2-小心使用四元数,

rotation.mul(newRotation); 
rotation.leftMul(newRotation);

不等同。通常,无论该模型是否已旋转,您都可以使用第二个选项按指定的轴旋转旋转的物体。这就是你想要做的,如果用户移动了,你可以向上旋转你的相机(你不关心相机是否有累积的旋转)。

3-要完成我的“廉价实施”答案,您可以使用Libgdx的setEulerAngles(浮动偏航,浮动俯仰,浮动滚动)轻松计算您的数量。所以:

   newRotation.setEulerAngles(angleX, angleY, 0). 

请记住左边这个四倍的数量,这样你就可以在所有三个轴上得到旋转!

可能缺少一些细节。可能你应该在setEulerAngles中切换参数,可能是你的凸轮不会看正确的地方,但我只是想表明有时一个便宜的解决方案是最好的解决方案(因为它是3线程近似)。至少应该足够好快速原型!