java - 来自 4x4 变换矩阵 (libgdx) 的意外旋转角度

标签 java 3d rotation libgdx linear-algebra

enter image description here

这是它的要点,你可以看到旋转角度从 0 - 236 跳到 119 - 0,很奇怪。

我想获得模型相对于其 y 轴的角度 (0-360)。但是到目前为止,使用 libgdx 我无法使用以下方法获得预期结果:

angle = modelInstance.transform.getRotation(new Quaternion()).getAxisAngle(Vector3.Zero)

假设我只绕 y 轴旋转,这是从 libgdx 中的变换矩阵获取旋转角度的正确方法吗?

我已经包含了一个完整的示例类来证明我通过这种方式获得的旋转角度不是预期的,而不是 0 - 360 的完整旋转我得到类似 0 - 117 然后跳转到243.

public class RotateExperiment extends InputAdapter implements ApplicationListener {

    private Environment environment;
    private ModelBatch modelBatch;
    private Camera camera;
    private ModelInstance player;

    @Override
    public void create() {
        this.player = new ModelInstance(new ModelBuilder().createCone(10, 10,10, 10, new Material(ColorAttribute.createDiffuse(Color.GRAY)), VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal));

        int w = Gdx.graphics.getWidth();
        int h = Gdx.graphics.getHeight();

        modelBatch = new ModelBatch();
        camera = new PerspectiveCamera(67, w, h);
        camera.position.set(10f, 10f, 10f);
        camera.lookAt(0,0,0);
        camera.near = 0.1f;
        camera.far = 300f;
        camera.update();

        environment = new Environment();
        environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
        environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
    }

    @Override
    public void dispose() {}

    private void updateModel(){
        Vector3 directionVector = new Vector3(0,0,0);
        if(Gdx.input.isKeyPressed(Input.Keys.S)){
            directionVector.z = -1;
        }
        if(Gdx.input.isKeyPressed(Input.Keys.W)){
            directionVector.z = 1;
        }
        if(Gdx.input.isKeyPressed(Input.Keys.A)){
            if(Gdx.input.isButtonPressed(Input.Buttons.RIGHT)){
                directionVector.y = 1;
            } else {
                player.transform.rotate(Vector3.Y, 90 * Gdx.graphics.getDeltaTime());
            }
        }
        if(Gdx.input.isKeyPressed(Input.Keys.D)){
            if(Gdx.input.isButtonPressed(Input.Buttons.RIGHT)){
                directionVector.y = -1;
            } else {
                player.transform.rotate(Vector3.Y, -90 * Gdx.graphics.getDeltaTime());
            }
        }
        if(directionVector.z != 0 || directionVector.y != 0){
            player.transform.translate(directionVector.nor().scl(20 * Gdx.graphics.getDeltaTime()));
        }
    }

    public void update() {
        if(player != null){
            updateModel();
            //update angle text -> actual test code
            outputText.setText(String.valueOf(player.transform.getRotation(new Quaternion()).getAxisAngle(Vector3.Zero)));
        }
    }

    @Override
    public void render() {
        update();
        Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

        modelBatch.begin(camera);
        modelBatch.render(player, environment);
        modelBatch.end();
    }

    @Override
    public void resize(int width, int height) {
        Gdx.gl.glViewport(0, 0, width, height);
    }

    @Override
    public void pause() {}

    @Override
    public void resume() {}

    private final static JLabel outputText = new JLabel("test");

    public static void main(String[] args) {
        LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
        cfg.title = "TestRotation";
        cfg.useGL20 = true;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        cfg.width = screenSize.width;
        cfg.height = screenSize.height;

        new LwjglApplication(new RotateExperiment(), cfg);

        JFrame outputWindow = new JFrame();
        JPanel outputPanel = new JPanel();

        outputPanel.add(outputText);
        outputWindow.add(outputPanel);
        outputWindow.setAlwaysOnTop(true);
        outputWindow.setVisible(true);
        outputWindow.pack();
        outputWindow.toFront();
    }
}

如果您正在运行该示例,请在加载后单击 libgdx 渲染窗口,然后可以使用“A”和“D”控制模型旋转。使用上述方法产生的旋转也在单独的 JFrame 中输出。

