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
}
});
}
Related
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
Firsty what am I creating: 2d tile based rpg game.
What I am currently doing I will post here, and comment some spots that I am not sure if I am using them correctly.
In GlSurfaceViewRenderer:
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//I enable some attributes
//Don't know if its needed in GLES20, there isnt GL20.GL_PERSPECTIVE_CORRECTION_HINT attribute at all
GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
//next I don't really know if I need them all:
GLES20.glClearColor(0, 0, 0, 1);
GLES20.glClearDepthf(1.0f);
GLES20.glDisable(GLES20.GL_CULL_FACE);// No culling of back faces
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
GLES20.glDisable(GLES20.GL_DITHER);
GLES20.glDisable(GL10.GL_LIGHTING);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// for transperent pixels
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
//Load shaders: (1 vertex and fragment shader I hope is enouph)
iProgId = Utils.LoadProgram(vertexShaderCode, fragmentShaderCode);
GLES20.glUseProgram(iProgId);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(iProgId, "vPosition");
// get handle to textures shader's a_TexCoordinate member
mTextureCoordinateHandle = GLES20.glGetAttribLocation(iProgId, "a_TexCoordinate");
// get handle to transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(iProgId, "uMVPMatrix");
//Now in here not sure: some people enable arrays in every draw frame, I do it once:
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Enable a handle to the texture vertices
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
Matrix.frustumM(mProjMatrix, 0, ratio, -ratio, 1, -1, 1, 10000);
//.... and other
}
#Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//set camera position
Matrix.setLookAtM(...);
Matrix.multiplyMM(...);
//begin drawing:
for(Sprite spr : Sprite_list){
spr.draw(){
//whats happening in draw:
//First setting the location of the sprite:
Matrix.setIdentityM(...x & y...);
Matrix.translateM(...);
Matrix.setIdentityM(...);
Matrix.multiplyMM(...);
//Some developers enables vertex attrib arrays here and then disables at the end of this drawing method. But I enable it in on surface created and don't disable it, maybe it's faster this way, not sure.
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(GLRenderer.mPositionHandle, DIMENSION, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(GLRenderer.mTextureCoordinateHandle, DIMENSION, GLES20.GL_FLOAT, false, vertexStride, textureBuffer);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textID);
GLES20.glUniformMatrix4fv(GLRenderer.mMVPMatrixHandle, 1, false, GLRenderer.mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
}
}
}
And everything works, for now, but I really not sure if I am doing any mistakes here, can someone elaborate?
The code itself does not necessarily do anything incorrect from what I can see.
Apart from GLES20.glDisable(GL10.GL_LIGHTING); as it's not a feature available in ES2.
Although the code is very limited to what it will be able to do. Reason is that you're uploading most of the rendering states in surfaceCreated, not when you actually might need them/change renderstates.
For example:
//Now in here not sure: some people enable arrays in every draw frame, I do it once:
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Enable a handle to the texture vertices
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
It's perfectly fine to just enable the attribute arrays at one place, however if you would like to utilize a different shader program for a particular piece of geometry in your scene, this would become quite problematic.
Consider this, if you would want to change program, you need to potentially Enable/Disable the particular attributes for that program. You would also need to bind the program (glUseProgram)
Therefore as you referenced yourself in the code comment, that others normally enable/disable these during rendering is for that specific reason.
However it's not just for attribute streams but also all types of renderstates like changing program, enabling Cullmode, blending and so forth.
Now one shouldn't go crazy and just upload and change all of these before every draw call, as changing states are expensive.
One will try to batch all the draw calls that will use the same type of renderstates and resources such as textures/programs and so forth together to minimize number of renderstates changes.
So in your renderloop you would then render the sorted scene of renderable objects, then upload any renderstate that might need to change or been invalidated.
I am very new to OpenGL, and I am trying to create a 2 pass shader. Basically, it has two frame buffers and two shader programs. It runs the first pass as usual, and then I need to take the resulting texture and pass it as an input to the second shader. How is this done? I cannot seem to see how you take a resulting texture and use it as an input to the next texture?
Here is some code: This code assumes I have setup the second filter program, and some attributes and uniforms in the program correctly
#Override
public void onDraw(final int textureId, final FloatBuffer cubeBuffer,final FloatBuffer textureBuffer){
//this draws the first pass (this is tested and working)
super.onDraw(textureId, cubeBuffer, textureBuffer);
//change the program
GLES20.glUseProgram(secondFilterProgram);
//clear the old colors
GLES20.glClearColor(0, 0, 0, 1);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glActiveTexture(GLES20.GL_TEXTURE3); //change the texture
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, secondFilterOutputTexture[0]);
GLES20.glUniform1i(secondFilterInputTextureUniform, 3);
cubeBuffer.position(0);
GLES20.glVertexAttribPointer(secondFilterPositionAttribute, 2, GLES20.GL_FLOAT, false, 0, cubeBuffer);
GLES20.glEnableVertexAttribArray(secondFilterPositionAttribute);
textureBuffer.position(0);
GLES20.glVertexAttribPointer(secondFilterTextureCoordinateAttribute, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
GLES20.glEnableVertexAttribArray(secondFilterTextureCoordinateAttribute); //same as line from init
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDisableVertexAttribArray(secondFilterPositionAttribute);
GLES20.glDisableVertexAttribArray(secondFilterTextureCoordinateAttribute);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
I feel like I am missing a piece of the puzzle here. Again, I am very new to OpenGL, so any help, even conceptually is appreciated
What you want to achieve is called Render to Texture
A small tutorial how to do this with android can be found here:
http://blog.shayanjaved.com/2011/05/13/android-opengl-es-2-0-render-to-texture/
I have searched quite a bit and all the similar problems and solutions have not worked for me yet. So I hope someone will recognize my problem
Problem Description:
What I want to do is to swap with which texture I am rendering with.
When the application starts I load a texture and it renders fine.
Later I want to replace it and calls the same load function again thus generating a new texture id. When I render with the new texture id I encounter the problem of the model becoming completely black.
So if I run the code below the above happens:
Bitmap bmp = BitmapFactory.decodeResource(this.getResources(),
R.drawable.sphere_map1);
currentTexture = TextureHelper.loadTextureToOGL(bmp);
I get the problem with a completely black texture.
Here follow some of the important functions that I use to load a texture and draw my model
If anyone need additional code, let me know. As this is my first post feel free to do recommendations regarding the structure of the post as well.
Thanks on beforehand!
public static int loadTextureToOGL(Bitmap bitmap) {
int texId[] = { -1 };
GLES20.glGenTextures(1, texId, 0);
int textureId = texId[0];
glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, mMinFilter);
glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, mMaxFilter);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0, GL_RGBA, bitmap, 0);
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
GeneralUtil.printGLErrorInHex();
bitmap.recycle();
return textureId;
}
public void draw(float[] mvp) {
glUseProgram(mProgram);
glEnableVertexAttribArray(mPositionHandle);
glEnableVertexAttribArray(mNormalHandle);
glEnableVertexAttribArray(mTexCoordHandle);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, currentTexture);
glUniform1i(mEnvTex0Handle, 0);
glUniformMatrix4fv(mMVPHandle, 1, false, mvp, 0);
glUniform3f(mColorHandle, red, green, blue);
glUniform1f(mAttSpecularHandle, attSpecular);
glUniform1f(mAttDiffuseHandle, attDiffuse);
glUniform1f(mAttAmbientHandle, attAmbient);
GeneralUtil.printGLErrorInHex();
if (model != null) {
glDrawArrays(GL_TRIANGLES, 0, model.getmNFaces() * 3);
}
}
So I think I found my problem.
Instead of running the glTexImage call from the GL thread I ran it from the UI thread instead.
I did not realize that it created a GL thread on its own.
I'm new to OpenGL ES and developing a simple 2D game. However, I'm confused as to how I can go about loading multiple animation frames as textures (for the player character). I've tried to load a different image every time the character is rendered, but that's too slow.
Here is my texture loading code thus far:
public void loadGLTexture(GL10 gl, Context context) {
InputStream[] is=new InputStream[3];
is[0]= context.getResources().openRawResource(R.drawable.r1);
is[1]= context.getResources().openRawResource(R.drawable.r2);
is[2]= context.getResources().openRawResource(R.drawable.r3);
try {
bitmap[0]= BitmapFactory.decodeStream(is[0]);
bitmap[1]= BitmapFactory.decodeStream(is[1]);
bitmap[2]= BitmapFactory.decodeStream(is[2]);
} finally {
try {
is[0].close();
is[1].close();
is[2].close();
is = null;
} catch (IOException e) {
}
}
gl.glGenTextures(3, textures,0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[0], 0);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[1], 0);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[2], 0);
bitmap[0].recycle();
bitmap[1].recycle();
bitmap[2].recycle();
}
How can I make all three images accessible through an array?
You need to call glBindTexture before every texImage2D. Currently you are loading all three images into textures[0].
Don't try to load all textures at once. Change your function to load only one texture and just call it three times. You should be able to do:
textures[0]=loadGLTexture(GL10,context,R.drawable.r1);
textures[1]=loadGLTexture(GL10,context,R.drawable.r2);
textures[2]=loadGLTexture(GL10,context,R.drawable.r3);
You can place all the frames of animation on a single texture and use texture coordinates to select which one to use