Offscreen FBO on iPhone - iphone

I'm working on an iPhone 4, iOS 6.0.1. I'm creating an offscreen FBO with a different size than the main FBO. I use the following code to do that, where cxFBO and cyFBO are twice the size of the main FBO:
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, cxFBO, cyFBO);
glGenFramebuffers(1, &_frameBufferID);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferID);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer);
glGenTextures(1, &_fboTextureID);
glBindTexture(GL_TEXTURE_2D, _fboTextureID);
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_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cxFBO, cyFBO, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _fboTextureID, 0);
I'm then drawing to it after calling glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferID). I don't want to post the entire drawing routine with shaders. I think I'm not doing anything special, but if you think it matters, I will post a short example. The problem is that only one quarter of the texture is filled with vertices, the other 3 quarters are the color set by glClearColor().
What am I doing wrong? I'm especially a bit confused about the formats, i.e. GL_RGBA. Should I rather use GL_RGBA_OES? Everywhere?

The problem was that I forgot to call glViewPort(). Kind of obvious if you think about it ;-)

Related

Rendering to non-power-of-two texture on iPhone

Is it possible to render to texture with OpenGL ES 1.1 on the iPhone (2G and older)? If I bind a texture as a render buffer, it has to be the size of the render buffer, which isn't POT-sized. But OpenGL ES 1.1 requires textures to be POT.
Maybe it can't be done on ES 1.1?
While OpenGL ES 1.1 does not support non-power-of-two textures, newer iOS device models have the extension GL_APPLE_texture_2D_limited_npot, which states:
Conventional OpenGL ES 1.X texturing
is limited to images with power-of-two
(POT) dimensions.
APPLE_texture_2D_limited_npot
extension relaxes these size
restrictions for 2D textures. The
restrictions remain in place for cube
map and 3D textures, if supported.
There is no additional procedural or
enumerant API introduced by this
extension except that an
implementation which exports the
extension string will allow an
application to pass in 2D texture
dimensions that may or may not be a
power of two.
In the absence of OES_texture_npot,
which lifts these restrictions,
neither mipmapping nor wrap modes
other than CLAMP_TO_EDGE are supported
in conjunction with NPOT 2D textures.
A NPOT 2D texture with a wrap mode
that is not CLAMP_TO_EDGE or a
minfilter that is not NEAREST or
LINEAR is considered incomplete. If
such a texture is bound to a texture
unit, it is as if texture mapping
were disabled for that texture unit.
You can use the following code to determine if this extension is supported on your device (drawn from Philip Rideout's excellent iPhone 3D Programming book):
const char* extensions = (char*) glGetString(GL_EXTENSIONS);
bool npot = strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != 0;
On these devices, you should then be able to use non-power-of-two textures as long as you set the proper texture wrapping:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Unfortunately, this example application that I have which renders to a non-power-of-two texture uses OpenGL ES 2.0, so I'm not sure that will help you in this case.
It can be done, all you need to do is get the next power of 2 bigger than the non-POT.
Then generate a framebuffer:
GLuint aFramebuffer;
glGenFramebuffersOES(1, &aFramebuffer);
And a texture:
GLuint aTexturebuffer;
glGenTextures(1, &aTexturebuffer);
Then you do the same texture like things:
glBindTexture(GL_TEXTURE_2D, aTexturebuffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glColor4ub(0, 0, 0, 255);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
int area[] = {0.0, 0.0, renderWidth, renderHeight};
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, area);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, aFramebuffer);
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, aTexturebuffer, 0);
Here I used the draw_texture extension.
textureWidth and textureHeight are the power of 2 bigger, and renderWidth and renderHeight are the the rendere's width and height.
Then when you bind to aFramebuffer it will draw to texture.

Troubleshooting OpenGL ES 1.1 textures on the iPhone

