简体   繁体   中英

Animating Multiple sprites in Android OpenGL ES 2.0

I've spent days searching, trying tutorials, and not actually getting results in this, so here I am.

I'm trying, simply put, to animate a collection of objects (Android Studio) on the screen, in a 2D format, with each independent movements and rotations. However, when I try this, I'm either not getting the object rendered, or its rendering skewed (as if rotated through the vertical Y-axis)

I know the importance of the order in which objects are drawn too (to give correct Z-ordering appearance) however, I'm at a bit of a loss with the matrix manipulation.

Here is what I have so far:


Main Activity - standard stuff

    private GLSurfaceView mGLSurfaceView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mGLSurfaceView = new GLSurfaceView(this);

    //check if device supports ES 2.0
    final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
    final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;

    if (supportsEs2) {
        //Get the ES2 compatible context
        mGLSurfaceView.setEGLContextClientVersion(2);
        //set renderer to my renderer below
        mGLSurfaceView.setRenderer(new MyGL20Renderer(this));

    } else {
        //no support
        return;

    }


    //setContentView(R.layout.activity_main);
    setContentView(mGLSurfaceView);
}

GL20Renderer class - Notice I'm now just manually adding 2 objects to my collection to render

public class MyGL20Renderer implements GLSurfaceView.Renderer
{
    private final Context mActivityContext;

    //Matrix Initializations
    private final float[] mMVPMatrix = new float[16];
    private final float[] mProjMatrix = new float[16];
    private final float[] mVMatrix = new float[16];
    private float[] mRotationMatrix = new float[16];

    private final float[] mRotateMatrix = new float[16];
    private final float[] mMoveMatrix = new float[16];
    private final float[] mTempMatrix = new float[16];

    private final float[] mModelMatrix = new float[16];

    private int                 numObjects = 2;
    private ArrayList<Sprite>   spriteList = new ArrayList<Sprite>();


    //Declare as volatile because we are updating it from another thread
    public volatile float mAngle;

    //private Triangle triangle;
    //private Sprite sprite;

    public MyGL20Renderer(final Context activityContext)
    {
        mActivityContext = activityContext;

    }

    public void onSurfaceCreated(GL10 unused, EGLConfig config)
    {
        //Set the background frame color
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        //Set the camera position (View Matrix) //mtx, offset, eyex,y,z, centrex,y,z, upx,y,z
        Matrix.setLookAtM(mVMatrix, 0,
                0, 0, -1.5f,        //Eye XYZ - position eye behind the origin
                0f, 0f, -5.0f,         //Look XYZ - We are looking toward the distance
                0f, 1.0f, 0.0f);    //Up XYZ - Up vector - where head would be pointing if holding the camera

        //Initialize Shapes
        //triangle = new Triangle();
        //sprite = new Sprite(mActivityContext);

        //Sprite newSprite;

        float xMax = 2.0f;
        float yMax = 2.0f;

        //rand = 0->1
        float newX = (new Random().nextFloat() * xMax * 2) - xMax; //2.0f; //-2 -> +2
        float newY = (new Random().nextFloat() * yMax * 2) - yMax; //-3 -> +3
        float newZ = 0f;



        //for (int i=0; i<numObjects; i++) {
            //newSprite = new Sprite(mActivityContext);
            //spriteList.add(new Sprite(mActivityContext, newX, newY, newZ));
        //}

        spriteList.add(new Sprite(mActivityContext, -0.0f, -0.0f, 0.0f));
        spriteList.add(new Sprite(mActivityContext, +0.5f, -0.5f, 0.0f));
        //spriteList.add(new Sprite(mActivityContext, -1.0f, +1.0f, 0.0f));
        //spriteList.add(new Sprite(mActivityContext, +1.0f, +1.0f, 0.0f));


    }

    public void onDrawFrame(GL10 unused)
    {
        //init
        Sprite currSprite;

        //Redraw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        //timing
        float jFactor = 0.1f;
        long time = SystemClock.uptimeMillis() % 10000L;
        float angleInDegrees = (360.0f / 1000.0f) * ((int) time) * jFactor;




/*
        //number 1
        //Matrix.setIdentityM(mModelMatrix, 0);
        //currSprite = spriteList.get(0);
        //Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
        //currSprite.Draw(mModelMatrix);

        //number 2
        Matrix.setIdentityM(mModelMatrix, 0);
        currSprite = spriteList.get(1);
        Matrix.translateM(mModelMatrix, 0, 0.0f, -0.1f, 0.0f);
        //Matrix.rotateM(mModelMatrix, 0, 90.0f, 1.0f, 0.0f, 0.0f);
        Matrix.rotateM(mModelMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
        currSprite.Draw(mModelMatrix);

        //Matrix.translateM(mModelMatrix, 0, 0, 0, 4.0f);
    */


        //Calculate the projection and view transformation
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);

        //zoom out a bit?
        Matrix.translateM(mMVPMatrix, 0, 0, 0, 4.0f);

        //number 1
        //currSprite = spriteList.get(0);
        //Matrix.setIdentityM(mMVPMatrix, 0);
        //Matrix.rotateM(mMVPMatrix, 0, angleInDegrees, 0.0f, 0.0f, 1.0f);
        //Matrix.translateM(mMVPMatrix, 0, currSprite.coordX, 0.0f, 0.0f);
        //currSprite.coordX += 0.01f;
        //currSprite.Draw(mMVPMatrix);

        //number 2
        currSprite = spriteList.get(0);

        Matrix.setIdentityM(mMVPMatrix, 0);
        Matrix.translateM(mMVPMatrix, 0, 0.0f, 0.0f, 0.0f);

        Matrix.rotateM(mMVPMatrix, 0, angleInDegrees, 0.0f, 0.0f, +1.0f);

        //float[] mTempMatrix = new float[16];
        //mTempMatrix = mModelMatrix.clone();

        //Matrix.multiplyMM(mMVPMatrix, 0, mMVPMatrix, 0, mRotateMatrix, 0);

        //mTempMatrix = mMVPMatrix.clone();
        //Matrix.multiplyMM(mMVPMatrix, 0, mTempMatrix, 0, mModelMatrix, 0);

        //Matrix.setIdentityM(mMVPMatrix, 0);

        currSprite.Draw(mMVPMatrix);

        /*


        //Set the camera position (View Matrix) //mtx, offset, eyex,y,z, centrex,y,z, upx,y,z
        Matrix.setLookAtM(mVMatrix, 0,
                    0, 0, -10,
                    0f, 0f, 0f,
                    0f, 1.0f, 0.0f);


        //Calculate the projection and view transformation
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);

        //zoom out a bit?
        Matrix.translateM(mMVPMatrix, 0, 0, 0, 4.0f);


        for (int i=0; i<numObjects; i++) {

        //Create a rotation transformation for the triangle
        //Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);
        Matrix.setRotateM(mRotationMatrix, 0, 0, 0, 0, -1.0f); //-1.0 = Z, for some reason need this. Grr


        //Combine the rotation matrix with the projection and camera view
        Matrix.multiplyMM(mMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);

        //Draw Shape
        //triangle.Draw(mMVPMatrix);
        //sprite.Draw(mMVPMatrix);


            currSprite = spriteList.get(i);
        //Move the object to the passed initial coordinates?
            //Matrix.translateM(mMVPMatrix, 0, currSprite.coordX, currSprite.coordY, currSprite.coordZ);


            currSprite.Draw(mMVPMatrix);
        }
        */

    }

    public void onSurfaceChanged(GL10 unused, int width, int height)
    {
        GLES20.glViewport(0, 0, width, height);
        if (height == 0) {
            height = 1; //incase of div 0 errors
        }

        float ratio = (float) width / height;

        final float left = -ratio;
        final float right = ratio;
        final float bottom = -1.0f;
        final float top = 1.0f;
        final float near = 1.0f;
        final float far = 10.f;


        //This Projection Matrix is applied to object coordinates in the onDrawFrame() method
        //Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
        Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);

    }

    public static int loadShader(int type, String shaderCode)
    {
        //Create a Vertex Shader Type Or a Fragment Shader Type (GLES20.GL_VERTEX_SHADER OR GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        //Add The Source Code and Compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }
}

Please excuse the commented code in OnDrawFrame() where I've been experimenting, and failing.


Sprite Class

public class Sprite
{
    //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 =
//Test
            "attribute vec2 a_TexCoordinate;" +
                    "varying vec2 v_TexCoordinate;" +
//End Test
                    "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 vec4 vColor;" +
//Test
                    "uniform sampler2D u_Texture;" +
                    "varying vec2 v_TexCoordinate;" +
//End Test
                    "void main() {" +
//"gl_FragColor = vColor;" +
                    "gl_FragColor = (vColor * texture2D(u_Texture, v_TexCoordinate));" +
                    "}";

    private final int shaderProgram;
    private final FloatBuffer vertexBuffer;
    private final ShortBuffer drawListBuffer;
    private int mPositionHandle;
    private int mColorHandle;
    private int mMVPMatrixHandle;
    public float coordX;
    public float coordY;
    //public float coordZ;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 2;
    static float spriteCoords[] = { -0.5f,  0.5f,   // top left
            -0.5f, -0.5f,   // bottom left
            0.5f, -0.5f,   // bottom right
            0.5f,  0.5f }; //top right

    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; //Order to draw vertices
    private final int vertexStride = COORDS_PER_VERTEX * 4; //Bytes per vertex

    // Set color with red, green, blue and alpha (opacity) values
    //float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
    float color[] = { 1f, 1f, 1f, 1.0f };

    public Sprite(final Context activityContext, float initX, float initY, float initZ)
    {
        mActivityContext = activityContext;

        this.coordX = initX;
        this.coordY = initY;
        //this.coordZ = initZ;

        //ergh - will do manually for now. Paxo n00b
        //just a 2D array, no need for Z nonsense
        for (int i=0; i<spriteCoords.length; i++) {
            spriteCoords[i] -= (i%2==0) ? coordX : coordY; //- works better than +

        }

        //float newPosMatrix[] = { initX, initY, 0f };

        //adjust the vector coords accordingly

        //Matrix.multiplyMV(spriteCoords, 0, newPosMatrix, 0, spriteCoords, 0);

        //Initialize Vertex Byte Buffer for Shape Coordinates / # of coordinate values * 4 bytes per float
        ByteBuffer bb = ByteBuffer.allocateDirect(spriteCoords.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(spriteCoords);
        //Set the Buffer to Read the first coordinate
        vertexBuffer.position(0);

        // S, T (or X, Y)
        // Texture coordinate data.
        // Because images have a Y axis pointing downward (values increase as you move down the image) while
        // OpenGL has a Y axis pointing upward, we adjust for that here by flipping the Y axis.
        // What's more is that the texture coordinates are the same for every face.
        final float[] cubeTextureCoordinateData =
                {
                        //Front face
            /*0.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,
            1.0f, 0.0f*/

                        /*-0.5f,  0.5f,
                        -0.5f, -0.5f,
                        0.5f, -0.5f,
                        0.5f,  0.5f*/

                        0f, 1f,
                        0f, 0f,
                        1f, 0f,
                        1f, 1f
                };

        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(spriteCoords.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);

        int vertexShader = MyGL20Renderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader = MyGL20Renderer.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
        mTextureDataHandle = loadTexture(mActivityContext, R.drawable.cube);
    }

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

        //Get Handle to Fragment Shader's vColor member
        mColorHandle = GLES20.glGetUniformLocation(shaderProgram, "vColor");

        //Set the Color for drawing the triangle
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);

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


        //glTranslatef(0f, 0f, 0f);

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

Now, I don't know if I'm going about this the right way at all, but I simply want to just animate the collection of Sprite objects in spriteList .

More specifically, have a collection of 3 objects and then respond to screen touch, and animate the objects to that location (but that will come later)

Initially, I just want to be able to correctly render these objects (with initial locations) and then rotate them on the centre point (about the Z axis).

For some reason, TranslateM is warping the texture (as if about the Y axis) and not actually moving an object along the X/Y planes

Many thanks for any help you can offer. As you can see I'm fairly new to OpenGL and have had little luck with the limited tutorials out there that support Android Studio and GLES2.0.

Kind regards, James

I think the problem is that you have not multiplied the translation matrices into your rotation matrices. A matrix multiply is required to combine those.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM