offscreen rendering and taking screenshot

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

offscreen rendering and taking screenshot

c0d3x
Hi there,
i am trying to take a screenshoot by offscreen rendering to some fbo, but it does not work as i expect.
 i want to get this


but i got this


the code iam using is

package wigl.core.rendering;

import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GL2ES3;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.util.GLBuffers;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

import static com.jogamp.opengl.GL.*;
import static com.jogamp.opengl.GL.GL_UNSIGNED_BYTE;
import static com.jogamp.opengl.GL2ES3.GL_DEPTH_STENCIL_ATTACHMENT;

public class OffscreenRenderer {

    private byte[] bbuffer;
    private ByteBuffer glbuffer;
    int[] fbo;
    int[] texture;
    int[] renderBuffer;
    int width;
    int height;

    public OffscreenRenderer(GL2 gl, int w, int h) {
        fbo = new int[1];
        texture = new int[1];
        renderBuffer = new int[1];
        width = height = 0;
        gl.glGenFramebuffers(1, fbo, 0);
        gl.glGenTextures(1, texture, 0);
        gl.glGenRenderbuffers(1, renderBuffer, 0);
        setFrameSize(w, h);
    }

    public void setFrameSize(int w, int h) {
        width = w;
        height = h;
        bbuffer = new byte[width * height * 3];
        glbuffer = GLBuffers.newDirectByteBuffer(bbuffer);
    }

    public void render(GLRenderer renderer, GLAutoDrawable drawable) {

            GL2 gl = drawable.getGL().getGL2();
            drawable.getContext().makeCurrent();

            gl.glViewport(0, 0, width, height);
            renderer.setViewport(width, height);

            gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbo[0]);
            gl.glActiveTexture(GL_TEXTURE0);
            gl.glBindTexture(GL_TEXTURE_2D, texture[0]);

            gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, null);
            gl.glPixelStorei(GL_UNPACK_ALIGNMENT,1);
            gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            gl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture[0], 0);

            gl.glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer[0]);
            gl.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
            gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderBuffer[0]);

            int[] buffers = {GL_COLOR_ATTACHMENT0};
            gl.glDrawBuffers(1, buffers, 0);

            renderer.draw(drawable.getGL().getGL2ES3());
            gl.glFlush();
            gl.glFinish();

            glbuffer.rewind();
            gl.glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, glbuffer);

            gl.glFlush();
            gl.glFinish();

            gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0);

            drawable.getContext().release();
    }

    public void saveCurrentBuffer(String path, GLAutoDrawable drawable) {

        BufferedImage screenshot = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics graphics = screenshot.getGraphics();

        glbuffer.rewind();
        for (int h = 0; h < height; h++) {
            for (int w = 0; w < width; w++) {
                // The color are the three consecutive bytes, it's like referencing
                // to the next consecutive array elements, so we got red, green, blue..
                // red, green, blue, and so on...
                // the boolean bitise and converts byte to unsigned byte, that actually not exists in java language
                //graphics.setColor(new Color(glbuffer.get()& 0xFF, glbuffer.get()& 0xFF, glbuffer.get()& 0xFF));
                int r = glbuffer.get() & 0xFF;
                int g = glbuffer.get() & 0xFF;
                int b = glbuffer.get() & 0xFF;
                graphics.setColor(new Color(r, g, b));
                graphics.drawRect(w, height - h - 1, 1, 1); // height - h is for flipping the image
            }
        }

        try {
            ImageIO.write(screenshot, "jpg", new File(path));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void dispose(GL2 gl) {
        fbo = null;
        texture = null;
        gl.glDeleteTextures(1, texture, 0);
        gl.glDeleteRenderbuffers(1, renderBuffer, 0);
        gl.glDeleteFramebuffers(1, fbo, 0);
    }
}


called as

 public void takeScreenShoot(String path, GLAutoDrawable drawable) {
        // render screen shot
        offscreenRenderer.render(renderer, drawable);

        // render visdibile frame
        renderer.setViewport(getWidth(), getHeight());
        drawable.getGL().getGL2().glViewport(0, 0, getWidth(), getHeight());
        display();

        offscreenRenderer.saveCurrentBuffer(path, drawable);
    }

where the GLAutoDrawable is created as

GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile());
        drawable = factory.createDummyAutoDrawable(null, true, caps, null);

i think its a problem with the depth buffer, the gl call routines in this sequence work in C++ but not in jogl/java. Please help, i have no idea where to go.

thank you very much for advice :)
Reply | Threaded
Open this post in threaded view
|

Re: offscreen rendering and taking screenshot

gouessej
Administrator
Hello

Sorry but I don't see why JOGL would be to blame here. It's not a bug, maybe the frustum isn't the same in both cases.

Please provide a SSCCE.

Maybe you could use AWTGLReadBufferUtil and look at the existing tests and examples.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: offscreen rendering and taking screenshot

c0d3x
Thanke you for response,

it's exactly the same frustum configured in a camera class that is contained in the renderer class that is passed
as

public void render(GLRenderer renderer, GLAutoDrawable drawable) {
...
}

the same renderer is used to render the corect image as you can see in the first screen shot.

It's definitely a depth buffer configuration issue, as you can see on the second screenshot. Look at the chairs in the car model where the rear chair should be covered by the front chair, but this is not the case. I also thought it was a Frustum problem in the first place and so I carefully checked everything that goes with my camera code.

The code i posted is converted from C++ and i used raw OpenGL code, in general we want avoid using specific jogl api  and use raw OpenGL calls because we here have already experience.

Reply | Threaded
Open this post in threaded view
|

Re: offscreen rendering and taking screenshot

gouessej
Administrator
However, if you reinvent the wheel, don't complain about doing it worse than the JogAmp community. If you really think that it's a bug in JOGL, ask for a bugzilla account and give us a mean of reproducing it.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: offscreen rendering and taking screenshot

Sven Gothel
Administrator
I concur.

c0d3x, no offense, but it might be helpful if you
(1) produce a simple plain test case
(2) produce variant to (1) using our FBO and 'snapshot' code
(3) compare (1) and (2) just to double check whether
    you might have made a mistake

then - or in parallel

(a) ask for a bugzilla account and post all of the above

Thank you!

~Sven

On 8/2/19 10:10 AM, gouessej [via jogamp] wrote:
> However, if you reinvent the wheel, don't complain about doing it worse than
> the JogAmp community. If you really think that it's a bug in JOGL, ask for a
> bugzilla account and give us a mean of reproducing it.
> Julien Gouesse | Personal blog <http://gouessej.wordpress.com> | Website
> <http://tuer.sourceforge.net>
>