I am reading tutorials online on how to load textures in an android application and passing them in shaders. I have found this method
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];
}
but how do I use it? what do I put as parameters when calling this method??? what is that integer that it returns? I suppose from my knowledge of opengl that the int it returns is is just the "number" of the texture in case I am loading many textures. The texture handle if you may. But what about the rest???
ANDROID AND CONTEXT If you look through the various Android APIs, you’ll notice that many of them take an android.content.Context object as a parameter. You’ll also see that an Activity or a Service is usually used as a Context. This works because both of these classes extend from Context.
What’s Context exactly? Per the Android reference documentation, it’s an entity that represents various environment data. It provides access to local files, databases, class loaders associated to the environment, services including system-level services, and more. Throughout this book, and in your day-to- day coding with Android, you’ll see the Context passed around frequently. From: "Android in Practice" book.
So the above method should be called inside the mainactivity class with 1st parameter being one of getApplicationContext(), getContext(), getBaseContext() or this
resourceID represents an the resource file that you wish to work with. In my case I had a BMP image inside res/drawable folder and this could be got by writing
R.res.myimagename.bmp
and this simple code returns a simple integer that is the location of the resource file so in fact resourceID. In other terms it somewhat like a relative path to the resource file
Related
I've managed to load textures and free rotate a sphere thanks to several tutorials, questions and answers asked here but i stumbled upon the need of texture reloading at runtime (get a bitmap image, process it and then apply it as a new texture to an object). I didnt find any working solutions for my specific problem (i've read all related questions and answers).
Im quite new to OpenGL. This is my second project, my first 3D one and my first question asked here. So here it goes:
Basicaly the texture loading is done in the following function:
public void loadGLTexture(final Context context, final int texture) {
GLES20.glGenTextures(1, mTextures, 0);
if (mTextures[0] != 0)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), texture, options);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
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);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
if (mTextures[0] == 0)
{
throw new RuntimeException("Texture load fail");
}
}
While the draw is done in this function:
public void draw(float[] mvpMatrix) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.mTextures[0]);
GLES20.glFrontFace(GLES20.GL_CW);
for (int i = 0; i < this.mTotalNumStrips; i++) {
//vertex
this.mVertexBuffer.get(i).position(0);
GLES20.glVertexAttribPointer(mPositionHandle, NUM_FLOATS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, this.mVertexBuffer.get(i));
GLES20.glEnableVertexAttribArray(mPositionHandle);
//texture
this.mTextureBuffer.get(i).position(0);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, NUM_FLOATS_PER_TEXTURE,
GLES20.GL_FLOAT, false,
textureStride, this.mTextureBuffer.get(i));
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
//draw strip
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, this.mVertices.get(i).length / NUM_FLOATS_PER_VERTEX);
}
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// Disable the client state before leaving.
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTextureCoordinateHandle);
}
In the above function i use the Strips tehnique to render a sphere. For each strip i have to load the texture and vertex data and finaly draw it.
I also have a function that should delete the textures that does nothing more than:
public void clearGLTextures(){
GLES20.glDeleteTextures(1, mTextures, 0);
}
What i want to achieve here is texture reloading so the plan was:
INITIALISE(works): loadGLTexture(initialtexture) -> loop the draw() function
RELOAD(does not work): clearGLTextures() -> loadGLTexture(newTexture) -> loop the draw() function
So not only that i cant reload the texture but also the call to clearGLTextures() seems not to work because the initialtexture remains on scren.
Any thoughts are welcome,
Thanks!
This is an example of a very common kind of problem when doing OpenGL programming on Android. Unfortunately the symptoms are very variable, so the questions are not really duplicates.
When you use GLSurfaceView, it creates a separate rendering thread. All the methods in your GLSurfaceView.Renderer implementation (onSurfaceCreated, onSurfaceChanged, onDrawFrame) are called in this rendering thread.
The second part of the puzzle is that you need a current OpenGL context to make OpenGL calls. And OpenGL contexts are current per thread. GLSurfaceView takes care of creating a context, and making it current in the rendering thread.
As a consequence, you can't make any OpenGL calls from other threads (unless you do much more work, and add complexity). The most common error is to try making OpenGL calls from the UI thread, typically when handling user input like touch events. Those calls will not work.
There are a number of options if you want to execute OpenGL calls in response to user input. For example, you can set members in the Renderer that describe the necessary state changes, and are checked in the onDraw() method. Another convenient option is using the queueEvent() method on the view, which allows you to pass in a Runnable that will later be executed in the rendering thread.
I just ran into the same problem.
Reto Koradi explained it great but I wanted to shared my solution:
I used queueEvent with a Runnable in the GLSurfaceView.
public void addTexture(final int textureResId) {
queueEvent(new Runnable() {
#Override
public void run() {
mRenderer.loadTexture(textureResId);
// or different GL thread tasks like clearing the texture
}
});
}
I have created a texture like this
public int createTexture(Bitmap bitmap){
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
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);
return textureHandle[0];
}
Now based on the user input I want to update my Texture with the new Bitmap. I tried recalling same function with different Bitmap but its not getting updated. Am I doing something wrong here?
EDIT
I tried as Tommy said in his answer but no use. Let me elaborate how I am using textures.
public void changeFilter(){
//create required bitmap here
if(mTextureDataHandle1==0)
mTextureDataHandle1 =loadTexture(bitmap);
else
updateTexture(mTextureDataHandle1);
}
In onDrawFrame
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mTextureDataHandle1);
glUniform1i(mTextureUniformHandle1, 1);
That method both creates a new texture and uploads a Bitmap to it. It sounds like you want to do the second thing but not the first? If so then provide the int texture name as a parameter rather than receiving it as a result, and skip straight to the glBindTexure (i.e. omit the glGenTextures, which is what creates the new texture).
E.g.
public int createTexture(Bitmap bitmap){
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
glBindTexture(GLES20.GL_TEXTURE_2D, textureName);
glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
updateTexture(textureHandle[0], bitmap);
return textureHandle[0];
}
public void updateTexture(int textureName, Bitmap bitmap) {
glBindTexture(GLES20.GL_TEXTURE_2D, textureName);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
}
So the bit where you upload a Bitmap is factored out from the bit where you create a texture and set its filtering type; to update an existing texture use the int you got earlier and call directly into updateTexture.
You may simply be not on the OpenGL rendering thread when changing the Bitmap. When you change the Bitmap, save in a boolean that you did so, and then only in the rendering thread call texImage2D.
I tried the techniques in the above responses I got horrible performance. If what you want is to update the texture live as in 60 fps there is a better way. BTW I didn't find documentation on it, I had to pull and pieces together from different sources.
(1) you have to tell OpenGL extensions to use external textures. That is when defining your texture instead of using GLES20.GL_TEXTURE0 you use GLES11Ext.GL_TEXTURE_EXTERNAL_OES
(2) you have to use the classes Surface and SurfaceTexture. (look into consumers and producers for more into. They work like a server/client but instead the architecture is called consumer/producer)
(3) You have to enable an extension on your fragment shader. You can't use sampler2D you have to use samplerExternalOES. To enable the extension you put the following line at the top of your shader code:
#extension GL_OES_EGL_image_external: require
Code snapshot
The above is a snapshot of my code, below is the fragment shader
Fragment shader
I will like to load textures during run time ( so I dont need to load all of the texture)
If I try to load a texture during run time with this function, I get the RunTimeException ( I think that is because the context is already created)
Is any way to do this without having 2 OpenlGL context(I read that this may cause error if the drivers are bad implemented)?
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];
}
I haven't touched OpenGL ES in Java, only C++ but I would have thought this would be perfectly OK - are you trying to create this texture on a different thread to the thread you created the OpenGL context on? Did your OpenGL context creation code succeed?
I am building an android opengl es 2.0 application. I am drawing squares in a circle, and with the user's movements left and right I am moving the squares. I am applying textures on the squares and keep getting this error:
2-19 16:08:25.666: E/Adreno200-EGL(8183): eglLockWindowSurface: failed to map the memory
for fd=42 offs=6352896
I think that it has something to do with the textures. For loading the textures I use:
public static int loadTexture(final Context context, Bitmap bmp)
{
final int[] textureHandle = new int[1];
// GLES20.glDeleteTextures(1, textureHandle, 0);
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 = bmp;
// 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];
}
Note: with bitmap.recycle() the app does not work.
Also I must say that the onDraw() method is always working and the renderer is NOT set to RENDER_WHEN_DIRTY. I assume that I am loading too many textures and not doing anything to the old ones, but I can't solve this problem for several days now. If someone has a solution, please let me know. I will deeply appreciate any advice or feedback. Thank u in advance!!!
I am having problems rendering textures loaded from the assets folder. If I do exactly the same procedure loading the same texture from the resources folder, everything works fine. Also the image from the assets seems to be loading correctly as when I get the width and height property from the bitmap object, these correspond with the dimensions of the file on disk. However attempting to render the texture results in plain black.
// [...]
int [] tmpId = new int[1];
GLES20.glGenTextures(1, tmpId, 0);
int id = tmpId[0];
InputStream in = null;
try {
in = context.getAssets().open(resource);
}
catch (IOException e)
{
e.printStackTrace();
}
Bitmap bitmap = BitmapFactory.decodeStream(in);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
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);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
bitmap.recycle();
Am I missing something, are there rules which apply to assets which are different from resources? I can´t seem to figure out why this isn´t working.
The image is 512x512 png format with transparency.
I'm not 100% sure as You didn't present the rest of the code, but basing on what You've shown it seems that You're unbinding the texture, which You want to draw, through glBindTexture call with the last parameter equal 0.