Android OpenGL ES 2.0旋转PNG是偏斜的

时间:2015-08-12 13:37:13

标签: android opengl-es rotation opengl-es-2.0

我试着做一些"简单"试验。 我遇到了很多困难,但我找不到其中一个的解决方案。

我的测试是:

  1. 绘制旋转方格(由2个三角形组成)
  2. 绘制旋转纹理(png加载文件)
  3. 问题是绘制的png在旋转时是倾斜的,而不是正方形。

    0 degree

    90 degrees

    正如您所看到的,squre通过旋转保持相同的尺寸,而尺度调整大小",就像我希望它始终适合屏幕一样。

    下面,您可以找到我的一些代码:

    我使用的投影矩阵

    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        this.screenWidth = width;
        this.screenHeight = height;
        float ratio = (float) width / height;
    
        // this projection matrix is applied to object coordinates
        // in the onDrawFrame() method
        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    }
    

    视图矩阵和旋转矩阵

        public void onDrawFrame(GL10 unused) {
    
            // Rotation matrix
            float[] scratch = new float[16];
            // Same rotation matrix except the angle is the opposite
            // Because of the way the PNG is loaded ?
            float[] scratch_ = new float[16];
    
            // Redraw background color
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    
            // Set the camera position (View matrix)
            Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    
            // Calculate the projection and view transformation
            Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
    
            // Create a rotation transformation for the triangle
            float angle = 90;
            Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, 1.0f);
            Matrix.setRotateM(mRotationMatrix_, 0, -angle, 0, 0, 1.0f);
    
            // Combine the rotation matrix with the projection and camera view
            // Note that the mMVPMatrix factor *must be first* in order
            // for the matrix multiplication product to be correct.
            Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
            Matrix.multiplyMM(scratch_, 0, mMVPMatrix, 0, mRotationMatrix_, 0);
    
            //draw square and scale
            draw(scratch, scratch_)
        }
    

    我元素的坐标

        float squareCoords[] = {
                -0.5f,  0.5f, 0.0f,   // top left
                -0.5f, -0.5f, 0.0f,   // bottom left
                0.5f, -0.5f, 0.0f,   // bottom right
                0.5f,  0.5f, 0.0f }; // top right
    
        float scaleCoords[] = {
                -0.2f,  1.5f,   // top left
                -0.2f, -0.5f,   // bottom left
                0.2f, -0.5f,   // bottom right
                0.2f,  1.5f}; // top right
    

    最后,您可以在下面找到我的"图像"我用来绘制PNG的类

    public class Image {
        //Reference to Activity Context
        private final Context mActivityContext;
    
        //Added for Textures
        private final FloatBuffer mCubeTextureCoordinates;
        private int mTextureUniformHandle;
        private int mTextureCoordinateHandle;
        private final int mTextureCoordinateDataSize = 2;
        private int mTextureDataHandle;
    
    
        private final String vertexShaderCode =
                "attribute vec2 a_TexCoordinate;" +
                        "varying vec2 v_TexCoordinate;" +
                        "uniform mat4 uMVPMatrix;" +
                        "attribute vec4 vPosition;" +
                        "void main() {" +
                        "  gl_Position = vPosition * uMVPMatrix;" +
                        //Test
                        "v_TexCoordinate = a_TexCoordinate;" +
                        //End Test
                        "}";
    
        private final String fragmentShaderCode =
                "precision mediump float;" +
                        "uniform sampler2D u_Texture;" +
                        "varying vec2 v_TexCoordinate;" +
                        "void main() {" +
                        "gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" +
                        "}";
    
        private final int shaderProgram;
        private final FloatBuffer vertexBuffer;
        private final ShortBuffer drawListBuffer;
        private int mPositionHandle;
        private int mMVPMatrixHandle;
    
        // number of coordinates per vertex in this array
        static final int COORDS_PER_VERTEX = 2;
    
        private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; //Order to draw vertices
        private final int vertexStride = COORDS_PER_VERTEX * 4; //Bytes per vertex
    
        public Image(final Context activityContext, float[] coords)
        {
            mActivityContext = activityContext;
    
            //Initialize Vertex Byte Buffer for Shape Coordinates / # of coordinate values * 4 bytes per float
            ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * 4);
            //Use the Device's Native Byte Order
            bb.order(ByteOrder.nativeOrder());
            //Create a floating point buffer from the ByteBuffer
            vertexBuffer = bb.asFloatBuffer();
            //Add the coordinates to the FloatBuffer
            vertexBuffer.put(coords);
            //Set the Buffer to Read the first coordinate
            vertexBuffer.position(0);
    
            // U, V coordinates
            final float[] cubeTextureCoordinateData =
                    {
                            0.0f,  0.0f,
                            0.0f, 1.0f,
                            1.0f, 1.0f,
                            1.0f,  0.0f
                    };
    
            mCubeTextureCoordinates = ByteBuffer.allocateDirect(cubeTextureCoordinateData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
            mCubeTextureCoordinates.put(cubeTextureCoordinateData).position(0);
    
            //Initialize byte buffer for the draw list
            ByteBuffer dlb = ByteBuffer.allocateDirect(coords.length * 2);
            dlb.order(ByteOrder.nativeOrder());
            drawListBuffer = dlb.asShortBuffer();
            drawListBuffer.put(drawOrder);
            drawListBuffer.position(0);
    
            int vertexShader = PfdRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
            int fragmentShader = PfdRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
    
            shaderProgram = GLES20.glCreateProgram();
            GLES20.glAttachShader(shaderProgram, vertexShader);
            GLES20.glAttachShader(shaderProgram, fragmentShader);
    
            //Texture Code
            GLES20.glBindAttribLocation(shaderProgram, 0, "a_TexCoordinate");
    
            GLES20.glLinkProgram(shaderProgram);
    
            //Load the texture
            // Retrieve our image from resources.
            int id = mActivityContext.getResources().getIdentifier("drawable/coeur_hsi", "drawable",
                    mActivityContext.getPackageName());
            Log.d("Id of coeur_hsi is: ", Integer.toString(id));
            mTextureDataHandle = loadTexture(mActivityContext, id);
        }
    
        public void draw(float[] mvpMatrix)
        {
            //Add program to OpenGL ES Environment
            GLES20.glUseProgram(shaderProgram);
    
            //Get handle to vertex shader's vPosition member
            mPositionHandle = GLES20.glGetAttribLocation(shaderProgram, "vPosition");
    
            //Enable a handle to the triangle vertices
            GLES20.glEnableVertexAttribArray(mPositionHandle);
    
            //Prepare the triangle coordinate data
            GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
    
            //Set Texture Handles and bind Texture
            mTextureUniformHandle = GLES20.glGetAttribLocation(shaderProgram, "u_Texture");
            mTextureCoordinateHandle = GLES20.glGetAttribLocation(shaderProgram, "a_TexCoordinate");
    
            //Set the active texture unit to texture unit 0.
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    
            //Bind the texture to this unit.
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
    
            //Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
            GLES20.glUniform1i(mTextureUniformHandle, 0);
    
            //Pass in the texture coordinate information
            mCubeTextureCoordinates.position(0);
            GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize,
                    GLES20.GL_FLOAT, false, 0, mCubeTextureCoordinates);
            GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
    
            //Get Handle to Shape's Transformation Matrix
            mMVPMatrixHandle = GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix");
    
            //Apply the projection and view transformation
            GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
    
            //Draw the triangle
            GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
    
            //Disable Vertex Array
            GLES20.glDisableVertexAttribArray(mPositionHandle);
        }
    
        public static int loadTexture(final Context context, final int resourceId)
        {
            final int[] textureHandle = new int[1];
    
            GLES20.glGenTextures(1, textureHandle, 0);
    
            if (textureHandle[0] != 0)
            {
                final BitmapFactory.Options options = new BitmapFactory.Options();
                options.inScaled = false;   // No pre-scaling
    
                // Read in the resource
                final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);
    
                // Bind to the texture in OpenGL
                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
    
                // Set filtering
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
    
                // Load the bitmap into the bound texture.
                GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    
                // Recycle the bitmap, since its data has been loaded into OpenGL.
                bitmap.recycle();
            }
    
            if (textureHandle[0] == 0)
            {
                throw new RuntimeException("Error loading texture.");
            }
    
            return textureHandle[0];
        }
    }
    

    我的猜测是错误来自投影矩阵,但我无法弄清问题到底是什么。 你能告诉我为什么我的PNG图像会歪曲吗?

1 个答案:

答案 0 :(得分:3)

在我看来你错误地计算了你的gl_Position。

  

“gl_Position = vPosition * uMVPMatrix;”

当您查看矩阵乘法时,您必须知道当乘以两个矩阵(顶点也是1x4矩阵)时,您将第一个矩阵的每一行与第二个矩阵的每一列相乘。 这仅在矩阵“兼容”时有效。这意味着第一个矩阵必须具有与第二个矩阵具有行完全相同的列数。例如。您可以将3x2矩阵与2x3矩阵相乘,但不能将2x3矩阵与3x2矩阵相乘。

因此订单非常重要!

尝试将其计算为

gl_Position = uMVPMatrix * vPosition;

Wiki有一个关于矩阵乘法的好页面。 https://en.wikipedia.org/wiki/Matrix_multiplication