2

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

3 回答 3

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(input) 因此,如果输入为无穷大,则角度将变化 90 度。但是您可以轻松保存/加载状态,您可以手动设置相机位置和伴随向量。因此,您拥有调用“lookAt”所需的一切。

于 2012-12-20T10:30:18.567 回答
2

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

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

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

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

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

  • 凸轮在 pos (2, 0, 0)
  • cam dir 向量 (-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);
    }
}
}
于 2012-12-20T15:53:33.503 回答
1

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

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(float yaw, float pitch, float roll) 轻松计算您的四元数。所以:

   newRotation.setEulerAngles(angleX, angleY, 0). 

记住要左乘这个四元数,这样你就可以在所有三个轴上旋转!

可能缺少几个细节。可能是您应该在 setEulerAngles 中切换参数,可能是您的凸轮不会看到正确的位置,但我只是想表明有时廉价的解决方案是最好的解决方案(因为它是 3 线性近似值)。至少对于快速原型应该足够好!

于 2012-12-21T14:44:52.027 回答