34

I have been having trouble finding straightforward code to render a scene to a texture in OpenGL ES (specifically for the iPhone, if that matters). I am interested in knowing the following:

  1. How do you render a scene to a texture in OpenGL ES?
  2. What parameters must you use to create a texture that is capable of being a render target in OpenGL ES?
  3. Are there any implications with applying this rendered texture to other primitives?
Andrew Garrison
  • 6,977
  • 11
  • 50
  • 77

2 Answers2

44

This is how I'm doing it.

I define a texture variable (I use Apple's Texture2D class, but you can use an OpenGL texture id if you want), and a frame buffer:

Texture2d * texture;
GLuint textureFrameBuffer;

Then at some point, I create the texture, frame buffer and attach the renderbuffer. This you only need to do it once:

texture = [[Texture2D alloc] initWithData:0 
                             pixelFormat:kTexture2DPixelFormat_RGB888
                             pixelsWide:32
                             pixelsHigh:32
                             contentSize:CGSizeMake(width, height)];

// create framebuffer
glGenFramebuffersOES(1, &textureFrameBuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFrameBuffer);

// attach renderbuffer
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, texture.name, 0);

// unbind frame buffer
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

Every time I want to render to the texture, I do:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFrameBuffer);

...
// GL commands
...

glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

About your question 3, that's it, you can use the texture as if it is any other texture.

Marco Mustapic
  • 3,879
  • 1
  • 21
  • 20
  • Marco, do you know how much overhead is involved in keeping a framebuffer and renderbuffer for each texture you need to render into? I have several large textures my app needs to draw into, and I'm currnetly attaching different ones to a single "extra" framebuffer using glFramebufferTexture2DOES. Would keeping a separate framebuffer for each be better? – Ben Gotow Jun 21 '09 at 20:12
  • Could you please edit your code so that initWithData:... call fits in without scrolling? – nornagon Jun 12 '10 at 00:08
  • 3
    Just small detail. You want to have glBindFramebufferOES(GL_FRAMEBUFFER_OES, 1); instead 0 (or even better store previous version and restore it afterward) if you are working on iphone to "unbind" your custom frame buffer. – Lope Nov 06 '10 at 14:59
  • 3
    Binding to Framebuffer Id 0 isn't always the Id of the one intialized as your primary framebuffer of the GL session. My ES2 session definitely stores a different one. I advise people to reference the one given upon the creation of your GL session. – Kalen Jan 26 '11 at 10:02
  • Any link to Apple's Texture2D file? It appears to not exist in their painting app anymore... – jjxtra May 31 '12 at 16:07
  • Never mind, it looks like Cocos2D Texture2D class will work fine – jjxtra May 31 '12 at 16:09
  • Just to add to what Kyle said, you can get the current frameBuffer name with: GLint currentFrameBuffer = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &currentFrameBuffer); – Bikush Jan 31 '13 at 10:53
  • Why the _OES extension? – jjxtra Jan 24 '15 at 16:52
11

To render the scene to a texture you must use a framebuffer associated with a texture. Here is a method that i created to simplify it :

void glGenTextureFromFramebuffer(GLuint *t, GLuint *f, GLsizei w, GLsizei h)
{
    glGenFramebuffers(1, f);
    glGenTextures(1, t);

    glBindFramebuffer(GL_FRAMEBUFFER, *f);

    glBindTexture(GL_TEXTURE_2D, *t);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *t, 0);

    GLuint depthbuffer;
    glGenRenderbuffers(1, &depthbuffer);    
    glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, w, h);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthbuffer);

    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if(status != GL_FRAMEBUFFER_COMPLETE)
        NSLog(@"Framebuffer status: %x", (int)status);
}

You can create the frame buffer and the texture easily :

GLuint _texture, _framebuffer;
GLsizei w,h;
float scale = [UIScreen mainScreen].scale;
w = self.view.bounds.size.width * scale;
h = self.view.bounds.size.height * scale;
glGenTextureFromFramebuffer(&_texture, &_framebuffer, w, h);

You can later use _framebuffer to render the scene into _texture in your draw method :

glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
//draw here the content you want in the texture
//_texture is now a texture with the drawn content

//bind the base framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//or if you use GLKit
[view bindDrawable];
//draw normaly

Now you can do what you want with the texture. If you want to do some post processing (blur, bloom, shadow, etc...) you can !

Anthony
  • 255
  • 3
  • 7
  • Hello, where did you get `_renderToTextureBuffer` from? – The Way Nov 26 '13 at 10:47
  • Cool, that's what I was thinking. Do you have this in a working example? I will try to add this to my code (again), but im not to sure about how to draw into the texture `//draw here the content you want in the texture` Can I do that with VBOs and `glDrawArrays()`? – The Way Nov 29 '13 at 19:02