I'm trying to draw a texture into an offscreen framebuffer, and its renderbuffer always ends up completely blank (black). The weird thing is, I know the context is set up, and I'm checking for errors using glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) and glGetError(), but neither function says anything is wrong. Are there any other error-checking functions I can call which might shed some light on what's happening?
Difficult to give you a precise answer without more information. Perhaps could you post some code about your setup and usage of the render buffer?
In the meantime, here is some info about how to properly setup an offscreen framebuffer:
// Remember the FBO being used for the display framebuffer
glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, (GLint *)&SystemFBO);
// Create the texture and the FBO for offscreen frame buffer
glGenTextures(1, &ResultTexture);
glBindTexture(GL_TEXTURE_2D, ResultTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glGenFramebuffersOES(1, &ResultFBO);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, ResultFBO);
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, ResultTexture, 0);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, ResultFBO);
// do your rendering to offscreen framebuffer
...
// restore original frame buffer object
glBindFramebufferOES(GL_FRAMEBUFFER_OES, SystemFBO);
// use ResultTexture as usual
glBindTexture(GL_TEXTURE_2D, ResultTexture);
Hope this helps...

OpenGL ES 2.0 FBO creation goes wrong with unknown error

I've been struggling with this for a while now, and this code crashes with, to me, unknown reasons. I'm creating an FBO, binding a texture, and then the very first glDrawArrays() crashes with a "EXC_BAD_ACCESS" on my iPhone Simulator.
Here's the code I use to create the FBO (and bind texture and...)
glGenFramebuffers(1, &lastFrameBuffer);
glGenRenderbuffers(1, &lastFrameDepthBuffer);
glGenTextures(1, &lastFrameTexture);
glBindTexture(GL_TEXTURE1, lastFrameTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 768, 1029, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_6_5, NULL);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//Bind/alloc depthbuf
glBindRenderbuffer(GL_RENDERBUFFER, lastFrameDepthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 768, 1029);
glBindFramebuffer(GL_FRAMEBUFFER, lastFrameBuffer);
//binding the texture to the FBO :D
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, lastFrameTexture, 0);
// attach the renderbuffer to depth attachment point
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, lastFrameDepthBuffer);
[self checkFramebufferStatus];
As you can see this takes part in an object, checkFrameBufferStatus looks like this:
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch(status)
{
case GL_FRAMEBUFFER_COMPLETE:
JNLogString(#"Framebuffer complete.");
return TRUE;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
JNLogString(#"[ERROR] Framebuffer incomplete: Attachment is NOT complete.");
return false;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
JNLogString(#"[ERROR] Framebuffer incomplete: No image is attached to FBO.");
return false;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
JNLogString(#"[ERROR] Framebuffer incomplete: Attached images have different dimensions.");
return false;
case GL_FRAMEBUFFER_UNSUPPORTED:
JNLogString(#"[ERROR] Unsupported by FBO implementation.");
return false;
default:
JNLogString(#"[ERROR] Unknown error.");
return false;
JNLogString is just an NSLog, and in this case it gives me:
2010-04-03 02:46:54.854 Bubbleeh[6634:207] ES2Renderer.m:372 [ERROR] Unknown error.
When I call it right there.
So, it crashes, and diagnostic tells me there's an unknown error and I'm kinda stuck. I basically copied the code from the OpenGL ES 2.0 Programming Guide...
What am I doing wrong?
glBindTexture(GL_TEXTURE1, lastFrameTexture);
That's not allowed, I was trying to bind the texture to unit one (GL_TEXTURE1), but that should be done by glActiveTexture(), not by glBindTexture(), which wants to know the type of the texture (GL_TEXTURE_2D, GL_TEXTURE_3D, etc.) not the texture unit. To place a texture in texture unit 1 I now have the following code which I think is correct:
//Bind 2D Texture lastFrameTexture to texture unit 1
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, lastFrameTexture);
Try using glGetError after each GL call. Specially the glTexImage2D.
If you use XCode, add breakpoint for OpenGL errors in "Breakpoint Navigator" ([+] -> [Add OpenGL ES Error Breakpoint]). Then all OpenGL problems will be showed on line, where problem appear.

Strange colors when loading some textures with openGL for iphone

I'm writting a 2d game in Iphone, which uses textures as sprites, I'm getting colored noise around some of the images I render ( but such noise never appears over the texture Itself, only in the transparent portion around It). The problem does not happen with the rest of my textures. This is the code I use to load textures:
- (void)loadTexture:(NSString*)nombre {
CGImageRef textureImage = [UIImage imageNamed:nombre].CGImage;
if (textureImage == nil) {
NSLog(#"Failed to load texture image");
return;
}
textureWidth = NextPowerOfTwo(CGImageGetWidth(textureImage));
textureHeight = NextPowerOfTwo(CGImageGetHeight(textureImage));
imageSizeX= CGImageGetWidth(textureImage);
imageSizeY= CGImageGetHeight(textureImage);
GLubyte *textureData = (GLubyte *)malloc(textureWidth * textureHeight * 4);
CGContextRef textureContext = CGBitmapContextCreate(textureData, textureWidth,textureHeight,8, textureWidth * 4,CGImageGetColorSpace(textureImage),kCGImageAlphaPremultipliedLast);
CGContextDrawImage(textureContext, CGRectMake(0.0, 0.0, (float)textureWidth, (float)textureHeight), textureImage);
CGContextRelease(textureContext);
glGenTextures(1, &textures[0]);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
free(textureData);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
Your memory is not zeroed out before you draw the image in it. The funny pixels you see is old data in the transparent regions of your image. Use calloc instead of malloc (calloc returns zeroed memory).
You can also use CGContextSetBlendMode(textureContext, kCGBlendModeCopy); before drawing the image.
If you want to know the whole story:
This problem exists only for small images due to the fact that malloc has different code paths for small allocation sizes. It returns a small block from a pool it manages in user space. If the requested size is larger than a certain threshold (16K, I believe), malloc gets the memory from the kernel. The new pages are of course zeroed out.
It took me a while to figure that out.

glError: 0x0501 when loading a large texture with OpenGL ES on the iPhone?

Here's the code I use to load a texture. image is a CGImageRef. After loading the image with this code, I eventually draw the image with glDrawArrays().
size_t imageW = CGImageGetWidth(image);
size_t imageH = CGImageGetHeight(image);
size_t picSize = pow2roundup((imageW > imageH) ? imageW : imageH);
GLubyte *textureData = (GLubyte *) malloc(picSize * picSize << 2);
CGContextRef imageContext = CGBitmapContextCreate( textureData, picSize, picSize, 8, picSize << 2, CGImageGetColorSpace(image), kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big );
if (imageContext != NULL) {
CGContextDrawImage(imageContext, CGRectMake(0.0, 0.0, (CGFloat)imageW, (CGFloat)imageH), image);
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
// when texture area is small, bilinear filter the closest mipmap
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// when texture area is large, bilinear filter the original
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// the texture wraps over at the edges (repeat)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picSize, picSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
GLenum err = glGetError();
if (err != GL_NO_ERROR)
NSLog(#"Error uploading texture. glError: 0x%04X", err);
CGContextRelease(imageContext);
}
free(textureData);
This seems to work fine when the image is 320x480, but it fails when the image is larger (for example, picSize = 2048).
Here's what I get in the Debugger Console:
Error uploading texture. glError: 0x0501
What's the meaning of this error? What's the best workaround?
Aren’t you simply hitting the maximum texture size limit? The error code is GL_INVALID_VALUE, see the glTexImage2D docs:
GL_INVALID_VALUE is generated if width
or height is less than 0 or greater
than 2 + GL_MAX_TEXTURE_SIZE, or if
either cannot be represented as
2k+2(border) for some integer value of
k.
iPhone does not support textures larger than 1024 pixels. The workaround is to split the image into several textures.
Maybe you are simply running out of memory - did you verify the value returned from malloc() is not NULL?
(this could explain getting 0x0501, which is GL_INVALID_VALUE).
I also experienced the same kind of issue with GLKitBaseEffect, it seemed to be a memory issue (as Hexagon proposed). To fix this, I had to add manual texture release calls:
GLuint name = self.texture.name;
glDeleteTextures(1, &name);
See this thread for more: Release textures (GLKTextureInfo objects) allocated by GLKTextureLoader.