Java OpenGL矩阵奇异变换

Java OpenGL矩阵奇异变换,java,opengl,matrix,lwjgl,Java,Opengl,Matrix,Lwjgl,我一直在尝试制作一个游戏引擎,只是为了学习一些新的东西,一切都正常,但我后来决定,对于照明,我将制作自己的矩阵类,并将最终的矩阵传递给着色器。不幸的是,这最终破坏了整个渲染系统,因为现在所有的东西都在拉伸,这肯定是不对的。目前我已经搞乱了乘法的顺序,但它仍然不起作用,我已经有一个多月的问题了,我只想修复它,继续制造引擎 首先,这里是在PC上使用的默认着色器: public static final String[] pcVertexShaderCode = new String[] {

我一直在尝试制作一个游戏引擎,只是为了学习一些新的东西,一切都正常,但我后来决定,对于照明,我将制作自己的矩阵类,并将最终的矩阵传递给着色器。不幸的是,这最终破坏了整个渲染系统,因为现在所有的东西都在拉伸,这肯定是不对的。目前我已经搞乱了乘法的顺序,但它仍然不起作用,我已经有一个多月的问题了,我只想修复它,继续制造引擎

首先,这里是在PC上使用的默认着色器:

public static final String[] pcVertexShaderCode = new String[] {
    "attribute vec4 andor_vertexPosition;",
    "attribute vec3 andor_normal;",
    "attribute vec2 andor_vtextureCoord;",
    "attribute vec4 andor_vcolour;",
    "uniform mat4 andor_modelmatrix;",
    "uniform mat4 andor_viewmatrix;",
    "uniform mat4 andor_projectionmatrix;",
    "uniform mat4 andor_modelviewprojectionmatrix;",
    "varying vec4 andor_colour;",
    "varying vec2 andor_textureCoord;",
    "void andor_main();",
    "void main() {",
    "  andor_colour = andor_vcolour;",
    "  andor_textureCoord = andor_vtextureCoord;",
   // "  andor_modelviewprojectionmatrix = andor_modelmatrix * andor_viewmatrix * andor_projectionmatrix;",
    "  gl_Position = andor_modelviewprojectionmatrix * andor_vertexPosition;",
    "  andor_main();",
    "}" };

public static final String[] pcFragmentShaderCode = new String[] {
    "uniform sampler2D andor_texture;",
    "uniform float andor_hasTexture;",
    "varying vec4 andor_colour;",
    "varying vec2 andor_textureCoord;",
    "void andor_main();",
    "void main() {",
    "  if (andor_hasTexture > 0.5) {",
    "    gl_FragColor = andor_colour * texture2D(andor_texture, andor_textureCoord);",
    "  } else {",
    "    gl_FragColor = andor_colour;",
    "  }",
    "  andor_main();",
    "}" };
这是场景中每个对象调用的渲染方法:

    /* The method used to draw the object */
    public void render() {
        //Multiply the matrices together
        Matrix4D projectionViewMatrix = Matrix.multiply(Matrix.projectionMatrix, Matrix.viewMatrix);
        Matrix.modelViewProjectionMatrix = (Matrix.multiply(projectionViewMatrix, Matrix.modelMatrix));

        //Sky box in relatively the right place
//      Matrix4D modelViewMatrix = Matrix.multiply(Matrix.modelMatrix, Matrix.viewMatrix);
//      Matrix.modelViewProjectionMatrix = (Matrix.multiply(modelViewMatrix, Matrix.projectionMatrix));

//      Matrix4D modelProjectionMatrix = Matrix.multiply(Matrix.modelMatrix, Matrix.projectionMatrix);
//      Matrix.modelViewProjectionMatrix = (Matrix.multiply(Matrix.viewMatrix, modelProjectionMatrix));

//      Matrix4D modelViewMatrix = Matrix.multiply(Matrix.modelMatrix, Matrix.viewMatrix);
//      Matrix.modelViewProjectionMatrix = (modelViewMatrix);

//      //TEST
//      for (int a = 0; a < 16; a++) {
//          //2D WORKS!!!!! Kind of...
//          Matrix.modelViewProjectionMatrix.values[a] = Matrix.modelMatrix.values[a] * Matrix.projectionMatrix.values[a] * Matrix.viewMatrix.values[a];
//      }
        System.out.println(Matrix.modelViewProjectionMatrix.toString() + "\n");
        //Set the correct android shader
        Shader shader = defaultShader;
        if (currentShader != null)
            shader = currentShader;
        //Use the shader program
        GL20.glUseProgram(shader.program);
        //Enable the arrays as needed
        int vertexPositionAttribute = shader.getAttributeLocation("andor_vertexPosition");
        int normalAttribute = 0;
        int colourAttribute = 0;
        int texturesAttribute = 0;
        int modelMatrixAttribute = shader.getUniformLocation("andor_modelmatrix");
        int viewMatrixAttribute = shader.getUniformLocation("andor_viewmatrix");
        int projectionMatrixAttribute = shader.getUniformLocation("andor_projectionmatrix");
        int matrixAttribute = shader.getUniformLocation("andor_modelviewprojectionmatrix");
        GL20.glUniformMatrix4(modelMatrixAttribute, false, BufferUtils.createFlippedBuffer(Matrix.modelMatrix.getValues()));
        GL20.glUniformMatrix4(viewMatrixAttribute, false, BufferUtils.createFlippedBuffer(Matrix.viewMatrix.getValues()));
        GL20.glUniformMatrix4(projectionMatrixAttribute, false, BufferUtils.createFlippedBuffer(Matrix.projectionMatrix.getValues()));
        GL20.glUniformMatrix4(matrixAttribute, false, BufferUtils.createFlippedBuffer(Matrix.modelViewProjectionMatrix.getValues()));
        GL20.glEnableVertexAttribArray(vertexPositionAttribute);
        GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.verticesHandle);
        GL20.glVertexAttribPointer(vertexPositionAttribute, this.vertexValuesCount, GL11.GL_FLOAT, false, 0, 0);
        if (this.normalsData != null) {
            normalAttribute = shader.getAttributeLocation("andor_normal");
            GL20.glEnableVertexAttribArray(normalAttribute);
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.normalsHandle);
            GL20.glVertexAttribPointer(normalAttribute, this.vertexValuesCount, GL11.GL_FLOAT, false, 0, 0);
        }
        if (this.colourData != null) {
            colourAttribute = shader.getAttributeLocation("andor_vcolour");
            GL20.glEnableVertexAttribArray(colourAttribute);
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.coloursHandle);
            GL20.glVertexAttribPointer(colourAttribute, this.colourValuesCount, GL11.GL_FLOAT, false, 0, 0);
        }
        if (this.textureData != null) {
            texturesAttribute = shader.getAttributeLocation("andor_vtextureCoord");
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.texturesHandle);
            GL20.glEnableVertexAttribArray(texturesAttribute);
            GL20.glVertexAttribPointer(texturesAttribute, this.textureValuesCount, GL11.GL_FLOAT, false, 0, 0);
            GL20.glUniform1i(shader.getUniformLocation("andor_texture"), 0);
            if (texture != null)
                GL20.glUniform1f(shader.getUniformLocation("andor_hasTexture"), 1f);
        }
        if (this.drawOrder != null) {
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, this.drawOrderHandle);
            GL11.glDrawElements(this.renderMode, this.drawOrder.length, GL11.GL_UNSIGNED_SHORT, 0);
            GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
        } else {
            //Draw the arrays
            GL11.glDrawArrays(this.renderMode, 0, this.verticesData.length / this.vertexValuesCount);
        }
        //Disable the arrays as needed
        if (this.normalsData != null)
            GL20.glDisableVertexAttribArray(normalAttribute);
        if (this.textureData != null)
            GL20.glDisableVertexAttribArray(texturesAttribute);
        if (this.colourData != null)
            GL20.glDisableVertexAttribArray(colourAttribute);
        GL20.glDisableVertexAttribArray(vertexPositionAttribute);
        //Stop using the shader program
        GL20.glUseProgram(0);
    }
在camera类中,这用于移动玩家视图:

    //Get the rotation
    Vector3D r = this.getRotation();
    //Get the position
    Vector3D p = this.getPosition();
    //Get the scale
    Vector3D s = this.getScale();
    //Rotate by the specified amount
    Matrix.viewMatrix = Matrix.rotate(Matrix.viewMatrix, r.x, 1, 0, 0);
    Matrix.viewMatrix = Matrix.rotate(Matrix.viewMatrix, r.y, 0, 1, 0);
    Matrix.viewMatrix = Matrix.rotate(Matrix.viewMatrix, r.z, 0, 0, 1);

    //Move to the correct position
    Matrix.viewMatrix = Matrix.translate(Matrix.viewMatrix, p);

    //Scale by the correct amount
    Matrix.viewMatrix = Matrix.scale(Matrix.viewMatrix, s);
以下是Matrix4D类:

public class Matrix4D {

    /* The values within this matrix */
    public float[] values;

    /* The default constructor */
    public Matrix4D() {
        //Create the values
        this.values = new float[16];
    }

    /* The constructor with the values given */
    public Matrix4D(float[] values) {
        //Create the values
        this.values = values;
    }

    /* The constructor with the values given */
    public Matrix4D(float[][] values) {
        //Load the values
        load(values);
    }

    /* The method used to set the values given a 2 dimensional array */
    public void load(float[][] values) {
        this.values = new float[] {
                values[0][0], values[0][1], values[0][2], values[0][3],
                values[1][0], values[1][1], values[1][2], values[1][3],
                values[2][0], values[2][1], values[2][2], values[2][3],
                values[3][0], values[3][1], values[3][2], values[3][3]
        };
    }

    /* The method used to get a value using the coordinate within this matrix */
    public float get(int x, int y) {
        //Get the position
        int position = x + (y * 4);
        //Return the value
        return this.values[position];
    }

    /* The method used to return a string representation of this matrix */
    public String toString() {
        //Return the string
        return "[ " + this.values[0] + " " + this.values[1] + " " + + this.values[2] + " " + + this.values[3] + " ]" + "\n" +
               "[ " + this.values[4] + " " + this.values[5] + " " + + this.values[6] + " " + + this.values[7] + " ]" + "\n" +
               "[ " + this.values[8] + " " + this.values[9] + " " + + this.values[10] + " " + + this.values[11] + " ]" + "\n" +
               "[ " + this.values[12] + " " + this.values[13] + " " + + this.values[14] + " " + + this.values[15] + " ]";
    }

    /* The method used to get the values */
    public float[] getValues() { return this.values; }

    /* The method used to get the values in a 2D array */
    public float[][] getValues2DArray() {
        //The array
        float[][] array = new float[4][4];
        //Go through each value
        int column = 0;
        int row = 0;
        while (column * row < array.length) {
            row ++;
            if (row >= 4) {
                column++;
                row = 0;
            }
            array[column][row] = this.values[column * row];
        }
        //Return the array
        return array;
    }

}
公共类矩阵x4d{
/*此矩阵中的值*/
公众价值观;
/*默认构造函数*/
公共矩阵x4d(){
//创建值
this.values=新浮动[16];
}
/*具有给定值的构造函数*/
公共矩阵x4d(浮点[]值){
//创建值
这个值=值;
}
/*具有给定值的构造函数*/
公共矩阵x4d(浮点[][]值){
//加载值
荷载(值);
}
/*用于设置给定二维数组的值的方法*/
公共无效荷载(浮动[][]值){
this.values=new float[]{
值[0][0]、值[0][1]、值[0][2]、值[0][3],
数值[1][0]、数值[1][1]、数值[1][2]、数值[1][3],
数值[2][0]、数值[2][1]、数值[2][2]、数值[2][3],
数值[3][0]、数值[3][1]、数值[3][2]、数值[3][3]
};
}
/*使用此矩阵中的坐标获取值的方法*/
公共浮点get(整数x,整数y){
//获得职位
内部位置=x+(y*4);
//返回值
返回此。值[位置];
}
/*用于返回此矩阵的字符串表示形式的方法*/
公共字符串toString(){
//返回字符串
返回“[”+this.values[0]+“”+this.values[1]+“”+this.values[2]+“”+this.values[3]+”]+“\n”+
“[“+this.values[4]+”“+this.values[5]+”“+this.values[6]+”“+this.values[7]+”]+“\n”+
“[“+this.values[8]+”“+this.values[9]+”“+this.values[10]+”“+this.values[11]+”]+“\n”+
“[“+this.values[12]+”“+this.values[13]+”“+this.values[14]+”“+this.values[15]+”;
}
/*用于获取值的方法*/
public float[]getValues(){返回this.values;}
/*用于获取二维数组中的值的方法*/
公开浮动[][]getValues2DArray(){
//阵列
float[][]数组=新的float[4][4];
//检查每个值
int列=0;
int行=0;
while(列*行=4){
列++;
行=0;
}
数组[列][行]=此.值[列*行];
}
//返回数组
返回数组;
}
}
下面是矩阵类:

package org.andor.core;

public class Matrix {

