1
\$\begingroup\$

Just trying to get some sprites (textured squares) to draw from a texture atlas and 50% of the time after the application is first launched/ reorientated/ brought from sleep these various graphical glitches seem to occur.

Here is what is supposed to be displayed.
https://i.sstatic.net/o9Mu7.jpg

Here is an example of what goes wrong (it varies)
https://i.sstatic.net/oIX6w.jpg

Here is the code for my Renderer

package com.krazy.androidopengl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.os.SystemClock;
import android.util.Log;

public class GameRenderer implements GLSurfaceView.Renderer 
{   
    public int spriteWidth, spriteHeight;
    private int spriteRows,spriteColumns;

public int width,height;

private Bitmap atlas =null;

private float mScale;

private float[] spritePositionData = new float[12];
private float[] cubeTextureCoordinateData = new float[12];

/** Used for debug logs. */
private static final String TAG = "LessonFourRenderer";

private final Context mActivityContext;

/**
 * Store the model matrix. This matrix is used to move models from object space (where each model can be thought
 * of being located at the center of the universe) to world space.
 */
private float[] mModelMatrix = new float[16];

/**
 * Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space;
 * it positions things relative to our eye.
 */
private float[] mViewMatrix = new float[16];

/** Store the projection matrix. This is used to project the scene onto a 2D viewport. */
private float[] mProjectionMatrix = new float[16];

/** Allocate storage for the final combined matrix. This will be passed into the shader program. */
private float[] mMVPMatrix = new float[16];


/** Store our model data in a float buffer. */
private FloatBuffer mCubePositions;
private FloatBuffer mCubeTextureCoordinates;

/** This will be used to pass in the transformation matrix. */
private int mMVPMatrixHandle;

/** This will be used to pass in the modelview matrix. */
private int mMVMatrixHandle;


/** This will be used to pass in the texture. */
private int mTextureUniformHandle;

/** This will be used to pass in model position information. */
private int mPositionHandle;


/** This will be used to pass in model texture coordinate information. */
private int mTextureCoordinateHandle;

/** How many bytes per float. */
private final int mBytesPerFloat = 4;   

/** Size of the position data in elements. */
private final int mPositionDataSize = 3;    

/** Size of the texture coordinate data in elements. */
private final int mTextureCoordinateDataSize = 2;

/** This is a handle to our cube shading program. */
private int mProgramHandle;

/** This is a handle to our texture data. */
private int mTextureDataHandle;

/**
 * Initialize the model data.
 */


private Drawer drawer;

public GameRenderer(final Context activityContext, Drawer d)
{   
    mActivityContext = activityContext;

    drawer = d;

}

@Override
public void onSurfaceCreated(GL10
        glUnused, EGLConfig config) 
{
    Log.v("krazy", "surfaceCreate");
    // Set the background clear color to black.
    GLES20.glClearColor(0.1f, 0.2f, 0.5f, 0.0f);

    // CHECK OUT WHAT THESE DO/ WHY?
    // Use culling to remove back faces.
    GLES20.glEnable(GLES20.GL_CULL_FACE);

    // Enable depth testing
    GLES20.glEnable(GLES20.GL_DEPTH_TEST);

    // Enable texture mapping
    GLES20.glEnable(GLES20.GL_TEXTURE_2D);

    // Position the eye in front of the origin.
    final float eyeX = 0.0f;
    final float eyeY = 0.0f;
    final float eyeZ = -0.5f;

    // Facing negative because of anti clockwise triangles are only visible in that direction
    // We are looking toward the distance
    final float lookX = 0.0f;
    final float lookY = 0.0f;
    final float lookZ = -1.0f;

    // Set our up vector. This is where our head would be pointing were we holding the camera.
    final float upX = 0.0f;
    final float upY = 1.0f;
    final float upZ = 0.0f;

    // Set the view matrix. This matrix can be said to represent the camera position.
    // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
    // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
    Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);        

    final String vertexShader = getVertexShader();          
    final String fragmentShader = getFragmentShader();          

    final int vertexShaderHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);       
    final int fragmentShaderHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);     

    mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, 
            new String[] {"a_Position",  "a_Color", "a_Normal", "a_TexCoordinate"});

    // Load the texture
        mTextureDataHandle = loadTexture(atlas);



}   

@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) 
{
    Log.v("krazy", "surfaceChanged");
    // Set the OpenGL viewport to the same size as the surface.
    GLES20.glViewport(0, 0, width, height);

    this.width = width;
    this.height = height;

    // Create a new perspective projection matrix. The height will stay the same
    // while the width will vary as per aspect ratio.
    final float left = 0f;
    final float right = width;
    final float bottom = 0f;
    final float top = height;
    final float near = 0f;
    final float far = 10.0f;
    Matrix.orthoM(mProjectionMatrix, 0, left, right, bottom, top, near, far);


}   

public void setAtlas(int w, int h, float scale, Bitmap b)
{
    atlas = b;  
    spriteWidth = w;
    spriteHeight = h;
    spriteRows = atlas.getHeight()/spriteHeight;
    spriteColumns = atlas.getWidth()/spriteWidth;
    mScale = scale;

    spritePositionData = spriteCoords(spriteWidth * scale,spriteHeight * scale);

    // Initialize the buffer.
    mCubePositions = ByteBuffer.allocateDirect(spritePositionData.length * mBytesPerFloat)
            .order(ByteOrder.nativeOrder()).asFloatBuffer();                            
    mCubePositions.put(spritePositionData).position(0); 

    // Initialize the texture buffer.
    mCubeTextureCoordinates = ByteBuffer.allocateDirect(12 * mBytesPerFloat)
            .order(ByteOrder.nativeOrder()).asFloatBuffer();
}


