I’ve given up on using Framebuffer objects (FBOs) in my app to save large versions of 3D views. There are too many limitations, and too many bugs.
Some limitations:
-
Not all macs support FBOs at all. You have to check for the FBO extension at runtime, and offer another option if they are not supported.
-
For those machines that do support FBOs, the size of the renderbuffer object you can attach varies greatly from machine to machine, so you can’t be sure how big an FBO you can create until runtime.
-
The max renderbuffer size that’s reported by the driver doesn’t work. At a little over 1/2 of the reported max size, the image you get back is black. At a little larger than that, it locks the machine up to the point where you have to do a forced shutdown. I could not figure out a reliable way to figure out what the threshold is at runtime.
-
Even when you request a renderbuffer that the system can handle, the machine goes to it’s knees while it is setting it up and rendering it, because it takes up so much of the system’s VRAM.
I’ve just gotten finished ripping out the FBO code in my app and implementing an approach that renders a series of tiles to the back buffer and assembles them into an image in main memory piece by piece. Thanks to Relic for pointing me to an open-source library that does pretty much what I needed.
Thanks too to Brian Paul, the author of that library, called “TR, the OpenGL Tile Rendering Library”. Seeing how he did it was very helpful.
I didn’t want to release my app as open source, so I studied the TR library and wrote my own code that does much the same thing.
The glReadPixels call is more powerful than I realized. If you use the calls:
glPixelStorei(GL_PACK_ROW_LENGTH, save_width);
glPixelStorei(GL_PACK_SKIP_ROWS, dest_y);
glPixelStorei(GL_PACK_SKIP_PIXELS, dest_x);
you can assemble image tiles from VRAM directly into the image data of an NSBitMapImageRep in main memory, without having to do any extra stitching.
You have to set up your NSBitMapImageRep so it doesn’t add any padding at the end of a row (by specifying bytesPerRow: as the exact count of bytes needed to store a row of pixels), and then specify a GL_PACK_ALIGNMENT of 1, but it works great.
I also had to give up on stretching the viewport larger than the dimensions of the window/renderbuffer in order to create a larger image. The viewport is silently clamped to a fairly small value, and your image gets distorted if you exceed that value.
My new code manipulates the projection frustum in order to create a large image. That’s actually pretty straightforward. You just set the left/right and top/bottom of the frustum to zoom in on the tile that you’re getting ready to render, using the same ratio of tile frustum/view frustum as the tile size/view size.
This might be old hat to most of you, but I spent quite a while pulling out my hair, figuring out how to make this work.
Anyway, I’m now able to reliably create HUGE 3D images and save them to disk as JPEGs or TIFFs.