    /* The different matrices */
    public static Matrix4D modelMatrix = new Matrix4D();
    public static Matrix4D viewMatrix = new Matrix4D();
    public static Matrix4D projectionMatrix = new Matrix4D();
    public static Matrix4D modelViewProjectionMatrix = new Matrix4D();

    /* The static method used to load an identity matrix */
    public static void loadIdentity(Matrix4D matrix) {
        //Load the identity matrix
        matrix.load(new float[][] {
                new float[] { 1, 0, 0, 0 },
                new float[] { 0, 1, 0, 0 },
                new float[] { 0, 0, 1, 0 },
                new float[] { 0, 0, 0, 1 },
        });
    }

    /* The static method used to add two matrices together */
    public static Matrix4D add(Matrix4D matrixA, Matrix4D matrixB) {
        //Create a new matrix
        Matrix4D matrix = new Matrix4D();
        //Go through each value
        for (int a = 0; a < matrix.values.length; a++)
            //Assign the current value
            matrix.values[a] = matrixA.values[a] + matrixB.values[a];
        //Return the matrix
        return matrix;
    }

    /* The static method used to subtract a matrix (B) from another (A) */
    public static Matrix4D subtract(Matrix4D matrixA, Matrix4D matrixB) {
        //Create a new matrix
        Matrix4D matrix = new Matrix4D();
        //Go through each value
        for (int a = 0; a < matrix.values.length; a++)
            //Assign the current value
            matrix.values[a] = matrixB.values[a] - matrixA.values[a];
        //Return the matrix
        return matrix;
    }

    /* The static method used to multiply two matrices together */
    public static Matrix4D multiply(Matrix4D matrixA, Matrix4D matrixB) {
        //Create a new matrix
        Matrix4D matrix = new Matrix4D(new float[][] {
                new float[] {
                        (matrixA.values[0] * matrixB.values[0]) + (matrixA.values[1] * matrixB.values[4]) + (matrixA.values[2] * matrixB.values[8]) + (matrixA.values[3] * matrixB.values[12]),
                        (matrixA.values[0] * matrixB.values[1]) + (matrixA.values[1] * matrixB.values[5]) + (matrixA.values[2] * matrixB.values[9]) + (matrixA.values[3] * matrixB.values[13]),
                        (matrixA.values[0] * matrixB.values[2]) + (matrixA.values[1] * matrixB.values[6]) + (matrixA.values[2] * matrixB.values[10]) + (matrixA.values[3] * matrixB.values[14]),
                        (matrixA.values[0] * matrixB.values[3]) + (matrixA.values[1] * matrixB.values[7]) + (matrixA.values[2] * matrixB.values[11]) + (matrixA.values[3] * matrixB.values[15])
                },
                new float[] {
                        (matrixA.values[4] * matrixB.values[0]) + (matrixA.values[5] * matrixB.values[4]) + (matrixA.values[6] * matrixB.values[8]) + (matrixA.values[7] * matrixB.values[12]),
                        (matrixA.values[4] * matrixB.values[1]) + (matrixA.values[5] * matrixB.values[5]) + (matrixA.values[6] * matrixB.values[9]) + (matrixA.values[7] * matrixB.values[13]),
                        (matrixA.values[4] * matrixB.values[2]) + (matrixA.values[5] * matrixB.values[6]) + (matrixA.values[6] * matrixB.values[10]) + (matrixA.values[7] * matrixB.values[14]),
                        (matrixA.values[4] * matrixB.values[3]) + (matrixA.values[5] * matrixB.values[7]) + (matrixA.values[6] * matrixB.values[11]) + (matrixA.values[7] * matrixB.values[15])
                },
                new float[] {
                        (matrixA.values[8] * matrixB.values[0]) + (matrixA.values[9] * matrixB.values[4]) + (matrixA.values[10] * matrixB.values[8]) + (matrixA.values[11] * matrixB.values[12]),
                        (matrixA.values[8] * matrixB.values[1]) + (matrixA.values[9] * matrixB.values[5]) + (matrixA.values[10] * matrixB.values[9]) + (matrixA.values[11] * matrixB.values[13]),
                        (matrixA.values[8] * matrixB.values[2]) + (matrixA.values[9] * matrixB.values[6]) + (matrixA.values[10] * matrixB.values[10]) + (matrixA.values[11] * matrixB.values[14]),
                        (matrixA.values[8] * matrixB.values[3]) + (matrixA.values[9] * matrixB.values[7]) + (matrixA.values[10] * matrixB.values[11]) + (matrixA.values[11] * matrixB.values[15])
                },
                new float[] {
                        (matrixA.values[12] * matrixB.values[0]) + (matrixA.values[13] * matrixB.values[4]) + (matrixA.values[14] * matrixB.values[8]) + (matrixA.values[15] * matrixB.values[12]),
                        (matrixA.values[12] * matrixB.values[1]) + (matrixA.values[13] * matrixB.values[5]) + (matrixA.values[14] * matrixB.values[9]) + (matrixA.values[15] * matrixB.values[13]),
                        (matrixA.values[12] * matrixB.values[2]) + (matrixA.values[13] * matrixB.values[6]) + (matrixA.values[14] * matrixB.values[10]) + (matrixA.values[15] * matrixB.values[14]),
                        (matrixA.values[12] * matrixB.values[3]) + (matrixA.values[13] * matrixB.values[7]) + (matrixA.values[14] * matrixB.values[11]) + (matrixA.values[15] * matrixB.values[15])
                }
        });
        //Return the matrix
        return matrix;
    }

