OpenGL矩阵奇怪的转换

时间:2014-10-11 10:16:22

标签: java opengl matrix lwjgl

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

首先,这是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);
    }

然后,这里是用于在渲染之前将翻译应用于场景中的对象的方法:

/* The method used to update the current view matrix */
public void updateViewMatrix() {
    //Get the position
    Vector3D p = this.getPosition();
    //Get the rotation
    Vector3D r = this.getRotation();
    //Get the scale
    Vector3D s = this.getScale();
    //Save the current matrix
    clone = Arrays.copyOf(Matrix.modelMatrix.getValues(), 16);
    //Scale by the specified amount
    Matrix.modelMatrix = Matrix.scale(Matrix.modelMatrix, s);
    //Rotate by the specified amount
    Matrix.modelMatrix = Matrix.rotate(Matrix.modelMatrix, r.x, 1, 0, 0);
    Matrix.modelMatrix = Matrix.rotate(Matrix.modelMatrix, r.y, 0, 1, 0);
    Matrix.modelMatrix = Matrix.rotate(Matrix.modelMatrix, r.z, 0, 0, 1);
    //Move to the correct position
    Matrix.modelMatrix = Matrix.translate(Matrix.modelMatrix, p);
}

/* The method used to restore the current view matrix */
public void restoreViewMatrix() {
    //Restore the current matrix
    Matrix.modelMatrix.values = clone;
}

在相机类中,这用于移动玩家视图:

    //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;
    }

}

这是Matrix类:

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;
    }
}

这是当前输出的样子:

enter image description here

编辑:我已经尝试在将矩阵提供给着色器之前更改乘法,而不是使用矩阵乘法作为实验。这导致2D看起来正常运行,但旋转似乎使对象变小,然后翻转然后它会再次变大但此时它会被反转。

1 个答案:

答案 0 :(得分:1)

有两件事使这种效果发生。

  1. 在着色器中使用矩阵之前需要转置矩阵
  2. 除非znear值为1或大于
  3. ,否则透视矩阵不起作用