High resolution Screenshot

Hi,

I want to capture an OpenGL scene (displayed in GLCanvas) as image. The scene is displayed in the GUI with a resolution betwenn 200x200 and 800x800, but I want a capture with resolution of, say, 2000x1500.

Jogl provides a class ScreenShot. This class seems to operate in the current gl context and reads backs pixels and thus does not allow capturing in the desired resolution of 2000x1500 I suppose?

So how can I proceed with that issue?
Are there any further utility classes?
Or has anybody done that before?

Thx in advance and best regards

Martin

Over to you Ken…

Why not just render it at 2000x1500?

The size of the framebuffer is limited. You have to render tiles and stick them together afterwards.

@Chris:

Okay, say, we are not limited by the resolution and an image with exactly 800x600 will be enough (in that case, TileRenderer will be not neccessary, as far as I have understood )

How would you proceed?
Especially since the canvas displaying the scene in the GUI will probably have a different resolution than 800x600. So Screenshot is not appropiate for that problem.

Example code?

Best regards

Martin

  1. i would set up a framebuffer at 2048 by 2048.
  2. bind it.
  3. set viewport to 2000x1500.
  4. render the scene.
  5. take screenshot using jogl-screenshot-utility.
  6. load the image and crop the overlapping 48 by 548 pixels.
  7. save the image again and enjoy.

@emzic (you get a :-* , or does your name mean something else ?) ;D

Could you please describe in detail point 1 to 3: how to setup a framebuffer, how to bind and set the viewport?

1:
I mean, where do I get the framebuffer from? Should I take GLCanvas for that or is there a different way to setup a framebuffer?

2: Bind it? What exactly do you mean?

Thanks!!!

Or you could skip the manual crop step by using the screenshot functions with the x and y offsets :slight_smile:

He is probably referring to a framebuffer object (FBO), though you would get the same effect by simply resizing the GLCanvas container object to the desired screenshot size. And actually if using an FBO I’d just set it to be the desired size of your screen shot and not worry about viewport or cropping.

The FBO approach would probably be preferred in the event you have end-users, as all rendering takes place off screen. This way the user won’t see their GUI/window become strangely distorted for a frame each time they take a screen shot with the resizing method.

Okay that sounds fine!

Since I’m not an export with FBO:
How would I setup one? Which classes / method / openGL functions should I use? Is there some example code around?

Thanks in advance

Martin

here is a short but good tutorial on FBOs
http://www.gamedev.net/reference/articles/article2331.asp

chris, yes taking the screenshot at the right resolution in the first place is probably even better.
but i dont know if it is possible to resize the canvas to a resolution that is larger than the monitor.

I can pretty much guarantee that using the tile rendering utilities that are already in jogl will be the easiest option. Plus the only limit on resolution is the amount of space left on your hard drive.

@emciz: I thought again about your approach. Why should one use a FBO if one takes a “standard” screenshot then in step 5? Or did you refer to standard framebuffer and not to a FBO?

@bleb: TileRenderer will be fine, but I wonder a bit, how the projection matrix is set up. What if a custom “projection matrix” is used, which is not generated by calls to either glFrustrum or glOrtho?

@all: How can I setup a standard framebuffer (not an FBO), on which glReadPixels (in class Screenshot) can be applied?
Can I create such a standard framebuffer with a GLCanvas, but without actually displaying the canvas? What happens, if one renders to a GLCanvas, which is actually not visible? Will that fill the framebuffer though?

Best regards again

Martin

It’s been a while since I’ve read the spec but I believe glReadPixels reads from the currently bound framebuffer object.

Look at the javadoc for the com.sun.opengl.util.TileRenderer class. You will need to be able to phrase your projection matrix setup in terms of a glPerspective, glOrtho or glFrustum call, which you then rewrite as a call to trPerspective, trOrtho or trFrustum. Arbitrary projection matrices are not supported. Once that is done, however, doing the actual render is very easy. See demos.misc.TiledRendering in the jogl-demos workspace.

First thanks to all for your help!

Can anybody provide a short example which uses FBO instead of PBuffer?

I have used the following code (which at least works, but I doubt in the strict correctness of what I did).
Please let me know your comments and remarks. What about opengl context and make current? Is it okay to use PBuffer the way I did?

If PBuffer is the current drawable, it seems, that reshape(…) of GLEventListener is never called. That’s why I had to setup matrizes in display(…) …

Perform screenshot


        // called from within AWT thread
        if (!GLDrawableFactory.getFactory().canCreateGLPbuffer()) {
            System.out.println("Requires pbuffer support for screen shot");
        } else {
            GLCapabilities glcaps = new GLCapabilities();
            glcaps.setDoubleBuffered(false);
            int width = 1000;
            int height = 1000;
            GLPbuffer pBuffer = GLDrawableFactory.getFactory().createGLPbuffer(glcaps, null, width, height, null);
            scene.registerScreenshotDrawable( pBuffer );
            pBuffer.addGLEventListener( scene );
            pBuffer.display();
            BufferedImage bi = scene.getScreenshotImage();
            File file = new File("test.png");
            try {
                if (!ImageIO.write( bi, FileUtil.getFileSuffix(file), file)) {
                    System.err.println("Error writing file using ImageIO (unsupported file format?)");
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

The Scene class implements GLEventListener.


public class Scene
extends Object 
implements GLEventListener, MouseListener, MouseMotionListener, MouseWheelListener 
{
    [....]
    protected GLAutoDrawable screenshotDrawable;
    protected BufferedImage screenShotImage;

    public void registerScreenshotDrawable(GLAutoDrawable pBuffer) {
        screenshotDrawable = pBuffer;
    }
    
    public BufferedImage getScreenshotImage() {
        return screenShotImage;
    }

    public void init(GLAutoDrawable drawable) {
        // setup lightning
        // enable opengl features
    }

    public void display(GLAutoDrawable drawable) {
        consumeAllMouseEvents();
        gl = drawable.getGL();
        
        if (drawable == screenshotDrawable) {
            float ratio = (float)1000 / (float)1000;

            gl.glMatrixMode(GL.GL_PROJECTION);
            gl.glLoadIdentity();
            gl.glFrustum(-1.0f, 1.0f, -ratio, ratio, 5.0f, 1000.0f);

            gl.glMatrixMode(GL.GL_MODELVIEW);
            gl.glLoadIdentity();
        }

        if (mode == GL.GL_RENDER) {
            if ((drawable instanceof GLJPanel) &&
                !((GLJPanel) drawable).isOpaque() &&
                ((GLJPanel) drawable).shouldPreserveColorBufferIfTranslucent()) {
               gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
            } else {
               gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
            }

            gl.glPushMatrix();
            gl.glLoadMatrixd( orbitRotation.getCameraMatrix(), 0 );
            renderObjects();
            gl.glPopMatrix();
        } 

        if (drawable == screenshotDrawable) {
            System.out.println("This is screenshot");
            screenShotImage = Screenshot.readToBufferedImage(1000, 1000, false);
        }
    }

    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        gl = drawable.getGL();

        ratio = (float)height / (float)width;
        
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glFrustum(-1.0f, 1.0f, -ratio, ratio, 5.0f, 1000.0f);

        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
        // not implemented
    }
}

Will also try to use TileRenderer for bigger resolution images.