编辑: 我相信可疑代码可能位于将 4x4 变换矩阵转换为四元数的 libgdx 方法中:Quaternion.setFromAxes。当您调用 transform.getRotation() 时,它会调用 setFromAxes 并传入矩阵轴。我看过开issue ,但是正如您在他的示例中看到的那样,我的立方体模型没有缩放,所以这不可能是正在发生的事情。 setFromAxes代码如下:

            // the trace is the sum of the diagonal elements; see
    // http://mathworld.wolfram.com/MatrixTrace.html
    final float m00 = xx, m01 = xy, m02 = xz;
    final float m10 = yx, m11 = yy, m12 = yz;
    final float m20 = zx, m21 = zy, m22 = zz;
    final float t = m00 + m11 + m22;

    // we protect the division by s by ensuring that s>=1
    double x, y, z, w;
    if (t >= 0) { // |w| >= .5
        double s = Math.sqrt(t + 1); // |s|>=1 ...
        w = 0.5 * s;
        s = 0.5 / s; // so this division isn't bad
        x = (m21 - m12) * s;
        y = (m02 - m20) * s;
        z = (m10 - m01) * s;
    } else if ((m00 > m11) && (m00 > m22)) {
        double s = Math.sqrt(1.0 + m00 - m11 - m22); // |s|>=1
        x = s * 0.5; // |x| >= .5
        s = 0.5 / s;
        y = (m10 + m01) * s;
        z = (m02 + m20) * s;
        w = (m21 - m12) * s;
    } else if (m11 > m22) {
        double s = Math.sqrt(1.0 + m11 - m00 - m22); // |s|>=1
        y = s * 0.5; // |y| >= .5
        s = 0.5 / s;
        x = (m10 + m01) * s;
        z = (m21 + m12) * s;
        w = (m02 - m20) * s;
    } else {
        double s = Math.sqrt(1.0 + m22 - m00 - m11); // |s|>=1
        z = s * 0.5; // |z| >= .5
        s = 0.5 / s;
        x = (m02 + m20) * s;
        y = (m21 + m12) * s;
        w = (m10 - m01) * s;
    }

    return set((float)x, (float)y, (float)z, (float)w);

最佳答案

因此,假设您有一个非缩放模型,并且您只执行了围绕 y 轴的旋转,下面的代码将仅从 libgdx 中模型的变换矩阵获得一个 0 -360 的角度:

        Vector3 axisVec = new Vector3();
        int angle = (int) (player.transform.getRotation(new Quaternion()).getAxisAngle(axisVec) * axisVec.nor().y);
        angle = angle < 0 ? angle + 360 : angle; //convert <0 values

如果您有缩放模型,则当前解决方案根据开放 issue是规范化 setFromAxes 代码中的轴,我已将 setFromAxes 替换为以下内容,直到更新为止:

private Quaternion setFromAxes(float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz){

    //normalise axis
    Vector3 xAxis = new Vector3(xx, xy, xz).nor();
    Vector3 yAxis = new Vector3(yx, yy, yz).nor();
    Vector3 zAxis = new Vector3(zx, zy, zz).nor();

    xx = xAxis.x;
    xy = xAxis.y;
    xz = xAxis.z;

    yx = yAxis.x;
    yy = yAxis.y;
    yz = yAxis.z;

    zx = zAxis.x;
    zy = zAxis.y;
    zz = zAxis.z;

    // the trace is the sum of the diagonal elements; see
    // http://mathworld.wolfram.com/MatrixTrace.html
    final float m00 = xx, m01 = xy, m02 = xz;
    final float m10 = yx, m11 = yy, m12 = yz;
    final float m20 = zx, m21 = zy, m22 = zz;
    final float t = m00 + m11 + m22;

    // we protect the division by s by ensuring that s>=1
    double x, y, z, w;
    if (t >= 0) { // |w| >= .5
        double s = Math.sqrt(t + 1); // |s|>=1 ...
        w = 0.5 * s;
        s = 0.5 / s; // so this division isn't bad
        x = (m21 - m12) * s;
        y = (m02 - m20) * s;
        z = (m10 - m01) * s;
    } else if ((m00 > m11) && (m00 > m22)) {
        double s = Math.sqrt(1.0 + m00 - m11 - m22); // |s|>=1
        x = s * 0.5; // |x| >= .5
        s = 0.5 / s;
        y = (m10 + m01) * s;
        z = (m02 + m20) * s;
        w = (m21 - m12) * s;
    } else if (m11 > m22) {
        double s = Math.sqrt(1.0 + m11 - m00 - m22); // |s|>=1
        y = s * 0.5; // |y| >= .5
        s = 0.5 / s;
        x = (m10 + m01) * s;
        z = (m21 + m12) * s;
        w = (m02 - m20) * s;
    } else {
        double s = Math.sqrt(1.0 + m22 - m00 - m11); // |s|>=1
        z = s * 0.5; // |z| >= .5
        s = 0.5 / s;
        x = (m02 + m20) * s;
        y = (m21 + m12) * s;
        w = (m10 - m01) * s;
    }

    return new Quaternion((float)x, (float)y, (float)z, (float)w);

}

关于java - 来自 4x4 变换矩阵 (libgdx) 的意外旋转角度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21062423/

相关文章:

php - 是否可以为 3D 打印对象编程/生成文件?

java - 在 Java 中绘制 3D 函数和点

java - Java 3D 中的元胞自动机

java - 图像旋转代码有什么问题?

c++ - 如何使用偏航、俯仰和滚动来旋转 3D vector ?

swift - iOS 8 : MapKit's rotateEnabled toggling not working right away

java - Spring Boot 中 YAML 处理器(Jackson、SnakeYAML)的值转换错误

java - 随机文本生成器

java - 如何为 System.out.println 输出着色?

java - 如何在 docx java 开头添加文本