Tips/tricks related to Computer Graphics, GPUs and other programming

NOTE: This tutorial builds up on my previous tutorial on how to setup OpenGL ES 2.0 on Android.

———————————————————————————————————-
I’ll first link to the apk, source code and the repository:
apk here.
Source code here.
Google Code Repository  here. NOTE: Branch is “renderToTex”
———————————————————————————————————–

Rendering to a texture is important when it comes to various graphics techniques and algorithms (Shadow Mapping, cube map generation, Deferred Shading, etc.) So this quick tutorial will demonstrate how to setup a texture for rendering (and then how to display that texture on screen).

What is rendered to the texture?
We are going to render the same objects as in the previous tutorial (where you can choose between gouraud/phong/normal mapping shaders). The texture is going to be a simple quad which will then be displayed on the full screen. I have a Samsung Captivate(Samsung Galaxy S) which has a screen resolution of 800 X 480, so I use those values as the height and width of my texture. Adjust accordingly to your phone (I was a tad lazy to read in the values from the system itself). Let’s begin:

    1. New Variable declarations:

The variables below are for creating the quad to render the texture onto:

// the full-screen quad buffers
final float x = 10.0f;
final float y = 15.0f;
final float z = 2.0f;
// vertex information - clockwise
                       // x,  y, z, nx, ny, nz, u, v
final float _quadv[] = { -x, -y, z, 0, 0, -1, 0, 0,
			-x,  y, z, 0, 0, -1, 0, 1,
			 x,  y, z, 0, 0, -1, 1, 1,
			 x, -y, z, 0, 0, -1, 1, 0
			};

private FloatBuffer _qvb;
// index
final int _quadi[] = { 0, 1, 2,
		      2, 3, 0 };
private IntBuffer _qib;

Variables used for rendering to texture are declared below:

// RENDER TO TEXTURE VARIABLES
int[] fb, depthRb, renderTex; // the framebuffer, the renderbuffer and the texture to render
int texW = 480 * 2;           // the texture's width
int texH = 800 * 2;           // the texture's height
IntBuffer texBuffer;          //  Buffer to store the texture
    1. Setup Render To Texture:

The method setupRenderToTexture() (Hopefully the comments give enough detail):

// create the ints for the framebuffer, depth render buffer and texture
fb = new int[1];
depthRb = new int[1];
renderTex = new int[1];

// generate
GLES20.glGenFramebuffers(1, fb, 0);
GLES20.glGenRenderbuffers(1, depthRb, 0); // the depth buffer
GLES20.glGenTextures(1, renderTex, 0);

// generate texture
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTex[0]);

// parameters - we have to make sure we clamp the textures to the edges
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
		GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
		GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
		GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
		GLES20.GL_LINEAR);

// create it
// create an empty intbuffer first
int[] buf = new int[texW * texH];
texBuffer = ByteBuffer.allocateDirect(buf.length
		* FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asIntBuffer();;

// generate the textures
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, texW, texH, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, texBuffer);

// create render buffer and bind 16-bit depth buffer
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthRb[0]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, texW, texH);
    1. Rendering:

The rendering begins in the onDrawFrame() method. First we render to texture:

// viewport should match texture size
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 0.5f, 10);
GLES20.glViewport(0, 0, this.texW, this.texH);

// Bind the framebuffer
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb[0]);

// specify texture as color attachment
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTex[0], 0);

// attach render buffer as depth buffer
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, depthRb[0]);

// check status
int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
if (status != GLES20.GL_FRAMEBUFFER_COMPLETE)
	return false;

// Clear the texture (buffer) and then render as usual...
GLES20.glClearColor(.0f, .0f, .0f, 1.0f);
GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

....usual rendering continues....

The final step is to render the quad itself to cover the screen fully (this is done using the gouraud shader):

// Bind the default framebuffer (to render to the screen) - indicated by '0'
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

GLES20.glClearColor(.0f, .0f, .0f, 1.0f);
GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

...setup all the matrices as usual....

// send the vertex coordinates
_qvb.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
GLES20.glVertexAttribPointer(GLES20.glGetAttribLocation(_program, "aPosition"), 3, GLES20.GL_FLOAT, false,
		TRIANGLE_VERTICES_DATA_STRIDE_BYTES, _qvb);
GLES20.glEnableVertexAttribArray(GLES20.glGetAttribLocation(_program, "aPosition"));

// the normal info (though shouldn't be needed)
_qvb.position(TRIANGLE_VERTICES_DATA_NOR_OFFSET);
GLES20.glVertexAttribPointer(GLES20.glGetAttribLocation(_program, "aNormal"), 3, GLES20.GL_FLOAT, false,
		TRIANGLE_VERTICES_DATA_STRIDE_BYTES, _qvb);
GLES20.glEnableVertexAttribArray(GLES20.glGetAttribLocation(_program, "aNormal"));

// bind the framebuffer texture - this is what will be attached to the quad
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTex[0]);
GLES20.glUniform1i(GLES20.glGetUniformLocation(_program, "texture1"), 0);

// enable texturing? [fix - sending float is waste]
GLES20.glUniform1f(GLES20.glGetUniformLocation(_program, "hasTexture"), 1.0f);

// texture coordinates
_qvb.position(TRIANGLE_VERTICES_DATA_TEX_OFFSET);
GLES20.glVertexAttribPointer(GLES20.glGetAttribLocation(_program, "textureCoord"), 2, GLES20.GL_FLOAT, false,
		TRIANGLE_VERTICES_DATA_STRIDE_BYTES, _qvb);
GLES20.glEnableVertexAttribArray(GLES20.glGetAttribLocation(_program, "textureCoord"));//GLES20.glEnableVertexAttribArray(shader.maTextureHandle);

// Draw with indices
GLES20.glDrawElements(GLES20.GL_TRIANGLES, _quadi.length, GLES20.GL_UNSIGNED_INT, _qib);

And that’s it! Hopefully all the comments in the code snippets above explain everything. The quad and texture sizes are something I just fiddled around with, so adjust accordingly to the device you are testing on.

If you have any questions, suggestions or comments please let me know.

———————————————————————————————————-
Links to the apk, source code and the repository:
apk here.
Source code here.
Google Code Repository  here. NOTE: Branch is “renderToTex”
———————————————————————————————————–

Advertisements

Comments on: "Android OpenGL ES 2.0 – Render To Texture" (31)

  1. This is great stuff, thanks a lot. I am wondering… do you see a massive performance drop as well? I’m basically going from 80fps to 45fps when rendering to a texture.

    • Are you talking about my example? Yes there is a big framerate drop – mainly because the texture I’m rendering to is twice the size of the screen (screen size is 800 X 480). It was just a test case – usually when render to texture is done you use much smaller sizes (in the case of shadow mapping and other techniques for example).

  2. it is really bad idea to give final lecture’s source code. When you expect to deal with something relatively simple you just lost yourself in the source code!

  3. Hi, Thanks for excellent tutorial, i have one question though. Why does result gets smaller if i render to texture and how can i get exact size result while rendering to texture ?

    • Not sure what you mean – I guess it depends on the size of the quad you are rendering to and the distance between the camera and the quad.

  4. Hi,

    I saw a post about your app on this forum:

    http://forums.arm.com/index.php?/topic/15265-fbo-doesnt-work/

    I did some investigation and got it working on the Galaxy SII. Please see the thread for details but basically the dot() function musn’t be overridden in the normalmap shaders, glDrawElements() must only be passed GL_UNSIGNED_SHORT and the related quad index buffers must be changed from int buffers to short buffers.

    HTH, Pete

    • Thanks for your comment! I actually saw that forum post yesterday. It’s good to know that you fixed the code so that it now runs on Galaxy S2 also. I remember I was having issues with GL_UNSIGNED_INT/SHORT on my Galaxy S Captivate, but I will try to fix it soon.

      Thanks again

  5. Thank you so much for this example. Very helpful!

    Are you seeing a huge performance hits simply by using the glBindRenderBuffer() line? I got the code working, and then I tried simply running it without any draw calls, and narrowed down a framerate hit to this line. I’m dropping from 60fps to 38fps on my HTC Incredible. And I think the 60 was just a limit from the display, so the hit might be even worse than it looks.

    What factors could be making glBindRenderBuffer() really slow?

  6. […] using the Frame Buffer Object and render it on top of the current frame. So seeing some nice tutorials on how to do something like that I ended up with this code which basically **render my scene on the […]

  7. […] using the Frame Buffer Object and render it on top of the current frame. So seeing some nice tutorials on how to do something like that I ended up with this code which basically **render my scene on the […]

  8. Imaging you only have one texture and u are changing the texture color when the user touches the screen. How could I switch the original texture to the changed one(render[0]) so when the user touches the screen again the changes are not lost …

    I tried switching in regularRender the original tex with renderTex[0] and I got a very weird behavior ..

    Anyone help would be appreciate it

  9. Imagine that when the user touches the screen the shader changes the texture color. How could you switch the original texture to the changed tex (renderTex[0]) so the changes made in the last touch dont get lost ?
    In other words how could I keep rendering the “result” texture as the original

    • you could just have a simple flag which changes on the first touch:

      in the class:
      boolean ifTouched = false;

      in the onTouch() function:
      if (!onTouch) {
      onTouch = true;
      changeTextureFunction();
      }

      I think that’s what you are looking for?

  10. sjaay thanks for ur replay. Im pretty much a newbie to opengl

    mTextureId being the original texture read from a bitmap
    renderTex[0] being the texture attached to the fbo

    u mead something like this:

    if(!screenTouched)
    {
    GLES20.glBindTexture ( GLES20.GL_TEXTURE_2D, mTextureId );
    }else
    {
    GLES20.glBindTexture ( GLES20.GL_TEXTURE_2D, renderTex[0]);
    }

    First of all when I render renderTex[0] I dont know why it appears upside down and when I touch the screen both the upside down and the normal texture are rendered and the screen is flickering

    Maybe I should unbind the mTextureId?

    Thanks

    • Sorry I posted instead of reply.

      from your previous comment, if u could give some sample code for that

      changeTextureFunction(); you wrote I would really appreciate it. Thanks

    • yes you should unbind the previous texture. that should be all there is to it

  11. As a follow-up to rendering-to-texture, do you know if this can be extended to render a simple static OpenGL ES drawing to an Android bitmap format (for being displayed in an Android framework bitmap control)

    Gingerbread doesn’t seem to support scrolling a SurfaceView within a ScrollView and so I’m trying to figure out if it will be possible to render OpenGL ES output into a bitmap and place the bitmap in the ScrollView

  12. Thanks for sharing.
    Do you know of a resource on the web that accomplishes render-to-texture using NDK?
    Also, shadow mapping code often renders to a depth-texture, which has no R/G/B/A, just Z.

  13. Hi.
    The only commands you need when rendering to texture is

    // Bind the framebuffer
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb[0]);

    // check status
    int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
    if (status != GLES20.GL_FRAMEBUFFER_COMPLETE)
    return false;

    // Clear the texture (buffer) and then render as usual…
    GLES20.glClearColor(.0f, .0f, .0f, 1.0f);
    GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT

    and when switching back to default

    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,0);

    this helps with fps , but still drops down >10 fps

  14. Your int[] buf seems to be totally useless!

  15. namename said:

    Also if anyone is looking for a way to render android’s Views to OpenGL texture here is a open source sample project:
    https://github.com/ArtemBogush/AndroidViewToGLRendering

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: