【问题标题】:glReadPixels returns more data than expectedglReadPixels 返回比预期更多的数据
【发布时间】:2026-01-06 21:00:01
【问题描述】:

我想使用 JOGL 保存我在 openGL 中展示的内容的视频。为此,我将我的帧写入图片如下,然后,一旦我保存了所有帧,我将使用 ffmpeg。我知道这不是最好的方法,但我仍然不太清楚如何使用 tex2dimage 和 PBO 加速。在这方面的任何帮助都会非常有用。

无论如何,我的问题是,如果我运行 opengl 类它可以工作,但是,如果我从另一个类调用这个类,那么我看到 glReadPixels 给我一个错误。它总是向缓冲区返回比分配给我的缓冲区“pixelsRGB”的内存更多的数据。有谁知道为什么?

例如:width = 1042;高度=998。分配=3.119.748 glPixels 返回=3.121.742

public void display(GLAutoDrawable drawable) {
       //Draw things.....

       //bla bla bla
       t++; //This is a time variable for the animation (it says to me the frame).

       //Save frame        
       int width = drawable.getSurfaceWidth();
       int height = drawable.getSurfaceHeight();
       ByteBuffer pixelsRGB = Buffers.newDirectByteBuffer(width * height * 3);
       gl.glReadPixels(0, 0, width,height, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, pixelsRGB);
       BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

       int[] pixels = new int[width * height];

        int firstByte = width * height * 3;
        int sourceIndex;
        int targetIndex = 0;
        int rowBytesNumber = width * 3;

        for (int row = 0; row < height; row++) {
            firstByte -= rowBytesNumber;
            sourceIndex = firstByte;
            for (int col = 0; col < width; col++) {
                int iR = pixelsRGB.get(sourceIndex++);
                int iG = pixelsRGB.get(sourceIndex++);
                int iB = pixelsRGB.get(sourceIndex++);

                pixels[targetIndex++] = 0xFF000000
                    | ((iR & 0x000000FF) << 16)
                    | ((iG & 0x000000FF) << 8)
                    | (iB & 0x000000FF);
            }

        }

        bufferedImage.setRGB(0, 0, width, height, pixels, 0, width);


        File a = new File(t+".png");
        ImageIO.write(bufferedImage, "PNG", a);
 }

注意: 现在有了 pleluron 的回答,它可以工作了。好的代码是:

public void display(GLAutoDrawable drawable) {
       //Draw things.....

       //bla bla bla
       t++; //This is a time variable for the animation (it says to me the frame).

       //Save frame        
       int width = drawable.getSurfaceWidth();
       int height = drawable.getSurfaceHeight();
       ByteBuffer pixelsRGB = Buffers.newDirectByteBuffer(width * height * 4);
       gl.glReadPixels(0, 0, width,height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pixelsRGB);
       BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

       int[] pixels = new int[width * height];

        int firstByte = width * height * 4;
        int sourceIndex;
        int targetIndex = 0;
        int rowBytesNumber = width * 4;

        for (int row = 0; row < height; row++) {
            firstByte -= rowBytesNumber;
            sourceIndex = firstByte;
            for (int col = 0; col < width; col++) {
                int iR = pixelsRGB.get(sourceIndex++);
                int iG = pixelsRGB.get(sourceIndex++);
                int iB = pixelsRGB.get(sourceIndex++);

                sourceIndex++;

                pixels[targetIndex++] = 0xFF000000
                    | ((iR & 0x000000FF) << 16)
                    | ((iG & 0x000000FF) << 8)
                    | (iB & 0x000000FF);
            }

        }

        bufferedImage.setRGB(0, 0, width, height, pixels, 0, width);


        File a = new File(t+".png");
        ImageIO.write(bufferedImage, "PNG", a);
 }

【问题讨论】:

  • 宁可将 com.jogamp.opengl.util.GLReadBufferUtil 与 com.jogamp.opengl.util.texture.TextureIO 一起使用。如果你正确使用它,你可以继续为所有图像使用相同的缓冲区(在 TextureData 对象内),你摆脱了 AWT,JOGL PNG 编码器(基于 PNGJ)比 AWT/摇摆等效。
  • 顺便说一下,FFMPEG 和 LibAV 已经在媒体播放器内部的 JOGL 中使用。也许你可以看看源代码,看看如何公开所需的方法来编写,这样可以避免你使用大量的PNG文件。

标签: java opengl buffer jogl glreadpixels


【解决方案1】:

GL_PACK_ALIGNMENTglPixelStore 设置的默认值为 4。这意味着pixelsRGB 的每一行应该从一个 4 的倍数的地址开始,并且缓冲区的宽度 (1042) 乘以像素中的字节数 (3) 不是 4 的倍数。添加一点填充以使下一行从 4 的倍数开始将使缓冲区的总字节大小大于您的预期。

要修复它,请将 GL_PACK_ALIGNMENT 设置为 1。您还可以使用 GL_RGBA 读取像素并使用更大的缓冲区,因为数据最有可能以这种方式存储在 GPU 和 BufferedImage 中.

编辑:BufferedImage 没有方便的“setRGBA”,太糟糕了。

【讨论】:

  • 我尝试添加 gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1);在 pixelRGB 声明之前,但它再次失败。无论如何,我尝试不破坏 alpha 值并且它正在工作!
  • 这是“打包”,而不是“解包”。打包用于 OpenGL -> 用户,解包用于用户 -> OpenGL。
  • UPS!行!现在它可以工作了。我想我更喜欢 glPixelStore 而不是添加 alpha 通道。它消耗的内存更少。再次感谢!
  • 是的,我认为BufferedImage 可以直接拍摄RGBA 图像……那么最好使用行打包。
最近更新 更多