    /* The static method used to transpose a matrix */
    public static Matrix4D transpose(Matrix4D matrix) {
        //Get the values from the matrix
        float[][] values = matrix.getValues2DArray();
        //The new values
        float[][] newValues = new float[4][4];
        //Go through the array
        for (int y = 0; y < values.length; y++) {
            for (int x = 0; x < values[y].length; x++) {
                //Assign the new value
                newValues[x][y] = values[y][x];
            }
        }
        //Return the matrix
        return new Matrix4D(newValues);
    }

    /* The static method used to translate a matrix */
    public static Matrix4D translate(Matrix4D matrix, Vector3D vector) {
        //The transform matrix
        Matrix4D transform = new Matrix4D(new float[][] {
                new float[] { 1, 0, 0, vector.x },
                new float[] { 0, 1, 0, vector.y },
                new float[] { 0, 0, 1, vector.z },
                new float[] { 0, 0, 0, 1 },
        });
        //Add onto the matrix and return the result
        return multiply(matrix, transform);
    }

    /* The static method used to rotate a matrix */
    public static Matrix4D rotate(Matrix4D matrix, float angle, int x, int y, int z) {
        //The transform matrix
        Matrix4D transform = new Matrix4D();
        //Calculate the values needed
        float cos = (float) Math.cos(angle);
        float sin = (float) Math.sin(angle);
        //Check the x y and z values
        if (x == 1) {
            transform.load(new float[][] {
                    new float[] { 1, 0, 0, 0 },
                    new float[] { 0, cos, -sin, 0 },
                    new float[] { 0, sin, cos, 0 },
                    new float[] { 0, 0, 0, 1 },
            });
        } else if (y == 1) {
            transform.load(new float[][] {
                    new float[] { cos, 0, sin, 0 },
                    new float[] { 0, 1, 0, 0 },
                    new float[] { -sin, 0, cos, 0 },
                    new float[] { 0, 0, 0, 1 },
            });
        } else if (z == 1) {
            transform.load(new float[][] {
                    new float[] { cos, -sin, 0, 0 },
                    new float[] { sin, cos, 0, 0 },
                    new float[] { 0, 0, 1, 0 },
                    new float[] { 0, 0, 0, 1 },
            });
        }
        //Add onto the matrix and return the result
        return multiply(matrix, transform);
    }

    /* The static method used to scale a matrix */
    public static Matrix4D scale(Matrix4D matrix, Vector3D vector) {
        //The transform matrix
        Matrix4D transform = new Matrix4D(new float[][] {
                new float[] { vector.x, 0, 0, 0 },
                new float[] { 0, vector.y, 0, 0 },
                new float[] { 0, 0, vector.z, 0 },
                new float[] { 0, 0, 0, 1 },
        });
        //Add onto the matrix and return the result
        return multiply(matrix, transform);
    }

    /* The static method used to return an orthographic projection matrix */
    public static Matrix4D ortho(float left, float right, float bottom, float top, float zfar, float znear) {
//      Matrix4D mat = new Matrix4D();
//      mat.values[0] = 2 / (right - left);
//      mat.values[5] = 2 / (top - bottom);
//      mat.values[10] = - 2 / (zfar - znear);
//      mat.values[12] = - (right + left) / (right - left);
//      mat.values[13] = -(top + bottom) / (top - bottom);
//      mat.values[14] = -(zfar + znear) / (zfar - znear);
//      return mat;
        return new Matrix4D(new float[][] {
                new float[] { 2 / (right - left), 0, 0, -((right + left) / (right - left)) },
                new float[] { 0, 2 / (top - bottom), 0, -((top + bottom) / (top - bottom)) },
                new float[] { 0, 0, -2 / (zfar - znear), -((zfar + znear) / (zfar - znear)) },
                new float[] { 0, 0, 0, 1 },
        });
    }

