loading multiple textures openGL - java

I just started using openGL I need to load allot of bitmaps for animation I can get it to work great for a few frames but run out of memory when I try to load all the frames How can I load just a few frames at a time? This is the code I'm using to load the textures
public void loadGLTexture(GL10 gl, Context context) {
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.r1);
Bitmap bitmap01 = Bitmap.createScaledBitmap(bitmap, 512, 512, false);
Bitmap bitmap2 = BitmapFactory.decodeResource(context.getResources(),R.drawable.r2);
Bitmap bitmap02 = Bitmap.createScaledBitmap(bitmap2, 512, 512, false);
Bitmap bitmap3 = BitmapFactory.decodeResource(context.getResources(),R.drawable.r3);
Bitmap bitmap03 = Bitmap.createScaledBitmap(bitmap3, 512, 512, false);
try {
bitmap = bitmap01;
bitmap2 = bitmap02;
bitmap3 = bitmap03;
} finally {
}
//Generate there texture pointer
gl.glGenTextures(3, textures, 0);
//Create Texture and bind it to texture 0
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
//Create Texture and bind it to texture 1
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[1]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap2, 0);
//Create Texture and bind it to texture 2
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[2]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap3, 0);
//Clean up
bitmap.recycle();
bitmap2.recycle();
bitmap3.recycle();
}
}

Well, it's the classic issue of loading to many bitmaps in the (very limited) memory available to your app's process. I'm not gonna get into this, suffice to say that pre Honeycomb these bitmaps were loaded offheap, from Honeycomb and beyond they're loaded on the heap, but the issue still remains: a bitmap of 128x256 pixels will take 4*128*256 bytes in memory. And you usually have between 16 and 48 megs of available memory for your app (depending on device and Android version).
Good news here is that once you load a bitmap and create an opengl texture with it, you no longer need that Bitmap object, you can recycle it and make it null. So you might try loading (and, as I see from your code, scaling) the first bitmap, then create the first texture object, then recycle and de-refference that first bitmap. Then go on and do the same with the second bitmap for the second texture object.

First, you could recycle the bitmaps as soon as you scale the image. Second, you should prescale them down, that is, store them as 512x512 from the get go.
But most importantly, you should create a texture atlas for this. Essentially, instead of using multiple textures, you could use a single, large texture and store all the fragments there. The idea would be to create, say, a 2048x2048 texture, which can store 16 "cels" of 512x512 pixels (4 rows of 4 cels each), and then when drawing your quads on screen, altering the texture coordinates to display a particular cel.
For offline generation, there are a number of utilities that can help; TexturePacker is one, and there are many others. To do it in code, you can generate your texture once:
gl.glGenTextures(1, textures, 0);
//Create Texture and bind it to texture 0
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, 2048, 2048, 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, null);
That last line allocates an empty image of the size (2048x2048)
Then for each of your bitmaps, you can use:
int xoffset = (imageIndex % 4) * 512;
int yoffset = (imageIndex / 4) * 512;
GLUtils.texSubImage2D(GL10.GL_TEXTURE_2D, 0, xoffset, yoffset, bitmap);
This will load your bitmap into a portion of the texture. As #Joseph hinted, the constraints for the RAM usage on the GL Textures are quite relaxed.

Related

OpenGL ES Android - Texture not loaded (rendered black)

As in the title, I'm using OpenGL ES (2) on Android (7.0).
Apparently, though, I have some problems when trying to load a texture from the asset folder.
This is the code I'm using
public static int loadTexture(final AssetManager assetManager, final String img)
{
final int[] textureHandle = new int[1];
glGenTextures(1, textureHandle, 0);
if(textureHandle[0] == 0)
throw new RuntimeException("Error loading texture");
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
try
{
final Bitmap bitmap = BitmapFactory.decodeStream(assetManager.open(img));
glBindTexture(GL_TEXTURE_2D, textureHandle[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
catch (IOException e)
{
e.printStackTrace();
}
textures.put(img, textureHandle[0]);
return textureHandle[0];
}
This is called from onSurfaceCreated as I know I need a running OpenGL context
Most of the time this works and gives me this result
but a considerable number of other times I get this
No Exception is thrown.
I know the problem is not depending on the 3D model nor the texture because I've tried other ones. The only thing I can do when this happens is to restart my App a couple of times.
I've tried googling around but with no results. I know I could try to implement another loading function, but first I'd like to understand why this is not ok and where it's not working.
If you think this could depends on the code I'm using to render the scene here it is (no VAOs because I need to support older devices and OpenGL ES 2):
glBindBuffer(GL_ARRAY_BUFFER, model.getCoordsVBO());
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, model.getTextureVBO());
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, model.getNormalsVBO());
glVertexAttribPointer(2, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, entity.getTexture());
loadFloat(loc_textureSampler, 0);
if(model.getIndicesBuffer() != null)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model.getIndicesVBO());
glDrawElements(GL_TRIANGLES, model.getVertexCount(), GL_UNSIGNED_INT, 0);
}
else
glDrawArrays(GL_TRIANGLES, 0, model.getVertexCount());
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
What I didn't say (because I thought it was irrelevant) is that I had a little more code ad the top of the function...
if(textures.containsKey(img))
return textures.get(img);
and at the bottom
textures.put(img, textureHandle[0]);
I thought it was intelligent to keep track of the textures, so I could have called this load function anytime I needed a texture and if I had already loaded it, it would have returned it without loading again.
This could work on a desktop application, but I haven't considered that on Android the garbage collector could delete the texture from OpenGL when I exit the app so that when the activity restarts it wouldn't be there anymore.
Removing that HashMap solved the problem, demonstrating that the actual reading and loading code had nothing to do with that black thing.

Effective clean at opengl animation in android?

My Renderer looks like this:
public void onDrawFrame(GL10 gl) {
if(draw) {
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glFrontFace(GL10.GL_CW);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
textures[i]);
gl.glGenTextures(1, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
} else {
gl.glDeleteTextures(textures.length, textures, 0);
gl.glFlush();
}
}
Is this all that I can do to clean after animation is over? I use this renderer all the time and after a few minutes I always get out of memory.
I use this renderer all the time and after a few minutes I always get out of memory.
You're not supposed to create all the textures anew every frame. You create them once and reuse them. If you want to update the picture while keeping format and dimensions use glTexSubImage2D on an existing texture object.

Android - OpenGL - Emulator vs Actual Device

I am writing a game which uses opengles. I have created my renderer class and have a sample of my game working on the emulator, however none of the texures display on an actual device. I have read about the most common cause for this being the need for texture to be a factor of 2 however I have tried drawing a square (128x128) with a texture of the same size mapped to it and this only shows on the emulator. Further to that my actual game will be using rectangles so I'm unsure how I can map textures that are squares to rectangles..
This is my code so far (The game is 2d so I'm using ortho mode):
EDIT: I have updated my code, it is now correctly binding textures and using textures of size 128x128, still only seeing textures on the emulator..
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
byteBuffer = ByteBuffer.allocateDirect(shape.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuffer.asFloatBuffer();
vertexBuffer.put(cardshape);
vertexBuffer.position(0);
byteBuffer = ByteBuffer.allocateDirect(shape.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
textureBuffer = byteBuffer.asFloatBuffer();
textureBuffer.put(textureshape);
textureBuffer.position(0);
// Set the background color to black ( rgba ).
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
// Enable Smooth Shading, default not really needed.
gl.glShadeModel(GL10.GL_SMOOTH);
// Depth buffer setup.
gl.glClearDepthf(1.0f);
// Enables depth testing.
gl.glEnable(GL10.GL_DEPTH_TEST);
// The type of depth testing to do.
gl.glDepthFunc(GL10.GL_LEQUAL);
// Really nice perspective calculations.
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
gl.glEnable(GL10.GL_TEXTURE_2D);
loadGLTexture(gl);
}
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glMatrixMode(GL10.GL_PROJECTION); // Select Projection
gl.glPushMatrix(); // Push The Matrix
gl.glLoadIdentity(); // Reset The Matrix
gl.glOrthof(0f, 480f, 0f, 800f, -1f, 1f);
gl.glMatrixMode(GL10.GL_MODELVIEW); // Select Modelview Matrix
gl.glPushMatrix(); // Push The Matrix
gl.glLoadIdentity(); // Reset The Matrix
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glLoadIdentity();
gl.glTranslatef(card.x, card.y, 0.0f);
gl.glBindTexture(GL10.GL_TEXTURE_2D, card.texture[0]); //activates texture to be used now
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
// Sets the current view port to the new size.
gl.glViewport(0, 0, width, height);
// Select the projection matrix
gl.glMatrixMode(GL10.GL_PROJECTION);
// Reset the projection matrix
gl.glLoadIdentity();
// Calculate the aspect ratio of the window
GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f,
100.0f);
// Select the modelview matrix
gl.glMatrixMode(GL10.GL_MODELVIEW);
// Reset the modelview matrix
gl.glLoadIdentity();
}
public int[] texture = new int[1];
public void loadGLTexture(GL10 gl) {
// loading texture
Bitmap bitmap;
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.image);
// generate one texture pointer
gl.glGenTextures(0, texture, 0); //adds texture id to texture array
// ...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0]); //activates texture to be used now
// create nearest filtered texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// Use Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Clean up
bitmap.recycle();
}
Is there anything I have done wrong? Or something I haven't done? It works perfectly fine in the emulator so I could only assume it was the power of 2 issue but like I said I tried that using a 128x128 texture on a square but it didn't show.. any help would be appreciated..
EDIT: I have also tried setting the minsdkversion is 3, loading the bitmap via an input stream bitmap = BitmapFactory.decodeStream(is), setting BitmapFactory.Options.inScaled to false, putting the images in the nodpi folder and then trying them in the raw folder.. any other ideas?
I'm actually looking for the solution to a similar problem right now. I think I might have a temporary fix for you, however.
The problem appears to be that on the emulator the orthographic view is flipped. To solve this, in my app we added an option in preferences to manually flip the view if nothing draws. Here's the snippet that handles this:
if (!flipped)
{
glOrthof(0, screenWidth, screenHeight, 0, -1, 1); //--Device
}
else
{
glOrthof(0, screenWidth, 0, -screenHeight, -1, 1); //--Emulator
}
Hope this helps! If anybody has a more general solution, I'd be happy to hear it!
I didn't look at your code but I have been on that road before. Developing in OpenGL is a real pain in the ass. If you are not obligated to use OpenGL, then use a graphics engine. Unity is a great one and it's free. Also your game would work on Android, iOS or other platforms. Study your choices carefully. Good luck..

Drawable to Bitmap, Drawable unknown size / dimension

I have a drawable object that is a vector. Since you can't crop (or clip because of ICS forced hardware acceleration) a drawable it make's sprite animation useless.
I'm trying to convert the drawable to a bitmap with a specific size (percentage of the lowest screen dimension for best results).
The version needs to be super memory efficient of course.
What I need help with is creating the bitmap with the specific size, here's what I got so far:
public Bitmap svgTObitmap(String name, int percentage, int screen_width, int screen_height)
{
_resID = _context.getResources().getIdentifier( name , "raw" , _context.getPackageName());
SVG svg = SVGParser.getSVGFromResource(_context.getResources(), _resID);
Drawable drawable = svg.createPictureDrawable();
Bitmap bmp = ((BitmapDrawable)drawable).getBitmap();
return bmp;
}
Being from a vector, clearly your Drawable is not in fact a BitmapDrawable. You need to instead draw the Drawable on a Bitmap. See this answer I gave to another question:
Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
Obviously in your case you would specify the size you want explicitly instead of trying to get the intrinsic height/width of the drawable.

Translucent or Transparent Image created in Java does not display in Android OpenGL ES

I create an image in Java 6, using a Mac computer with the code below. I then try to display that image in an Android device using OpenGL ES. The image does not not get displayed and only a white image gets displayed. However, if there are no translucent pixels then the image gets displayed fine.
I have no problems displaying translucent images created in Photoshop. It is only with translucent images created in Java that I have a problem. I've also tried different methods of creating a translucent image with Java and I always get the same result, a white image displayed in OpenGL. At this point I believe this is a bug with Java, (In a Mac computer). has anybody ever run into something like this? Any idea why this might be happening?
//Code for creating an image
int cmap[] = {
0x00000000, /*transparent*/
0xFF000000, /*black*/
0xFFFF0000, /*red*/
0xFFFFFF00, /*yellow*/
0xFF00FF00, /*green*/
//......
};
IndexColorModel colorModel = new IndexColorModel(8,
cmap.length, cmap, 0, true, -1, DataBuffer.TYPE_BYTE);
BufferedImage image = new BufferedImage(256, 256,
BufferedImage.TYPE_BYTE_INDEXED, colorModel);
Graphics2D g = image.createGraphics();
g.setBackground(new Color(0,0,0,0)); /*transparent*/
g.clearRect(0 , 0, image.getWidth(), image.getHeight());
g.setColor(Color.red);
g.draw(new Rectangle(10, 10, 40, 40));
ImageIO.write(image, "PNG", new File("rectangle.png"));
Below is the code for loading the texture. Please note that this is specific to the Android environment.
public static void loadAndBindTexture(GL10 gl) throws Exception
{
int numberTextures=1;
int[] textures = new int[numberTextures];
gl.glGenTextures(numberTextures, textures, 0);
int textureID=textures[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);//polyBreaker3DObject_.mTextureID);
checkGLError(gl, 998);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR_MIPMAP_LINEAR);
checkGLError(gl, 997);
//NOTE: FOR GL_TEXTURE_MAG_FILTER only GL_NEAREST or GL_LINEAR are valid.
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
checkGLError(gl, 996);
/*Commented on Sept. 30, 2010 6:21AM
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MAG_FILTER,GL10.GL_LINEAR);
*/
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,GL10.GL_CLAMP_TO_EDGE);
checkGLError(gl, 1000);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,GL10.GL_DECAL);
checkGLError(gl, 1001);
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
checkGLError(gl, 1002);
if(mContext==null)//mContext is the Context of the current Android application (it is set before this method is called
throw new Exception("Context has not been set.");
InputStream is = mContext.getResources().openRawResource(R.drawable.metal128x128);//blueski);//robot);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch(IOException e) {
Log.printCaughtExceptionMsg(e);
}
}
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
checkGLError(gl, 1003);
}
Can you post the actual call to glDrawElements() or glDrawArrays()? It's hard to tell what you are doing wrong without looking looking at the actual OpenGL draw call. But it's possible that you are not enabling blending before you draw the texture.
Something like this should do the trick:
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glEnable(GL10.GL_BLEND);
Take a look at this question if you want a little more information.
EDIT:
My mistake, I misread your question. You should take a look at the BufferedImage format you are using. TYPE_BYTE_INDEXED doesn't seem to support alpha values based on the documentation. Have you tried using TYPE_INT_ARGB instead?
I still have not really been able to figure out why transparent images created with Java do not work in Android OpenGL ES. However, I did find a hack that allows the images to work.
When you create your new transparent image in java, the first thing that you should do is to draw a semi-transparent image on it using the method call
g.drawImage(Image img, x, y, null);
//g is a Graphics object obtained from the new image being created
//img argument is a semi transparent, not 100% transparent (or else it will not work) image created in Photoshop. I use a 4x4 size so that it won't be noticeable.
After this call you can go ahead and continue executing your original code. The new image will now work in Android OpenGL ES.
My guess is that by drawing a semi transparent image it changes the format of the PNG file generated, and this format change makes it work in Android OpenGL ES.

Categories