在java OpenGL中从屏幕(视口)导出图像

在java OpenGL中从屏幕(视口)导出图像,java,opengl,jogl,Java,Opengl,Jogl,我是OpenGL编程的新手。我正在用OpenGL制作一个java程序。我在里面画了很多立方体。我现在想在我的程序中实现一个屏幕截图功能,但我就是不能让它工作。情况如下: 我使用FPSanimator以60 fps刷新了我的绘图 我在显示器里画了几十个立方体 我在面板中添加了一个KeyListener,如果我按下alt键,程序将运行以下方法: public static void exportImage() { int[] bb = new int[Constants.PanelSize

我是OpenGL编程的新手。我正在用OpenGL制作一个java程序。我在里面画了很多立方体。我现在想在我的程序中实现一个屏幕截图功能,但我就是不能让它工作。情况如下:

  • 我使用FPSanimator以60 fps刷新了我的绘图
  • 我在显示器里画了几十个立方体
  • 我在面板中添加了一个KeyListener,如果我按下alt键,程序将运行以下方法:

    public static void exportImage() {
        int[] bb = new int[Constants.PanelSize.width*Constants.PanelSize.height*4];
        IntBuffer ib = IntBuffer.wrap(bb);
        ib.position(0);
        Constants.gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);
        Constants.gl.glReadPixels(0,0,Constants.PanelSize.width,Constants.PanelSize.height,GL2.GL_RGBA,GL2.GL_UNSIGNED_BYTE,ib);
        System.out.println(Constants.gl.glGetError());
        ImageExport.savePixelsToPNG(bb,Constants.PanelSize.width,Constants.PanelSize.height, "imageFilename.png");
    }
    // Constant is a class which I store all my global variables in static type
    
  • 控制台中的输出为0,这意味着没有错误。我在缓冲区中打印了内容,它们都是零

  • 我检查了输出文件,它只有1kB

我该怎么办?有没有什么好的建议让我使用OpenGL将屏幕内容导出到图像文件中?我听说有好几个图书馆,但我不知道哪一个合适。非常感谢您的帮助(如果我有任何语法错误,请原谅我…

您可以使用Robot类截图:

BufferedImage screenshot = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
ImageIO.write(screenshot, "png", new File("screenshot.png"));
有两件事需要考虑:

从屏幕上截取屏幕快照,可以确定视口的坐标位置,因此只能捕捉感兴趣的部分


有些东西可能驻留在视口的顶部(另一个窗口),因此视口可能被另一个窗口隐藏,这不太可能发生,但可以发生。

在LWJGL中使用缓冲区时,几乎总是需要直接分配缓冲区。OpenGL库并不真正理解如何与Java数组接口™, 为了使底层内存操作正常工作,需要将它们应用于本机分配(或者,在本文中,直接分配)的内存

如果您使用的是LWJGL 3.x,这非常简单:

//Check the math, because for an image array, given that Ints are 4 bytes, I think you can just allocate without multiplying by 4.
IntBuffer ib = org.lwjgl.BufferUtils.createIntBuffer(Constants.PanelSize.width * Constants.PanelSize.height);
如果该功能不可用,这就足够了:

//Here you actually *do* have to multiply by 4.
IntBuffer ib = java.nio.ByteBuffer.allocateDirect(Constants.PanelSize.width * Constants.PanelSize.height * 4).asIntBuffer();
然后你做你的普通代码:

Constants.gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);
Constants.gl.glReadPixels(0, 0, Constants.PanelSize.width, Constants.PanelSize.height, GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, ib);
System.out.println(Constants.gl.glGetError());
int[] bb = new int[Constants.PanelSize.width * Constants.PanelSize.height];
ib.get(bb); //Stores the contents of the buffer into the int array.
ImageExport.savePixelsToPNG(bb, Constants.PanelSize.width, Constants.PanelSize.height, "imageFilename.png");

假设正在绘制默认帧缓冲区,则可以执行以下操作:

    protected void saveImage(GL4 gl4, int width, int height) {

        try {

            GL4 gl4 = GLContext.getCurrentGL().getGL4();

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

            ByteBuffer buffer = GLBuffers.newDirectByteBuffer(width * height * 4);

            gl4.glReadBuffer(GL_BACK);
            gl4.glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

            for (int h = 0; h < height; h++) {
                for (int w = 0; w < width; w++) {
                    graphics.setColor(new Color((buffer.get() & 0xff), (buffer.get() & 0xff),
                            (buffer.get() & 0xff)));
                    buffer.get();   
                    graphics.drawRect(w, height - h, 1, 1);
                }
            }
            BufferUtils.destroyDirectBuffer(buffer);
            File outputfile = new File("D:\\Downloads\\texture.png");
            ImageIO.write(screenshot, "png", outputfile);
        } catch (IOException ex) {
              Logger.getLogger(EC_DepthPeeling.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
受保护的void saveImage(GL4 GL4,整数宽度,整数高度){
试一试{
GL4 GL4=GLContext.getCurrentGL().getGL4();
BuffereImage屏幕截图=新的BuffereImage(宽度、高度、BuffereImage.TYPE_INT_RGB);
Graphics Graphics=屏幕截图.getGraphics();
ByteBuffer buffer=GLBuffers.newDirectByteBuffer(宽*高*4);
gl4.glReadBuffer(GL_BACK);
gl4.glReadPixels(0,0,宽度,高度,GL_RGBA,GL_无符号字节,缓冲区);
对于(int h=0;h
实际上,您创建了一个BuffereImage和一个direct buffer。然后使用
Graphics
将后缓冲区的内容逐像素渲染到BuffereImage。 您需要一个额外的
缓冲区
因为它表示alpha值,您还需要
height-h
来翻转图像

编辑:当然,当有你想要的东西时,你需要阅读它

您有几个选择:

  • 触发一个布尔变量,并直接从
    display
    方法调用它,最后,当您想要的一切都呈现出来时
  • 禁用自动缓冲区交换,从按键侦听器调用
    display()
    方法,读取后缓冲区并再次启用交换
  • 从键侦听器调用与在显示器中调用相同的代码

您是否正在使用jogl?导入com.jogamp.opengl.GL2;导入com.jogamp.opengl.GLAutoDrawable;导入com.jogamp.opengl.glu.glu;忘了说,看着
常量
I图像,您正在其中存储
gl
对象?如果是,你应该避免它,因为它是非常危险的。如果要获取它,请使用
GL4 GL4=GLContext.getCurrentGL().getGL4()我明白了。我会换的。它有多危险?我这样做只是为了在类之间共享对象,以便轻松地分离作业,就像很多事情一样。因为它可能会在没有建议的情况下在不同的帧之间更改,并且您可能会发现自己在一些无效的位置启动命令。您有两种选择:从
AutoDrawable
中获取它并将其传递给所有方法,或者从
GLContext
中获取它。我认为它与OpenGl设置有关。在我读取像素之前,是否需要进行任何特殊设置?我实现了该方法,但图像完全是黑色的。我认为OpenGL只读取背景。我应该在哪里调用该方法?(为了确保OpenGL在绘制对象后读取像素),我实现了您的第一个建议,它成功了。非常感谢:)听到这个消息我很高兴,好极了:)