Automatic cubemaps via PBuffers

Sage
Posts: 1,199
Joined: 2004.10
Post: #1
I've been poking around with PBuffers. I can't say I've got a 100% grokking of them, but I can successfully render to a texture and use it later.

Now, what I'd like to do is, right after the level loads ( but before I start rendering ) I would render the six cubemap images via a camera at the center of the stage, facing in each of the directions. In and of itself, this seems pretty straightforward. I'd get six textures. But the thing is, I'm not certain how I'd get those six textures *into* one cubemap texture.

Can anybody give me some pointers, or show me some sample code? Frankly, I don't know where to start.

Also, this doesn't need to be fast. I'm not trying to get genuine realtime reflections; I'm only doing it once, when the level's initialized. My intent is to up the LOD bias and basically get some sort of cheap ambient/radiosity like coloration on certain objects.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #2
You can make a cube map PBuffer, specially for this purpose. CGLSetPBuffer has a "face" parameter for you to choose which face of the cube is rendered to.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #3
Aha! Thank you! hopefully I wont have to pester for more help Wink
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #4
This brings up another question. Sorry. Is it possible to generate mipmaps for PBuffers?

This is relevant since I intend to up the lod bias on them, but it doesn't look like I can call the nice gluBuild2DMipmaps function on the PBuffer's texture.

EDIT: Reading the docs I see that I can't mipmap when using GL_TEXTURE_RECTANGLE_EXT pbuffer texture targets. I guess that makes sense. I can still likely mipmap for cubemaps, since those'll be square POT textures.

At the moment I was trying to perform a crude fullscreen blur effect ( since a proper blur on my 5200 is so damn slow ) by upping the lod bias on a full-window rectangular pbuffer texture.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #5
I've found the only way to get automatic mipmap generation is to CopyTex(Sub)Image2D from the pbuffer into another texture which has GENERATE_MIPMAP_SGIS turned on.

EXT_framebuffer_object has a cleaner way to manage this, with an explicit GenerateMipmapsEXT function you can call.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #6
Perhaps this is an argument to drop Panther support. After all, I'm really just writing this to have fun.

Do you have an references/sample code for FBO using CGL?

( I appreciate your help, btw )

EDIT: What I'm doing at the moment is implementing a chainable full-screen effect API. So I can just push and pop effects while the game runs. In the screenshot below I've added a wobbly water effect ( via permutation of tex coords on a gid ) and over that, a per-fragment desaturator. I'm looking to implement a blur, too. But my 5200 is impossibly slow when it comes to even a simple median filter, much less a gaussian. So I was thinking I could use lod bias to fake it...

[Image: grey-wobbly.png]
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #7
EXT_framebuffer_object doesn't have any window-system interactions, so any sample code you find will be directly relevant. The spec has some good examples, IIRC.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #8
I've read a fair amount of the spec for FBO, and implemented a rudimentary version sans mipmaps.

Now, reading the spec, it gives an example for automatic mipmap generation which involves rendering to texture once per mipmap level. It requires N FBOs, one for each mipmap level.

That seems kind of harsh, am I interpreting it correctly?

From the horse's mouth:
Code:
(4) Render-to-texture loop with automatic mipmap generation.  There
    are N framebuffers, N mipmap color textures, and a single shared
    depth renderbuffer.  The depth renderbuffer is not a mipmap.

        GLuint fb_array[N];
        GLuint color_tex_array[N];
        GLuint depth_rb;

        glGenFramebuffersEXT(N, fb_array);
        glGenTextures(N, color_tex_array);
        glGenRenderbuffersEXT(1, &depth_rb);

        // initialize color textures
        for (int i=0; i<N; i++) {
          glBindTexture(GL_TEXTURE_2D, color_tex_array[N]);
          glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 512, 512, 0,
                       GL_RGB, GL_INT, NULL);

          // establish a mipmap chain for the texture
          glGenerateMipmapEXT(GL_TEXTURE_2D);
        }

        // initialize depth renderbuffer
        glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);
        glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
                                 GL_DEPTH_COMPONENT24, 512, 512);

        // setup framebuffers, sharing depth
        for (int i=0; i<N; i++) {
          glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb_array[i]);
          glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
                                    GL_COLOR_ATTACHMENT0_EXT,
                                    GL_TEXTURE_2D, color_tex_array[i], 0);
          glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
                                       GL_DEPTH_ATTACHMENT_EXT,
                                       GL_RENDERBUFFER_EXT, depth_rb);
        }

        // Check framebuffer completeness at the end of initialization.
        CHECK_FRAMEBUFFER_STATUS();

        loop {
            glBindTexture(GL_TEXTURE_2D, 0);

            for (int i=0; i<N; i++) {
              glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb_array[i]);
              <draw to texture i>
            }

            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

            // automatically generate mipmaps
            for (int i=0; i<N; i++) {
              glBindTexture(GL_TEXTURE_2D, color_tex_array[i]);
              glGenerateMipmapEXT(GL_TEXTURE_2D);
            }

            <draw to the window, reading from the color textures>
        }

And, one more thing. Do I have to do any jiggery pokery to make this work with GL_TEXTURE_RECTANGLE_EXT?
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #9
TomorrowPlusX Wrote:am I interpreting it correctly?
No. If you are rendering to texture via FBO, the minimum you need is one FBO and one mipmap level attached to it.

But, you don't even need to have an FBO bound to use glGenerateMipmapEXT. As long as the renderer exports GL_EXT_framebuffer_object, you can use it on any old texture object at any time. See issue (52).

Quote:And, one more thing. Do I have to do any jiggery pokery to make this work with GL_TEXTURE_RECTANGLE_EXT?
Rectangle targets do not support mipmaps, period.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #10
arekkusu Wrote:But, you don't even need to have an FBO bound to use glGenerateMipmapEXT. As long as the renderer exports GL_EXT_framebuffer_object, you can use it on any old texture object at any time. See issue (52).

OK, tell me then if I'm interpreting this correctly ( pseudocode ):

Code:
//Setup

glGenFramebuffersEXT( 1, &_frameBufferID );
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _frameBufferID );

glGenTextures( 1, &_textureID );
glBindTexture( GL_TEXTURE_2D, _textureID);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, _width, _height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0 );

glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _textureID, 0 );
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

...

//Now, to draw whatever we're drawing into the FBO
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _frameBufferID );

... DRAW

// unbind FBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

...

//Now, generate mipmaps from what I drew
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, _textureID );
glGenerateMipmapsEXT( GL_TEXTURE_2D );

arekkusu Wrote:Rectangle targets do not support mipmaps, period.

Good to know! Thanks,
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #11
For what it's worth, I gave a stab following this approach; but with no luck.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #12
OK, this is really beginning to cheese me off. I've been googling, reading specs, reading PDFs, bangining my head against the wall. Everything I've read says that glGenerateMipmapsEXT should simply work. And yet, I can't make it work.

Can somebody download this and see what I'm doing wrong? http://zakariya.net/shamyl/etc/RenderTexture.zip

This is basically just a playground app for render texture, since I'm new to the concept. Disregard PBuffer.h/cpp since I'll toss that. What matters is:

src/RenderTexture/FrameBufferObject.h/.cpp
src/RTEffectChain/Effects/MipmapLevelRTEffect.h/.cpp

The MipmapLevelRTEffect is just capturing the input into a POT GL_TEXTURE_2D via an FBO, calling glGenerateMipmaps on the bound texture, and drawing it.

Nothing's happening; Or, more specifically, I'm getting mipmap level 0.

Please help me!
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #13
That project doesn't build for me (C++ link errors) but looking at FrameBufferObject::FrameBufferObject there are a few problems (in /* */ below):

Code:
    glGenFramebuffersEXT( 1, &_frameBufferID );
    glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, _frameBufferID );
    
    glGenTextures( 1, &_textureID );
    glBindTexture( _target, _textureID);
    glTexImage2D( _target, 0, GL_RGB, _width, _height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0 );

    glTexParameteri( _target, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
/* this is not a valid MAG filter... for any target! */

    glTexParameteri( _target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
    if ( _target != GL_TEXTURE_RECTANGLE_EXT ) glGenerateMipmapEXT( _target );
/* you haven't drawn anything at this point, so you're generating mipmaps from whatever garbage the driver decided to allocate for you.
Remember, unlike the SGI generate_mipmap texture parameter, the FBO glGenerateMipmapEXT function generates mipmaps [i]when you call it[/i], not when you later modify the base level. */

    glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _textureID, 0 );
/* the textarget here should be a cubemap face, not 2D, if you are doing dynamic cubemaps. */

Your order of ops should be something like:

1) gen FBO
2) gen cubemap texture, submit initial base level for each face (can be null pointers)
3) attach a cubemap face to color attachment 0, optionally attach depth renderbuffer
4) make sure the cubemap texture is NOT bound, and draw something (you can't render to a texture with itself...)
5) repeat steps 3-4 for each cubemap face. At this point, the cubemap texture is complete only if mipmap filtering is not being used.
6) bind FBO zero to return to normal framebuffer.
7) bind the cubemap
8) call glGenerateMipmapEXT on GL_TEXTURE_CUBE_MAP to generate mipmaps for all faces. Now the texture is complete with mipmap filtering enabled.
9 use mipmap-filtered cubemap

That's a rough outline... hope it helps Wink
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #14
Whoo! It works!

Thanks arrekusu; I really appreciate it. Admittedly, the GL_TEXTURE_MAG_FILTER thing... well, that's a mistake I keep on making because I guess I'm typing too quickly. But I didn't realize I had to submit *blank* textures for each level. I think that was my primary disconnect.

Awesome!

EDIT: Out of curiosity, what linker errors did you encounter? I built this on my laptop g4, running 10.4.4, and also on my desktop g5, also running 10.4.4...

EDIT2: I bet you're on intel. The static libs linked against are PPC.
Quote this message in a reply
Sage
Posts: 1,232
Joined: 2002.10
Post: #15
TomorrowPlusX Wrote:the GL_TEXTURE_MAG_FILTER thing... well, that's a mistake I keep on making because I guess I'm typing too quickly.
glGetError() is your friend Wink

Quote:I didn't realize I had to submit *blank* textures for each level.
You don't have to submit blanks, but you have to submit something, so the dimensions and internal format of the texture are known before trying to render into it. Checking framebuffer completeness will help here.

For example, if you had a contrived situation where you knew you you only needed to update one face of a cube map, you could submit the static data for five faces. Then submit null (or real data) for the sixth face and render just to it.

Most of the time though, since you're going to replace the entire texture contents with rendering, it is best to use a null pointer (no useless data transmitted across bus.)

Quote:Out of curiosity, what linker errors did you encounter?
I was on my dev system, which has who knows what on it. I tried again with a G4 PowerBook, 10.4.4, XCode 2.2, and it worked fine.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Best strategy for rendering into cubemaps? TomorrowPlusX 7 4,873 Dec 6, 2009 11:53 AM
Last Post: TomorrowPlusX
  PBuffers and setTextureImageToPBuffer:colorBuffer: Marjock 4 2,799 Jul 16, 2006 01:19 AM
Last Post: Marjock
  cubemaps with shaders akb825 3 3,201 Apr 5, 2006 06:24 PM
Last Post: akb825
  Displaying my cubemaps in world space, rather than eye TomorrowPlusX 1 2,826 Jan 26, 2006 04:29 AM
Last Post: TomorrowPlusX
  Seams showing on my cubemaps TomorrowPlusX 4 4,073 Dec 14, 2005 12:56 PM
Last Post: TomorrowPlusX