【问题标题】:How do I load an image for use as an openGL texture with LWJGL?如何使用 LWJGL 加载图像以用作 openGL 纹理?
【发布时间】:2011-07-08 20:04:25
【问题描述】:

我正在尝试使用 LWJGL 库将图像加载为 openGL 的纹理。从我目前发现的情况来看,我需要将纹理作为 ByteBuffer 传递给 openGL。我现在拥有的是一些正确加载图像并将其存储在 BufferedImage 对象中的代码。问题是,我不知道如何从 BufferedImage 获取包含正确格式的数据以供 openGL 使用的 ByteBuffer(作为函数 GL11.glTexImage2D() 的输入)。 非常感谢您的帮助!

【问题讨论】:

    标签: java opengl lwjgl


    【解决方案1】:

    这是 Space Invaders 示例中的一种方法,可以满足您的需求。 (我认为)

    /**
     * Convert the buffered image to a texture
     */
    private ByteBuffer convertImageData(BufferedImage bufferedImage) {
        ByteBuffer imageBuffer;
        WritableRaster raster;
        BufferedImage texImage;
    
        ColorModel glAlphaColorModel = new ComponentColorModel(ColorSpace
                .getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8, 8, 8 },
                true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
    
        raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
                bufferedImage.getWidth(), bufferedImage.getHeight(), 4, null);
        texImage = new BufferedImage(glAlphaColorModel, raster, true,
                new Hashtable());
    
        // copy the source image into the produced image
        Graphics g = texImage.getGraphics();
        g.setColor(new Color(0f, 0f, 0f, 0f));
        g.fillRect(0, 0, 256, 256);
        g.drawImage(bufferedImage, 0, 0, null);
    
        // build a byte buffer from the temporary image
        // that be used by OpenGL to produce a texture.
        byte[] data = ((DataBufferByte) texImage.getRaster().getDataBuffer())
                .getData();
    
        imageBuffer = ByteBuffer.allocateDirect(data.length);
        imageBuffer.order(ByteOrder.nativeOrder());
        imageBuffer.put(data, 0, data.length);
        imageBuffer.flip();
    
        return imageBuffer;
    }
    

    【讨论】:

    • 我已经弄明白了,谢谢 :) 我将把答案标记为已回答
    • 对于未来的访问者,我发现@Displee 的解决方案具有相同的效果,但略显冗长。大多数关于颜色模型和复制图像的东西都可以使用getRGB来处理。
    【解决方案2】:

    我使用了 Ron 的上述解决方案,但图像在作为纹理应用时的颜色不正确,这意味着可接受的解决方案可能不会对所有类型的图像产生相同的结果。

    为了解决颜色问题,我尝试使用原来BufferedImageColorModel,可以通过调用BufferedImage#getColorModel来访问。但是,它给了我一个例外,原始图像的ColorModelWritableRaster 对象不兼容。

    我为此寻找解决方案,我找到了this 一个。我没有调用Raster.createInterleavedRaster 来创建WritableRaster,而是使用ColorModel#createCompatibleWritableRaster

    希望这会有所帮助。代码如下:

    public static ByteBuffer load(BufferedImage bufferedImage) {
    
        WritableRaster raster = bufferedImage.getColorModel().createCompatibleWritableRaster
            (bufferedImage.getWidth(), bufferedImage.getHeight());
        BufferedImage textureImage = new BufferedImage(bufferedImage.getColorModel(), raster,
            true, new Hashtable<>());
    
        Graphics graphics = textureImage.getGraphics();
        graphics.setColor(new Color(0, 0, 0));
        graphics.fillRect(0, 0, 256, 256);
        graphics.drawImage(bufferedImage, 0, 0, null);
    
        byte[] data = ((DataBufferByte) textureImage.getRaster().getDataBuffer()).getData();
    
        ByteBuffer imageBuffer = ByteBuffer.allocate(data.length);
        imageBuffer.order(ByteOrder.nativeOrder());
        imageBuffer.put(data, 0, data.length);
        imageBuffer.flip();
    
        return imageBuffer;
    }
    

    【讨论】:

    • 如果数据缓冲区不是DataBufferByte,则此方法不起作用。在我的代码中,我创建了一个类型为TYPE_INT_ARGB 的图像,发现它使用了DataBufferInt。 @Displee 的解决方案能够处理这些边缘情况。
    【解决方案3】:

    对前面的答案更直接的方法是使用image.getRGB 进行颜色模型转换。以下是getRGB 上的 Java 8 documentation 的摘录:

    从图像数据的一部分返回默认 RGB 颜色模型 (TYPE_INT_ARGB) 和默认 sRGB 颜色空间中的整数像素数组。如果默认模型与图像 ColorModel 不匹配,则会发生颜色转换。使用此方法时,返回数据中每个颜色分量的精度只有 8 位。

    public static ByteBuffer imageToBuffer(BufferedImage image) {
        int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
        ByteBuffer buffer = ByteBuffer.allocateDirect(pixels.length * 4);
        for (int pixel : pixels) {
            buffer.put((byte) ((pixel >> 16) & 0xFF));
            buffer.put((byte) ((pixel >> 8) & 0xFF));
            buffer.put((byte) (pixel & 0xFF));
            buffer.put((byte) (pixel >> 24));
        }
        buffer.flip();
        return buffer;
    }
    

    返回的缓冲区是GL_RGBAGL_UNSIGNED_BYTE

    【讨论】:

    • 我发现,即使与投票率较高的答案相比,这是最可靠和最详细的解决方案。 getRGB 通过在 sRGB 颜色空间中将输出标准化为 TYPE_INT_ARGB,为您处理所有边缘情况。然后可以跟glTexImage2D(GL_TEXTURE_2D, 0, yourDesiredImageType, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer)
    【解决方案4】:

    我们也可以在 Shader 中做到这一点。

    BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    
    DataBufferInt dbb = (DataBufferInt) image.getRaster().getDataBuffer();
    glBindTexture(GL_TEXTURE_2D, id);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0,   GL_RGBA,   GL_UNSIGNED_INT_8_8_8_8,dbb.getData());
    

    GL_FRAGMENT_SHADER:

    #version 130
    
    uniform sampler2D tex;
    
    in vec2 texCoordsVarying;
    out vec4 color;
    
    void main() {
      vec4 c2 = texture(tex, texCoordsVarying);
      color = vec4(c2.g, c2.b, c2.a, c2.r);
    }
    

    @Displee 给出的方法,使用 CPU 时间,如果你只是加载纹理一次就可以了。 如果你每帧更新纹理,你可以使用 Shader,它使用 GPU 和更少的时间(我在我的 PC 上测试的 1/4 时间)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-04
      • 1970-01-01
      • 1970-01-01
      • 2013-05-24
      • 1970-01-01
      • 1970-01-01
      • 2017-09-27
      • 1970-01-01
      相关资源
      最近更新 更多