    /* The static method used to return a perspective projection matrix */
    public static Matrix4D perspective(float fov, float aspect, float zNear, float zFar) {
        //Calculate the values that need to be calculated the most frequently
        float f = 1.0f / (float) Math.tan(fov / 2 * (Math.PI / 360.0));
        float rangeReciprocal = 1.0f / (zNear - zFar);

        Matrix4D matrix = new Matrix4D();

        //Set the matrix values
        matrix.values[0] = f / aspect;
        matrix.values[1] = 0.0f;
        matrix.values[2] = 0.0f;
        matrix.values[3] = 0.0f;

        matrix.values[4] = 0.0f;
        matrix.values[5] = f;
        matrix.values[6] = 0.0f;
        matrix.values[7] = 0.0f;

        matrix.values[8] = 0.0f;
        matrix.values[9] = 0.0f;
        matrix.values[10] = (zFar + zNear) * rangeReciprocal;
        matrix.values[11] = -1.0f;

        matrix.values[12] = 0.0f;
        matrix.values[13] = 0.0f;
        matrix.values[14] = 2.0f * zFar * zNear * rangeReciprocal;
        matrix.values[15] = 0.0f;
        return matrix;
    }
}
package org.andor.core;
公共类矩阵{
/*不同的矩阵*/
公共静态矩阵x4d modelMatrix=新矩阵x4d();
public static Matrix4D viewMatrix=new Matrix4D();
公共静态Matrix4D projectionMatrix=新Matrix4D();
公共静态Matrix4D modelViewProjectionMatrix=新Matrix4D();
/*用于加载单位矩阵的静态方法*/
公共静态void loadIdentity(矩阵x4d矩阵){
//加载单位矩阵
矩阵加载(新浮点[][]{
新浮点[]{1,0,0,0},
新浮点[]{0,1,0,0},
新浮点[]{0,0,1,0},
新浮点[]{0,0,0,1},
});
}
/*用于将两个矩阵相加的静态方法*/
公共静态Matrix4D添加(Matrix4D matrixA,Matrix4D matrixB){
//创建一个新的矩阵
Matrix4D矩阵=新的Matrix4D();
//检查每个值
对于(int a=0;apackage org.andor.core;

public class Matrix {

    /* The different matrices */
    public static Matrix4D modelMatrix = new Matrix4D();
    public static Matrix4D viewMatrix = new Matrix4D();
    public static Matrix4D projectionMatrix = new Matrix4D();
    public static Matrix4D modelViewProjectionMatrix = new Matrix4D();

    /* The static method used to load an identity matrix */
    public static void loadIdentity(Matrix4D matrix) {
        //Load the identity matrix
        matrix.load(new float[][] {
                new float[] { 1, 0, 0, 0 },
                new float[] { 0, 1, 0, 0 },
                new float[] { 0, 0, 1, 0 },
                new float[] { 0, 0, 0, 1 },
        });
    }

    /* The static method used to add two matrices together */
    public static Matrix4D add(Matrix4D matrixA, Matrix4D matrixB) {
        //Create a new matrix
        Matrix4D matrix = new Matrix4D();
        //Go through each value
        for (int a = 0; a < matrix.values.length; a++)
            //Assign the current value
            matrix.values[a] = matrixA.values[a] + matrixB.values[a];
        //Return the matrix
        return matrix;
    }

    /* The static method used to subtract a matrix (B) from another (A) */
    public static Matrix4D subtract(Matrix4D matrixA, Matrix4D matrixB) {
        //Create a new matrix
        Matrix4D matrix = new Matrix4D();
        //Go through each value
        for (int a = 0; a < matrix.values.length; a++)
            //Assign the current value
            matrix.values[a] = matrixB.values[a] - matrixA.values[a];
        //Return the matrix
        return matrix;
    }

    /* The static method used to multiply two matrices together */
    public static Matrix4D multiply(Matrix4D matrixA, Matrix4D matrixB) {
        //Create a new matrix
        Matrix4D matrix = new Matrix4D(new float[][] {
                new float[] {
                        (matrixA.values[0] * matrixB.values[0]) + (matrixA.values[1] * matrixB.values[4]) + (matrixA.values[2] * matrixB.values[8]) + (matrixA.values[3] * matrixB.values[12]),
                        (matrixA.values[0] * matrixB.values[1]) + (matrixA.values[1] * matrixB.values[5]) + (matrixA.values[2] * matrixB.values[9]) + (matrixA.values[3] * matrixB.values[13]),
                        (matrixA.values[0] * matrixB.values[2]) + (matrixA.values[1] * matrixB.values[6]) + (matrixA.values[2] * matrixB.values[10]) + (matrixA.values[3] * matrixB.values[14]),
                        (matrixA.values[0] * matrixB.values[3]) + (matrixA.values[1] * matrixB.values[7]) + (matrixA.values[2] * matrixB.values[11]) + (matrixA.values[3] * matrixB.values[15])
                },
                new float[] {
                        (matrixA.values[4] * matrixB.values[0]) + (matrixA.values[5] * matrixB.values[4]) + (matrixA.values[6] * matrixB.values[8]) + (matrixA.values[7] * matrixB.values[12]),
                        (matrixA.values[4] * matrixB.values[1]) + (matrixA.values[5] * matrixB.values[5]) + (matrixA.values[6] * matrixB.values[9]) + (matrixA.values[7] * matrixB.values[13]),
                        (matrixA.values[4] * matrixB.values[2]) + (matrixA.values[5] * matrixB.values[6]) + (matrixA.values[6] * matrixB.values[10]) + (matrixA.values[7] * matrixB.values[14]),
                        (matrixA.values[4] * matrixB.values[3]) + (matrixA.values[5] * matrixB.values[7]) + (matrixA.values[6] * matrixB.values[11]) + (matrixA.values[7] * matrixB.values[15])
                },
                new float[] {
                        (matrixA.values[8] * matrixB.values[0]) + (matrixA.values[9] * matrixB.values[4]) + (matrixA.values[10] * matrixB.values[8]) + (matrixA.values[11] * matrixB.values[12]),
                        (matrixA.values[8] * matrixB.values[1]) + (matrixA.values[9] * matrixB.values[5]) + (matrixA.values[10] * matrixB.values[9]) + (matrixA.values[11] * matrixB.values[13]),
                        (matrixA.values[8] * matrixB.values[2]) + (matrixA.values[9] * matrixB.values[6]) + (matrixA.values[10] * matrixB.values[10]) + (matrixA.values[11] * matrixB.values[14]),
                        (matrixA.values[8] * matrixB.values[3]) + (matrixA.values[9] * matrixB.values[7]) + (matrixA.values[10] * matrixB.values[11]) + (matrixA.values[11] * matrixB.values[15])
                },
                new float[] {
                        (matrixA.values[12] * matrixB.values[0]) + (matrixA.values[13] * matrixB.values[4]) + (matrixA.values[14] * matrixB.values[8]) + (matrixA.values[15] * matrixB.values[12]),
                        (matrixA.values[12] * matrixB.values[1]) + (matrixA.values[13] * matrixB.values[5]) + (matrixA.values[14] * matrixB.values[9]) + (matrixA.values[15] * matrixB.values[13]),
                        (matrixA.values[12] * matrixB.values[2]) + (matrixA.values[13] * matrixB.values[6]) + (matrixA.values[14] * matrixB.values[10]) + (matrixA.values[15] * matrixB.values[14]),
                        (matrixA.values[12] * matrixB.values[3]) + (matrixA.values[13] * matrixB.values[7]) + (matrixA.values[14] * matrixB.values[11]) + (matrixA.values[15] * matrixB.values[15])
                }
        });
        //Return the matrix
        return matrix;
    }

    /* The static method used to transpose a matrix */
    public static Matrix4D transpose(Matrix4D matrix) {
        //Get the values from the matrix
        float[][] values = matrix.getValues2DArray();
        //The new values
        float[][] newValues = new float[4][4];
        //Go through the array
        for (int y = 0; y < values.length; y++) {
            for (int x = 0; x < values[y].length; x++) {
                //Assign the new value
                newValues[x][y] = values[y][x];
            }
        }
        //Return the matrix
        return new Matrix4D(newValues);
    }

    /* The static method used to translate a matrix */
    public static Matrix4D translate(Matrix4D matrix, Vector3D vector) {
        //The transform matrix
        Matrix4D transform = new Matrix4D(new float[][] {
                new float[] { 1, 0, 0, vector.x },
                new float[] { 0, 1, 0, vector.y },
                new float[] { 0, 0, 1, vector.z },
                new float[] { 0, 0, 0, 1 },
        });
        //Add onto the matrix and return the result
        return multiply(matrix, transform);
    }

    /* The static method used to rotate a matrix */
    public static Matrix4D rotate(Matrix4D matrix, float angle, int x, int y, int z) {
        //The transform matrix
        Matrix4D transform = new Matrix4D();
        //Calculate the values needed
        float cos = (float) Math.cos(angle);
        float sin = (float) Math.sin(angle);
        //Check the x y and z values
        if (x == 1) {
            transform.load(new float[][] {
                    new float[] { 1, 0, 0, 0 },
                    new float[] { 0, cos, -sin, 0 },
                    new float[] { 0, sin, cos, 0 },
                    new float[] { 0, 0, 0, 1 },
            });
        } else if (y == 1) {
            transform.load(new float[][] {
                    new float[] { cos, 0, sin, 0 },
                    new float[] { 0, 1, 0, 0 },
                    new float[] { -sin, 0, cos, 0 },
                    new float[] { 0, 0, 0, 1 },
            });
        } else if (z == 1) {
            transform.load(new float[][] {
                    new float[] { cos, -sin, 0, 0 },
                    new float[] { sin, cos, 0, 0 },
                    new float[] { 0, 0, 1, 0 },
                    new float[] { 0, 0, 0, 1 },
            });
        }
        //Add onto the matrix and return the result
        return multiply(matrix, transform);
    }

    /* The static method used to scale a matrix */
    public static Matrix4D scale(Matrix4D matrix, Vector3D vector) {
        //The transform matrix
        Matrix4D transform = new Matrix4D(new float[][] {
                new float[] { vector.x, 0, 0, 0 },
                new float[] { 0, vector.y, 0, 0 },
                new float[] { 0, 0, vector.z, 0 },
                new float[] { 0, 0, 0, 1 },
        });
        //Add onto the matrix and return the result
        return multiply(matrix, transform);
    }

    /* The static method used to return an orthographic projection matrix */
    public static Matrix4D ortho(float left, float right, float bottom, float top, float zfar, float znear) {
//      Matrix4D mat = new Matrix4D();
//      mat.values[0] = 2 / (right - left);
//      mat.values[5] = 2 / (top - bottom);
//      mat.values[10] = - 2 / (zfar - znear);
//      mat.values[12] = - (right + left) / (right - left);
//      mat.values[13] = -(top + bottom) / (top - bottom);
//      mat.values[14] = -(zfar + znear) / (zfar - znear);
//      return mat;
        return new Matrix4D(new float[][] {
                new float[] { 2 / (right - left), 0, 0, -((right + left) / (right - left)) },
                new float[] { 0, 2 / (top - bottom), 0, -((top + bottom) / (top - bottom)) },
                new float[] { 0, 0, -2 / (zfar - znear), -((zfar + znear) / (zfar - znear)) },
                new float[] { 0, 0, 0, 1 },
        });
    }

    /* The static method used to return a perspective projection matrix */
    public static Matrix4D perspective(float fov, float aspect, float zNear, float zFar) {
        //Calculate the values that need to be calculated the most frequently
        float f = 1.0f / (float) Math.tan(fov / 2 * (Math.PI / 360.0));
        float rangeReciprocal = 1.0f / (zNear - zFar);

        Matrix4D matrix = new Matrix4D();

        //Set the matrix values
        matrix.values[0] = f / aspect;
        matrix.values[1] = 0.0f;
        matrix.values[2] = 0.0f;
        matrix.values[3] = 0.0f;

        matrix.values[4] = 0.0f;
        matrix.values[5] = f;
        matrix.values[6] = 0.0f;
        matrix.values[7] = 0.0f;

        matrix.values[8] = 0.0f;
        matrix.values[9] = 0.0f;
        matrix.values[10] = (zFar + zNear) * rangeReciprocal;
        matrix.values[11] = -1.0f;

        matrix.values[12] = 0.0f;
        matrix.values[13] = 0.0f;
        matrix.values[14] = 2.0f * zFar * zNear * rangeReciprocal;
        matrix.values[15] = 0.0f;
        return matrix;
    }
}