@Override
public void onDrawFrame(GL10 glUnused) 
{
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);                    

    // Set our per-vertex lighting program.
    GLES20.glUseProgram(mProgramHandle);

    // Set program handles for drawing.
    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
    mMVMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix"); 
    mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture");
    mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
    mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "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 on drawing to Drawer
    drawer.onDrawFrame(this);
}               

/**
 * Draws a cube.
 */         
public void drawSprite(float x, float y, int t, float scale)
{   
    if(Math.abs(scale-1f) > 0.00001)
    {
        spritePositionData = spriteCoords(spriteWidth * scale,spriteHeight * scale);
        mCubePositions.put(spritePositionData).position(0);
    }
    else
    {
        spritePositionData = spriteCoords(spriteWidth * mScale,spriteHeight * mScale);
        mCubePositions.put(spritePositionData).position(0);
    }
    int row = t/(spriteColumns+1);
    int column = (t-1)%spriteColumns;

    float rowHeight = 1f/spriteRows;
    float columnWidth = 1f/spriteColumns;

    cubeTextureCoordinateData[0] = column*columnWidth;
    cubeTextureCoordinateData[1] = row*rowHeight;
    cubeTextureCoordinateData[2] = column*columnWidth;
    cubeTextureCoordinateData[3] =(row+1)*rowHeight;
    cubeTextureCoordinateData[4] =(column+1)*columnWidth;
    cubeTextureCoordinateData[5] = row*rowHeight;
    cubeTextureCoordinateData[6] =column*columnWidth;
    cubeTextureCoordinateData[7] = (row+1)*rowHeight;
    cubeTextureCoordinateData[8] = (column+1)*columnWidth;
    cubeTextureCoordinateData[9] = (row+1)*rowHeight;
    cubeTextureCoordinateData[10] =(column+1)*columnWidth;
    cubeTextureCoordinateData[11] = row*rowHeight;

    mCubeTextureCoordinates.put(cubeTextureCoordinateData).position(0);

    // Translate/ Rotate      
    Matrix.setIdentityM(mModelMatrix, 0);
    Matrix.translateM(mModelMatrix, 0, x, y, -1f);

    // Pass in the position information
    mCubePositions.position(0);     
    GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false,
            0, mCubePositions);        

    GLES20.glEnableVertexAttribArray(mPositionHandle);        

    // Pass in the texture coordinate information
    mCubeTextureCoordinates.position(0);
    GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 
            0, mCubeTextureCoordinates);

    GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);

    // This multiplies the view matrix by the model matrix, and stores the result in the MVP matrix
    // (which currently contains model * view).
    Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);   

    // Pass in the modelview matrix.
    GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);                

    // This multiplies the modelview matrix by the projection matrix, and stores the result in the MVP matrix
    // (which now contains model * view * projection).
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);

    // Pass in the combined matrix.
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);

    // Draw the cube.
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36); 
}   

public static int loadTexture(Bitmap bitmap)
{
    final int[] textureHandle = new int[1];

    GLES20.glGenTextures(1, textureHandle, 0);

    if (textureHandle[0] != 0)
    {
        // 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];
}

private float[] spriteCoords(float x, float y)
{
    x /= 2;
    y /= 2;
    final float[] spritePositionData =
        {
            // In OpenGL counter-clockwise winding is default. This means that when we look at a triangle, 
            // if the points are counter-clockwise we are looking at the "front". If not we are looking at
            // the back. OpenGL has an optimization where all back-facing triangles are culled, since they
            // usually represent the backside of an object and aren't visible anyways.

            -x, y, -1f,             
            -x, -y, -1f,
            x, y, -1f, 
            -x, -y, -1f,                
            x, -y, -1f,
            x, y,-1f,
        };

    return spritePositionData;
}

protected String getVertexShader()
{
    return RawResourceReader.readTextFileFromRawResource(mActivityContext, R.raw.per_pixel_vertex_shader);
}

protected String getFragmentShader()
{
    return RawResourceReader.readTextFileFromRawResource(mActivityContext, R.raw.per_pixel_fragment_shader);
}
}

Any help would be greatly appreciated.

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

You may have to reload your textures after waking. On many platforms, GPU resources will be released as needed if the app becomes inactive. I'd check with OpenGL to verify that your handles are still valid when your app is reactivated, or you may need to reset the texture buffer contents.

Note that I haven't played with Android, but I've seen this on Windows with both DirectX and OpenGL, usually when waking or returning from a screen saver. I suspect that behavior is up to the video driver but I'm not sure how the driver model, if any, works on Android.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your response, reloading the textures is something that I have been doing(helped prevent complete garbage from being drawn to the screen after waking from sleep). Unfortunately this does not seem to affect the random artifacts appearing after reorienting the screen. I think I will try and simplify my code to help identify the problem. \$\endgroup\$
    – Krazy
    Commented Aug 21, 2012 at 0:56
1
\$\begingroup\$

Found the problem! I was still using GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36); which was for cubes, all the error was in the 36 vertices where I was actually only drawing 6.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .