AndroidでOpengl 2.0を使用して、テクスチャアトラスから正方形のスプライトを描画するのに役立つクラスを作成しようとしています。特にデバイスがスリープ状態から復帰したときに、奇妙なグラフィカルな不具合が発生します (線や四角形のテクスチャが欠落している場合があります)。
これがどのように見えるかです:
電話をスリープから復帰させた後の問題の例を次に示します。
レンダラーの私のコード。
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;
public class GameRenderer implements GLSurfaceView.Renderer
{
private int spriteWidth, spriteHeight;
private int spriteRows,spriteColumns;
private Bitmap atlas;
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)
{
// Set the background clear color to black.
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// 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)
{
// Set the OpenGL viewport to the same size as the surface.
GLES20.glViewport(0, 0, width, 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, Bitmap bitmap, float scale)
{
spriteWidth = w;
spriteHeight = h;
spriteRows = bitmap.getHeight()/spriteHeight;
spriteColumns = bitmap.getWidth()/spriteWidth;
atlas = bitmap;
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, 0f,
-x, -y, 0f,
x, y, 0f,
-x, -y, 0f,
x, -y, 0f,
x, y,0f,
};
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);
}
}