2个方向的3D旋转?

5

背景

我正在尝试使用LWJGL 3.0在Java中创建一款FPS游戏。我已经设置了一个相机类,它具有俯仰和偏航(滚动未被使用)。相机本身是继承自实体(Entity)的,因为它有一个模型。我希望这个模型总是出现在相机的“前方”,无论相机指向哪里。每个实体都有一个方法getTransformationMatrix(),它返回一个Matrix4f,然后传递到实体着色器中。

问题

模型需要指向相机的方向,并围绕相机旋转,以确保其始终位于前方。在这种情况下,对象是一双持枪的手,如下图所示。

enter image description here

我的尝试

我了解基本的三角学,所以我成功地让物体在俯仰和偏航方向上正确旋转。这是我目前的实现:

偏航

@Override
public Matrix4f getTransformationMatrix() {
    modelX = getPosition().x + (radius * (float)Math.sin(Math.toRadians(getYaw())));
    modelZ = getPosition().z + (radius * (float)Math.cos(Math.toRadians(getYaw())));

    return Transform.createTransformationMatrix(new Vector3f(modelX, getPosition().y - 5, modelZ), new Vector3f(0, getYaw(), 0), getScale());
}

音高

@Override
public Matrix4f getTransformationMatrix() {
    modelZ = getPosition().z + (radius * (float)Math.sin(Math.toRadians(getPitch())));
    modelY = (getPosition().y - 5) + (radius * (float)Math.cos(Math.toRadians(getPitch())));

    return Transform.createTransformationMatrix(new Vector3f(getPosition().x, modelY, modelZ), new Vector3f(getPitch(), 0, 0), getScale());
}

我已经做了一些研究,但是我担心我在这个问题上卡得太久了,需要一些新的眼睛。当我尝试将这两个计算结合起来时,模型似乎在除了0度偏航角之外的任何偏航角度上都呈现出图形的形状。以下是我尝试将它们结合起来的结果:

@Override
public Matrix4f getTransformationMatrix() {
    float zAxis = (radius * (float)Math.sin(Math.toRadians(getPitch())));
    modelY = (getPosition().y - 5) + (radius * (float)Math.cos(Math.toRadians(getPitch())));
    modelZ = getPosition().z + (zAxis * (float)Math.cos(Math.toRadians(getYaw())));
    modelX = getPosition().x + (radius * (float)Math.sin(Math.toRadians(getYaw())));

    return Transform.createTransformationMatrix(new Vector3f(modelX, modelY, modelZ), new Vector3f(getPitch(), getYaw(), 0), getScale());
}

Transform.createTransformationMatrix()的代码如下:

public static Matrix4f createTransformationMatrix(Vector3f translation, Vector3f rotation, Vector3f scale) {
    transform3d = new Matrix4f();
    transform3d.setIdentity();
    Matrix4f.translate(translation, transform3d, transform3d);
    Matrix4f.rotate((float) Math.toRadians(rotation.x), new Vector3f(1, 0, 0), transform3d, transform3d);
    Matrix4f.rotate((float) Math.toRadians(rotation.y), new Vector3f(0, 1, 0), transform3d, transform3d);
    Matrix4f.rotate((float) Math.toRadians(rotation.z), new Vector3f(0, 0, 1), transform3d, transform3d);
    Matrix4f.scale(scale, transform3d, transform3d);
    return transform3d;
}

思路

一个朋友建议创建一个指向上方的单位向量(即new Vector3f(0, 1, 0)),将该向量旋转pitch和yaw,然后将该向量乘以半径并加到相机位置。我尝试了这个方法,但我不知道如何通过角度旋转向量,并且在slick-utils Vector3f类中似乎没有Vector3f.rotate()方法。非常感谢任何帮助,因为这已经让我头痛了几天。谢谢!


3
绕任意轴旋转任意角度都可以表示为一个 4x4 矩阵,具体描述见这里。先绕 yaw 转动,再绕 pitch 转动可以表示成两个矩阵的乘积。注意顺序,它与先 pitchyaw 的顺序不同。 - Ripi2
你真的需要这个矩阵(即在世界中的位置)吗?将矩阵放在视图空间中不就足够了吗,因为它是恒定的?当你绘制模型时,将视图矩阵设置为单位矩阵(因为你的模型已经在视图空间中),并适当地设置模型矩阵(可能只需轻微地远离相机进行平移)。 - Nico Schertler
@NicoSchertler 我有一个视图矩阵传递到着色器中,还有一个变换矩阵也被传递进去,然后它们在着色器中相乘,以计算模型每个顶点的世界空间。你所建议的方法如何应用于这种情况?我尝试仅在着色器中应用视图矩阵,但它没有正常工作。 - Kris Rice
1
我是说不要应用视图矩阵(设置为单位矩阵)。这样,模型将相对于相机具有恒定的位置。 - Nico Schertler
我回家后会检查并告诉你,谢谢! - Kris Rice
显示剩余2条评论
1个回答

3
我们通常做的是,使用长度为1的向量作为我们的“轴”。在2D旋转中,我们一直使用一个轴——Z轴。

3D旋转

如果你像在2D中那样看轴,你会看到这样的东西:

2D旋转

因此,要在3D中旋转一个点,可以使用矩阵或向量。我建议先使用向量,这样你就可以了解3D旋转的工作原理。它让你大开眼界!

我将从theBennyBox的Vector3f类中放置代码。如果您对更多的数学感兴趣,请关注theBennyBox的Youtube频道。

Vector3f

    public Vector3F rotate(float angle, Vector3F axis) {
    double a = Math.toRadians(angle / 2f);
    float hs = (float) Math.sin(a);
    float hc = (float) Math.cos(a);
    Vector4F r = new Vector4F(axis.getX() * hs, axis.getY() * hs, axis.getZ() * hs, hc);
    Vector4F rc = r.conjugate();
    r = r.multiplyAsQuat(this).multiplyAsQuat(rc);

    return new Vector3F(r.getX(), r.getY(), r.getZ());
    }

四维向量

    public Vector4F multiplyAsQuat(Vector3F v) {
    float o = -x * v.getX() - y * v.getY() - z * v.getZ();
    float a = w * v.getX() + y * v.getZ() - z * v.getY();
    float b = w * v.getY() + z * v.getX() - x * v.getZ();
    float c = w * v.getZ() + x * v.getY() - y * v.getX();

    return new Vector4F(a, b, c, o);
}

    public Vector4F conjugate() {
    return new Vector4F(-x, -y, -z, w);
}

    public Vector4F multiplyAsQuat(Vector4F qt) {

    float o = w * qt.getW() - x * qt.getX() - y * qt.getY() - z * qt.getZ();
    float a = x * qt.getW() + w * qt.getX() + y * qt.getZ() - z * qt.getY();
    float b = y * qt.getW() + w * qt.getY() + z * qt.getX() - x * qt.getZ();
    float c = z * qt.getW() + w * qt.getZ() + x * qt.getY() - y * qt.getX();

    return new Vector4F(a, b, c, o);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接