我正在尝试使用 LWJGL 库将图像加载为 openGL 的纹理。从我目前发现的情况来看,我需要将纹理作为 ByteBuffer 传递给 openGL。我现在拥有的是一些正确加载图像并将其存储在 BufferedImage 对象中的代码。问题是,我不知道如何从 BufferedImage 获取包含正确格式的数据以供 openGL 使用的 ByteBuffer(作为函数 GL11.glTexImage2D() 的输入)。非常感谢您的帮助!
4 回答
这是 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;
}
我使用了 Ron 上面的解决方案,但是当作为纹理应用时图像的颜色不正确,这意味着,接受的解决方案可能不会对所有类型的图像产生相同的结果。
为了解决颜色问题,我尝试使用ColorModel
原始的BufferedImage
,可以通过调用BufferedImage#getColorModel
. 但是,它给了我一个例外,即原始图像与对象ColorModel
不兼容。WritableRaster
我为此寻找解决方案,并找到了这个。我没有调用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;
}
对先前答案的一种更直接的方法是使用image.getRGB
进行颜色模型转换。以下是 Java 8文档的摘录getRGB
:
TYPE_INT_ARGB
从图像数据的一部分返回默认 RGB 颜色模型 ( ) 和默认 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_RGBA
是GL_UNSIGNED_BYTE
.
我们也可以在 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 时间,如果你只是加载纹理一次就可以了。如果你每帧更新纹理,你可以使用着色器,它使用 GPU 和更少的时间(我在我的电脑上测试的 1/4 时间)