首页 > 解决方案 > Transformation Hierarchy (Problem with modelling transformation of parent on children), OpenGL, LWJGL 3

问题描述

我目前正在 OpenGL 中构建一个场景图,我试图对他们的孩子进行父级转换建模,但它似乎不起作用。:(

我的主要问题是孩子的旋转没有正确跟随父母的旋转。子对象围绕父对象(中心)旋转,这是它应该做的,但是它围绕父对象的速度随着父对象的旋转增加而增加,并且一旦父对象完成一个完整的圆圈(当父对象的旋转回到它开始的旋转)。此外,我确实在正确翻译孩子相对于父母方面遇到了一些问题,但我设法暂时修复了它们(我认为)。代码中的更多信息。很抱歉解释冗长,如果可以的话,我肯定会附上这个问题的视频。

这是我的转换类,其中设置了转换,特别是getTransformation()方法:

public class Transform {
    
    private Vector3f position, lastPosition;
    private Vector3f rotation, lastRotation;
    private Vector3f scale, lastScale;
    
    private Transform parent;
    private Matrix4f parentMatrix;
    
    public Transform() {
        this(null, null, null);
    }
    
    public Transform(Vector3f position, Vector3f rotation, Vector3f scale) {
        this.position = position != null ? position : new Vector3f(0, 0, 0);
        this.rotation = rotation != null ? rotation : new Vector3f(0, 0, 0);
        this.scale = scale != null ? scale : new Vector3f(1, 1, 1);
        
        this.lastPosition = new Vector3f(0, 0, 0);
        this.lastRotation = new Vector3f(0, 0, 0);
        this.lastScale = new Vector3f(1, 1, 1);
        
        parentMatrix = new Matrix4f().identity();
    }
    
    public boolean requireTMUpdate() { //checks if the matrix has changed and requires an update
        
        if(parent != null) {
            return parent.requireTMUpdate();
        }
        
        if(!position.equals(lastPosition)) {
            lastPosition.set(position);
            return true;
        }
        
        if(!rotation.equals(lastRotation)) {
            lastRotation.set(rotation);
            return true;
        }
        
        if(!scale.equals(lastScale)) {
            lastScale.set(scale);
            return true;
        }
        
        return false;
    }
    
    public Matrix4f getTransformation() {
        
        if((parent != null) && (requireTMUpdate())) {
            if(!(getParent().equals(getParent().getParent()))) {

                parentMatrix.set(parent.getTransformation()); 

                //The above line sets the updated parentMatrix

                setPosition(parentMatrix.transformPosition(position)); 

                //The above line sets the position to where child is supposed to be in 
                //relation the parent, otherwise once the key is stopped being pressed, 
                //it will return to the position at the start of the game / program.
            }
            
        }else {
            parentMatrix.rotationXYZ(0, 0, 0).translation(0, 0, 0).scale(1); 
            
            // The above line is supposed to reset the matrix, otherwise the previous 
            //transformations or rotations add up each frame and the it just gets messed 
            //up.
        }
        
        //System.out.println(parentMatrix.toString());
        
        return parentMatrix.mul(Matrices.transformationMatrix(position, rotation, scale)) 
        
        // The transformationMatrix() method above from the Matrices class is 
        //supposed to return a worldMatrix.
    }
    
    public Vector3f getPosition() {
        return position;
    }
    
    public void setPosition(Vector3f position) {
        this.position = position;
    }
    
    public Vector3f getRotation() {
        return rotation;
    }
    
    public void setRotation(Vector3f rotation) {
        this.rotation = rotation;
    }
    
    public Vector3f getScale() {
        return scale;
    }
    
    public void setScale(Vector3f scale) {
        this.scale = scale;
    }
    
    public Transform getParent() {
        return parent;
    }

    public void setParent(Transform parent) {
        this.parent = parent;
    }
    
    
}

矩阵类:

public class Matrices {
    
    public static Matrix4f transformationMatrix(Vector3f pos, Vector3f rot, Vector3f scale) {
        Matrix4f result = new Matrix4f();
        
        result.identity();
        
        result.translate(pos.x, pos.y, pos.z);
        
        Quaternionf rotation = 
                new Quaternionf().
                identity().
                rotateX((float) Math.toRadians(rot.x)).
                rotateY((float) Math.toRadians(rot.y)).
                rotateZ((float) Math.toRadians(rot.z));
        
        /*result.rotate((float) Math.toRadians(rot.getX()), 1, 0, 0);
        result.rotate((float) Math.toRadians(rot.getY()), 0, 1, 0);
        result.rotate((float) Math.toRadians(rot.getZ()), 0, 0, 1);*/
        
        result.rotate(rotation);
        
        result.scale(scale.x, scale.y, scale.z);
        
        return result;
        
    }
    
    public static Matrix4f transformationMatrix(Vector3f pos, Quaternionf rotation, Vector3f scale) {
        Matrix4f result = new Matrix4f();
        
        result.identity();
        
        result.translate(pos.x, pos.y, pos.z);
        
        /*Quaternionf rotation = 
                new Quaternionf().
                identity().
                rotateX((float) Math.toRadians(rot.x)).
                rotateY((float) Math.toRadians(rot.y)).
                rotateZ((float) Math.toRadians(rot.z));*/
        
        /*result.rotate((float) Math.toRadians(rot.getX()), 1, 0, 0);
        result.rotate((float) Math.toRadians(rot.getY()), 0, 1, 0);
        result.rotate((float) Math.toRadians(rot.getZ()), 0, 0, 1);*/
        
        result.rotate(rotation);
        
        result.scale(scale.x, scale.y, scale.z);
        
        return result;
        
    }
    
    public static Matrix4f viewMatrix(Vector3f pos, Vector3f rot) {
        Matrix4f result = new Matrix4f();
        
        result.identity();
        
        Quaternionf rotation = 
                new Quaternionf().
                identity().
                rotateX((float) Math.toRadians(rot.x)).
                rotateY((float) Math.toRadians(rot.y)).
                rotateZ((float) Math.toRadians(rot.z));
        
        /*result.rotate((float) Math.toRadians(rot.getX()), new org.joml.Vector3f(1, 0, 0));
        result.rotate((float) Math.toRadians(rot.getY()), new org.joml.Vector3f(0, 1, 0));*/
        
        result.rotate(rotation);
        
        result.translate(-pos.x, -pos.y, -pos.z);
        
        return result;
    }
    
    public static Matrix4f viewMatrix(Vector3f pos, float pitch, float yaw, float roll) {
        Matrix4f result = new Matrix4f();
        
        result.identity();
        
        Quaternionf rotation = 
                new Quaternionf().
                identity().
                rotateX((float) Math.toRadians(pitch)).
                rotateY((float) Math.toRadians(yaw)).
                rotateZ((float) Math.toRadians(roll));
        
        /*result.rotate((float) Math.toRadians(rot.getX()), new org.joml.Vector3f(1, 0, 0));
        result.rotate((float) Math.toRadians(rot.getY()), new org.joml.Vector3f(0, 1, 0));*/
        
        result.rotate(rotation);
        
        result.translate(-pos.x, -pos.y, -pos.z);
        
        return result;
    }
    
    public static Matrix4f projectionMatrix(float FOV, float aspectRatio, 
                                            float NearPlaneDis, float FarPlaneDis) {
        Matrix4f result = new Matrix4f();
        
        result.identity();
        
        result.perspective(FOV, aspectRatio, NearPlaneDis, FarPlaneDis);
        
        /*float y_scale = (float) ((1f / Math.tan(Math.toRadians(FOV / 2f))) * aspectRatio);
        float x_scale = y_scale / aspectRatio;
        float frustum_length = FarPlaneDis - NearPlaneDis;
        
        result.m00(x_scale);
        result.m11(y_scale);
        result.m22(-((FarPlaneDis + NearPlaneDis) / frustum_length));
        result.m23(-1);
        result.m32(-((2 * NearPlaneDis * FarPlaneDis) / frustum_length));
        result.m33(0);*/
        
        return result;
    }
    
    public static Matrix4f translate(Vector3f vec, Matrix4f src, Matrix4f dest) {
        if (dest == null)
            dest = new Matrix4f();

        dest.m30(dest.m30() + src.m00() * vec.x + src.m10() * vec.y + src.m20() * vec.z);
        dest.m31(dest.m31() + src.m01() * vec.x + src.m11() * vec.y + src.m21() * vec.z);
        dest.m32(dest.m32() + src.m02() * vec.x + src.m12() * vec.y + src.m22() * vec.z);
        dest.m33(dest.m33() + src.m03() * vec.x + src.m13() * vec.y + src.m23() * vec.z);

        return dest;
    }
    
    public static Vector4f transform(Matrix4f left, Vector4f right, Vector4f dest) {
        if (dest == null)
            dest = new Vector4f(0, 0, 0, 0);

        float x = left.m00() * right.x + left.m10() * right.y + left.m20() * right.z + left.m30() * right.w;
        float y = left.m01() * right.x + left.m11() * right.y + left.m21() * right.z + left.m31() * right.w;
        float z = left.m02() * right.x + left.m12() * right.y + left.m22() * right.z + left.m32() * right.w;
        float w = left.m03() * right.x + left.m13() * right.y + left.m23() * right.z + left.m33() * right.w;

        dest.x = x;
        dest.y = y;
        dest.z = z;
        dest.w = w;

        return dest;
    }
    
    public static Vector3f scale(Vector3f vector, float scale) {

        vector.x *= scale;
        vector.y *= scale;
        vector.z *= scale;

        return vector;

    }
    
    public static float[] getAll(Matrix4f matrix) {
        float[] f = new float[16];
        return matrix.get(f);
    }
    
    public static float barryCentric(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f pos) {
        float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
        float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
        float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
        float l3 = 1.0f - l1 - l2;
        return l1 * p1.y + l2 * p2.y + l3 * p3.y;
    }

}

最后是初始化对象的 Renderer 类:

public class Renderer {
    
    private Model model;
    
    private Model model2;
    
    private GameObject root;
    
    private GameObject child;
    
    public Camera camera;
    
    private float time = 0;

    /*private float vertices[] = {
        0.5f, 0.5f, 0f,    //0 - top right
        0.5f, -0.5f, 0f,   //1 - bottom right
        -0.5f, -0.5f, 0f,  //2 - bottom left
        -0.5f, 0.5f, 0f    //3 - top left
    };
    
    private int indices[] = {
            0, 1, 3,   // first triangle
            3, 1, 2    // second triangle
    };*/
    
    public Renderer() {
        model = new Model("/backpack.obj");
        model2 = new Model("/backpack.obj");
        
        camera = new Camera(new Vector3f(0, 0, 0), new Vector3f(0, 0, 0));
        root = new GameObject();
        child = new GameObject();
        root.addComponent(camera);
        root.addComponent(model);
        child.addComponent(model2);
        root.addChild(child);
        
        model.getTransform().setPosition(new Vector3f(0, 0, 0));
        model2.getTransform().setPosition(new Vector3f(10, 0, 10));
        
    }
    
    public void render(Shader shader) {
        if(Input.isKeyDown(GLFW.GLFW_KEY_RIGHT)) {
            model.getTransform().setRotation(new Vector3f(0, time += 1f, 0));
        }
        //model2.getTransform().setRotation(new Vector3f(0, time += 0.01f, 0));
        shader.bind();
        root.input();
        root.update();
        root.render(shader, camera);
        shader.unbind();
    }
    
    public void cleanUp() {
        for(Mesh mesh: model.getMeshes()) {
            mesh.cleanUp();
        }
    }
}

我在这里到底做错了什么?任何帮助表示赞赏!

标签: javaopengllwjglmatrix-multiplicationscenegraph

解决方案


Nvm,经过一天的调试,我找到了解决方案:

这一行:

return parentMatrix.mul(Matrices.transformationMatrix(position, rotation, scale));

实际上应该是:

return new Matrix4f(parentMatrix).mul(Matrices.transformationMatrix(position, rotation, scale));.

结果发现,mulJOML Matrix4f 类中的方法没有返回Matrix4f()应该输出的 worldMatrix 的新类,而是将其与当前矩阵(parentMatrix此处)本身相乘,从而parentMatrix为下一帧创建一个完全不同的新类。


推荐阅读