Why do my 512x512 bitmaps look jaggy on Android OpenGL?
- by Milo Mordaunt
This is sort of driving me nuts, I've googled and googled and tried everything I can think of, but my sprites still look super blurry and super jaggy. Example:
Here: https://docs.google.com/open?id=0Bx9Gbwnv9Hd2TmpiZkFycUNmRTA
If you click through to the actual full size image you should see what I mean, it's like it's taking and average of every 5*5 pixels or something, the background looks really blurry and blocky, but the ball is the worst. The clouds look all right for some reason, probably because they're mostly transparent.
I know the pngs aren't top notch themselves but hey, I'm no artist!
I would imagine it's a problem with either:
a. How the pngs are made
example sprite (512x512): https://docs.google.com/open?id=0Bx9Gbwnv9Hd2a2RRQlJiQTFJUEE
b. How my Matrices work
This is the relevant parts of the renderer:
public void onDrawFrame(GL10 unused) {
if(world != null) {
dt = System.currentTimeMillis() - endTime;
world.update( (float) dt);
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
Matrix.setIdentityM(mvMatrix, 0);
Matrix.translateM(mvMatrix, 0, 0f, 0f, 0f);
world.draw(mvMatrix, mProjMatrix);
endTime = System.currentTimeMillis();
} else {
Log.d(TAG, "There is no world....");
}
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
Matrix.orthoM(mProjMatrix, 0, 0, width /2, 0, height /2, -1.f, 1.f);
}
And this is what each Quad does when draw is called:
public void draw(float[] mvMatrix, float[] pMatrix) {
Matrix.setIdentityM(mMatrix, 0);
Matrix.setIdentityM(mvMatrix, 0);
Matrix.translateM(mMatrix, 0, xPos, yPos, 0.f);
Matrix.multiplyMM(mvMatrix, 0, mvMatrix, 0, mMatrix, 0);
Matrix.scaleM(mvMatrix, 0, scale, scale, 0f);
Matrix.rotateM(mvMatrix, 0, angle, 0f, 0f, -1f);
GLES20.glUseProgram(mProgram);
posAttr = GLES20.glGetAttribLocation(mProgram, "vPosition");
texAttr = GLES20.glGetAttribLocation(mProgram, "aTexCo");
uSampler = GLES20.glGetUniformLocation(mProgram, "uSampler");
int alphaHandle = GLES20.glGetUniformLocation(mProgram, "alpha");
GLES20.glVertexAttribPointer(posAttr, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, vertexBuffer);
GLES20.glVertexAttribPointer(texAttr, 2, GLES20.GL_FLOAT, false, 0, texCoBuffer);
GLES20.glEnableVertexAttribArray(posAttr);
GLES20.glEnableVertexAttribArray(texAttr);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
GLES20.glUniform1i(uSampler, 0);
GLES20.glUniform1f(alphaHandle, alpha);
mMVMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVMatrix");
mPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uPMatrix");
GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mvMatrix, 0);
GLES20.glUniformMatrix4fv(mPMatrixHandle, 1, false, pMatrix, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, 4, GLES20.GL_UNSIGNED_SHORT, indicesBuffer);
GLES20.glDisableVertexAttribArray(posAttr);
GLES20.glDisableVertexAttribArray(texAttr);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
c. How my texture loading/blending/shaders setup works
Here is the renderer setup:
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthMask(false);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glEnable(GLES20.GL_DITHER);
}
Here is the vertex shader:
attribute vec4 vPosition;
attribute vec2 aTexCo;
varying vec2 vTexCo;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
void main() {
gl_Position = uPMatrix * uMVMatrix * vPosition;
vTexCo = aTexCo;
}
And here's the fragment shader:
precision mediump float;
uniform sampler2D uSampler;
uniform vec4 vColor;
varying vec2 vTexCo;
varying float alpha;
void main() {
vec4 color = texture2D(uSampler, vec2(vTexCo));
gl_FragColor = color;
if(gl_FragColor.a == 0.0) {
"discard;
}
}
This is how textures are loaded:
private int loadTexture(int rescource) {
int[] texture = new int[1];
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inScaled = false;
Bitmap temp = BitmapFactory.decodeResource(context.getResources(), rescource, opts);
GLES20.glGenTextures(1, texture, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, temp, 0);
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
temp.recycle();
return texture[0];
}
I'm sure I'm doing about 20,000 things wrong, so I'm really sorry if the problem is blindingly obvious...
The test device is a Galaxy Note, running a JellyBean custom ROM, if that matters at all. So the screen resolution is 1280x800, which means... The background is 1024x1024, so yeah it might be a little blurry, but shouldn't be made of lego.
Thank you so much, any answer at all would be appreciated.