在libGDX中将Quaternion应用于相机

时间:2012-12-15 20:41:13

标签: java android opengl-es libgdx quaternions

我正在尝试使用libGDX中的Quaternion旋转相机。我有一个Quaternion被创建并被操纵,但我不知道如何将它应用到相机,我尝试的一切都没有移动相机。

以下是我设置轮换Quaternion

的方法
    public void rotateX(float amount) {
        tempQuat.set(tempVector.set(1.0f, 0.0f, 0.0f), amount * MathHelper.PIOVER180);
        rotation = rotation.mul(tempQuat);
    }

    public void rotateY(float amount) {
        tempQuat.set(tempVector.set(0.0f, 1.0f, 0.0f), amount * MathHelper.PIOVER180);
        rotation = tempQuat.mul(rotation);
    }

以下是我尝试更新相机的方法(与原始libGDX版本相同的更新方法,但我将关于旋转矩阵的部分添加到顶部):

    public void update(boolean updateFrustum) {
        float[] matrix = new float[16];
        rotation.toMatrix(matrix);
        Matrix4 m = new Matrix4();
        m.set(matrix);

        camera.view.mul(m);
        //camera.direction.mul(m).nor();
        //camera.up.mul(m).nor();

        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, tempVector.set(camera.position).add(camera.direction), camera.up);
        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 个答案:

答案 0 :(得分:2)

我必须对你的代码做两件事:

  1. 在调用setToLookAt之前无法修改视图矩阵, 这种方法重新计算孔矩阵是没用的。
  2. 更新()上的前几行非常浪费。避免     创造不必要的对象(特别是如果你打算更新你的     相机每个周期)。要将旋转应用于矩阵,您可以     调用[matrix] .rotate(quaternion)。
  3. 最后,有很多方法可以旋转你的相机,所以我无法真正解决你的问题,但如果你想看到旋转的东西,只需在camera.view.setToLookat之后调用camera.view.rotate(rotation) 。也许你应该旋转其他东西(例如方向向量,向上向量等),但你可以开始使用它:

    //protected float[] matrix = new float[16];//unused!
    
    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, tempVector.set(camera.position).add(camera.direction), camera.up);
    
            camera.view.rotate(q); // THIS IS THE ONLY REAL CHANGE TO YOUR CODE!
    
            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 :(得分:1)

我猜Quaternion.mul()是多个?我认为你需要做的不仅仅是一次乘法来进行旋转。这是我用于围绕轴角(四元数)旋转矢量或点的代码:

private double[] vecQuat = new double[4];
private double[] resQuat = new double[4];
private double[] thisQuat = new double[4];

private double[] conj = new double[4];

/**
 * Rotates a vector (or point) around this axis-angle
 * 
 * @param vectorX the x component of the vector (or point)
 * @param vectorY the y component of the vector (or point)
 * @param vectorZ the z component of the vector (or point)
 * @param outputArray the array in which the results will be stored
 */
public void RotateVector(double vectorX, double vectorY, double vectorZ, double[] outputArray){

    vecQuat[0] = 0.0f;
    vecQuat[1] = vectorX;
    vecQuat[2] = vectorY;
    vecQuat[3] = vectorZ;

    thisQuat[0] = w;
    thisQuat[1] = x;
    thisQuat[2] = y;
    thisQuat[3] = z;

    getConjugate(conj);
    Multiply(vecQuat,conj,resQuat);
    Multiply(thisQuat,resQuat,vecQuat);

    outputArray[0] = vecQuat[1];
    outputArray[1] = vecQuat[2];
    outputArray[2] = vecQuat[3];

}

public void getConjugate(double[] outputArray){

    outputArray[0] = w;
    outputArray[1] = -x;
    outputArray[2] = -y;
    outputArray[3] = -z;

}

public void Multiply(double[] aq, double[] rq, double[] outputArray){

    outputArray[0] = aq[0] * rq[0] - aq[1] * rq[1] - aq[2] * rq[2] - aq[3] * rq[3];
    outputArray[1] = aq[0] * rq[1] + aq[1] * rq[0] + aq[2] * rq[3] - aq[3] * rq[2];
    outputArray[2] = aq[0] * rq[2] + aq[2] * rq[0] + aq[3] * rq[1] - aq[1] * rq[3];
    outputArray[3] = aq[0] * rq[3] + aq[3] * rq[0] + aq[1] * rq[2] - aq[2] * rq[1];

}

我不知道libgdx,但也许看看它是否有像上面那样的旋转功能。如果没有,您可以将其添加到


编辑:

我不知道你想要用相机做什么,但这里有一个例子,我在季空中使用四元数来设置RTS游戏(想想家庭世界)。太空飞船有direction vector(他们进入的方向),用于更新每个刻度线的移动并将它们旋转到面向行进方向(在draw()函数中)。它们在船舶的位置和当前目的地之间也有target vector :.这艘船每秒只能转动turningCircle弧度。因此,在更新每个刻度的移动后,我取方向/目标矢量的叉积来获得旋转轴,并以turningCircle作为角度,以创建四元数。我通过四元数旋转船舶方向向量来“转向”船。

最终结果是船在多个刻度线上,以优美的弧度转弯,直到它向正确的方向前进。

同样的过程也可用于战斗飞行模拟器来模拟AI飞机的转向。使用四元数也可以避免“万向节锁定”,这是真正的飞机所遭受的!

然而,使用四元数,您的AI飞机可能会发现在一半时间内倒飞更方便,所以您可能不得不沿着机身轴线进行一些额外的慢速旋转(即z ())模拟飞行员将自己定向为“向上”。这实际上很容易通过向前(向)矢量

添加一个向上和向右矢量来实现

这是代码(减去向上矢量位的方向):

/**
 * The current position of the spaceship
 */
private Vertex3D currentPosition;

/**
 * The target position of the spaceship
 */
private Vertex3D targetPosition;

/**
 * The current direction in which the spaceship is travelling
 */
private Vector directionVector;

/**
 * The vector towards which the spaceship is turning
 */
private Vector targetVector;

/**
 * The right orientation vector
 */
private Vector rightOrientationVector;

/**
 * The up orientation vector
 */
private Vector upOrientationVector;

/**
 * Angle in radians by which directionVector turns towards TargetVector every tick
 */
private double turningCircle = 0.05f;

public Spaceship(Vertex3D target){

    currentPosition = new Vertex3D(0,0,0);

    // right hand coordinate system: ship is facing "away" from the camera
    directionVector = new Vector(currentPosition, 0,0,-1);
    rightOrientationVector = new Vector(currentPosition, 1,0,0);
    upOrientationVector = new Vector(currentPosition, 0,1,0);

    targetPosition = target;

}

    protected void tick(){
        incrementPosition();
        turn();
        draw();
    }


    protected void incrementPosition(){

        // get movement
        double velocity = getVelocity();

        // move
        currentPosition.mX(currentPosition.mX + directionVector.mX * velocity);
        currentPosition.mY(currentPosition.mY + directionVector.mY * velocity);
        currentPosition.mZ(currentPosition.mZ + directionVector.mZ * velocity);
    }


    private double[] cross = new double[3];
    private double[] newDir = new double[3];
    private Quaternion quat;

    protected void turn(){

        // update target vector relative to new position
        setTargetVector();

        // turn direction vector towards target vector
        MathsExtras.crossProduct(directionVector.mX, directionVector.mY, directionVector.mZ, targetVector.mX, targetVector.mY, targetVector.mZ, cross); 

        quat = new Quaternion(cross[0], cross[1], cross[2], turningCircle);

        quat.RotateVector(directionVector.mX, directionVector.mY, directionVector.mZ, newDir); 

        directionVector.mX = newDir[0];
        directionVector.mY = newDir[1];
        directionVector.mZ = newDir[2];

        direction.normalise();


        // update right orientation
        MathsExtras.crossProduct(direction.mX, direction.mY, direction.mZ, upOrientationVector.mX, upOrientationVector.mY, upOrientationVector.mZ, cross);

        rightOrientationVector.mX = cross[0];
        rightOrientationVector.mY = cross[1];
        rightOrientationVector.mZ = cross[2];

        rightOrientationVector.normalise();

        // update up orientation
        MathsExtras.crossProduct(rightOrientationVector.mX, rightOrientationVector.mY, rightOrientationVector.mZ, direction.mX, direction.mY, direction.mZ, cross);

        upOrientationVector.mX = cross[0];
        upOrientationVector.mY = cross[1];
        upOrientationVector.mZ = cross[2];

        upOrientationVector.normalise();

    }

    protected void setTargetVector(){
        targetVector.mX = targetPosition.getmX() - currentPosition.getmX();
        targetVector.mY = targetPosition.getmY() - currentPosition.getmY();
        targetVector.mZ = targetPosition.getmZ() - currentPosition.getmZ();
        targetVector.normalise();
    }

因此,如果您想使用相同的代码来说明将相机转向第一人称射击游戏中的物体,您可以将方向矢量设置为玩家正在查看的方向,并将currentPosition设置为玩家/摄像机位置,目标作为目标物体,以及转动角度/您希望摄像机转动的速度。在draw()中,您只需使用lookAt(directionVector.mX + currentPosition.mX, directionVector.mY + currentPosition.mY, directionVector.mZ + currentPosition.mZ)