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

Exporting an image from screen(viewport) in java OpenGL

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

我该怎么办?对于使用 OpenGL 将屏幕内容导出到图像文件,有什么好的建议吗?我听说有几个库可用,但我不知道哪个合适。感谢任何帮助T_T(如果我有任何语法错误,请原谅我...)

您可以使用机器人 class 进行截图:

BufferedImage screenshot = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
ImageIO.write(screenshot, "png", new File("screenshot.png"));

有两点需要考虑:

你从屏幕截图,你可以确定你的视口坐标在哪里,这样你就可以只抓到感兴趣的部分。

某些东西可以位于您视口的顶部(另一个 window),因此视口可能会被另一个 window 隐藏,这种情况不太可能发生,但它可能会发生。

当您将缓冲区与 LWJGL 一起使用时,它们几乎总是需要直接分配。 OpenGL 库并不真正了解如何与 Java Arrays™ 交互,并且为了使底层内存操作正常工作,它们需要应用于本地分配(或者,在这种情况下,直接分配)内存。

如果您使用的是 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);
        }
    }

本质上,您创建了一个 bufferedImage 和一个直接缓冲区。然后你使用Graphics将后台缓冲区的内容逐像素渲染到bufferedImage。 您需要一个额外的 buffer.get();,因为它代表 alpha 值,您还需要 height - h 来翻转图像。

编辑:有你要找的当然要看

您有多种选择:

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