繁体   English   中英

从2D游戏杆获取3D旋转轴和角度

[英]Get 3D Rotation Axis and Angle From 2D Joystick

我的左下角屏幕上有一个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());
}

我当前在Joystick类中的getAxis()方法:

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,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”所需的一切。

您可能希望基于其当前旋转状态来更新旋转,而不是仅在固定的全局轴巴黎上(右和右)旋转,我的意思是,在开始位置,例如,摄像机朝(0,0,1)向量看:

  • 对象位于(0,0,0)
  • 凸轮位于(0,0,-2)
  • cam目录指向(0,0,1)

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

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

如果现在将操纵杆向右旋转90º,则摄像机应如下所示:

  • 凸轮在位置(2,0,0)
  • cam dir矢量(-1,0,0)

话虽这么说,您可以通过将相机置于固定位置并旋转相机所要寻找的对象来完成此操作。

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

方法:

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

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

1-是否真的需要数学上完美的棍子? 我的意思是,您只需要检测距中心的位移,然后据此计算角度即可。 例如。 操纵杆以[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中切换参数,可能是您的cam不会在正确的位置看,但是我只是想表明有时候便宜的解决方案是最好的解决方案(因为它是3个线性的近似值)。 至少对于快速原型应该已经足够好了!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM