我试图使用LWJGL的纹理,结果发现RBG png有点歪斜。示例:原始图片/纹理
加载的代码是lwjgl wiki空间入侵者示例的98%。。Texture.java:
public int target, textureID, height, width, texWidth, texHeight;
private float widthRatio, heightRatio;
public Texture(int target, int textureID) {
this.target = target;
this.textureID = textureID;
}
public void bind() {
GL11.glBindTexture(target, textureID);
}
public void setWidth(int width) {
this.width = width;
setWidth();
}
public void setHeight(int height) {
this.height = height;
setHeight();
}
public int getImageWidth() {
return width;
}
public int getImageHeight() {
return height;
}
public float getWidth() {
return widthRatio;
}
public float getHeight() {
return heightRatio;
}
public void setTextureWidth(int texWidth) {
this.texWidth = texWidth;
setWidth();
}
public void setTextureHeight(int texHeight) {
this.texHeight = texHeight;
setHeight();
}
private void setWidth() {
if (texWidth != 0)
widthRatio = ((float) width) / texWidth;
}
private void setHeight() {
if (texHeight != 0)
heightRatio = ((float) height) / texHeight;
}
TextureLoader.java:
private static HashMap<String, Texture> table = new HashMap<String, Texture>();
private static ColorModel glAlphaColorModel, glColorModel;
private static IntBuffer textureIDBuffer = BufferUtils.createIntBuffer(1);
static {
glAlphaColorModel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8,
8, 8 }, true, false, ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
glColorModel = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8,
8, 0 }, false, false, ComponentColorModel.OPAQUE,
DataBuffer.TYPE_BYTE);
}
private static int createTextureID() {
GL11.glGenTextures(textureIDBuffer);
return textureIDBuffer.get(0);
}
public static Texture getTexture(String name, BufferedImage image)
throws IOException {
Texture tex = table.get(name);
if (tex != null)
return tex;
tex = getTexture(image, GL11.GL_TEXTURE_2D, GL11.GL_RGBA,
GL11.GL_LINEAR, GL11.GL_LINEAR);
table.put(name, tex);
return tex;
}
public static Texture getTexture(BufferedImage image, int target,
int dstPixelFormat, int minFilter, int magFilter)
throws IOException {
int srcPixelFormat;
int textureID = createTextureID();
Texture texture = new Texture(target, textureID);
GL11.glBindTexture(target, textureID);
texture.setWidth(image.getWidth());
texture.setHeight(image.getHeight());
if (image.getColorModel().hasAlpha())
srcPixelFormat = GL11.GL_RGBA;
else
srcPixelFormat = GL11.GL_RGB;
ByteBuffer textureBuffer = convertImageData(image, texture);
if (target == GL11.GL_TEXTURE_2D) {
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
}
GL11.glTexImage2D(target, 0, dstPixelFormat, image.getWidth(),
image.getHeight(), 0, srcPixelFormat,
GL11.GL_UNSIGNED_BYTE, textureBuffer);
return texture;
}
private static ByteBuffer convertImageData(BufferedImage bufferedImage,
Texture texture) {
ByteBuffer imageBuffer;
WritableRaster raster;
BufferedImage texImage;
int texWidth = bufferedImage.getWidth();
int texHeight = bufferedImage.getHeight();
texture.setTextureHeight(texHeight);
texture.setTextureWidth(texWidth);
if (bufferedImage.getColorModel().hasAlpha()) {
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
texWidth, texHeight, 4, null);
texImage = new BufferedImage(glAlphaColorModel, raster, false,
new Hashtable());
} else {
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
texWidth, texHeight, 3, null);
texImage = new BufferedImage(glColorModel, raster, false,
new Hashtable());
}
Graphics g = texImage.getGraphics();
g.setColor(new Color(0f, 0f, 0f, 0f));
g.fillRect(0, 0, texWidth, texHeight);
g.drawImage(bufferedImage, 0, 0, null);
// texImage is NOT skewed at this point
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;
}
我知道这是一个老问题,但我刚才自己也遇到过。希望其他人会受益,假设你已经自己解决了这个问题。
纹理扭曲和变色的原因是它没有与UNPACK_ALIGNMENT
正确对齐。据我所知,每行中的字节数必须是UNPACK_ALIGNMENT
设置为(默认为4(的倍数。
对于4分量格式,这不是问题,因为每个像素由4个字节组成,所以任何维度的图像都是正确对齐的。但对于其他格式,数据会被填充以保持正确对齐,这会导致这样的问题。
您可以更改图像的大小,使其正确对齐((width * formatComponents) % 4 == 0)
,将格式更改为4组件格式,或者使用将UNPACK_ALIGNMENT
更改为不填充图像的格式
glPixelStore(GL_UNPACK_ALIGNMENT, alignment); //Alignment must be 1, 2, 4, or 8
。
http://www.opengl.org/wiki/Common_Mistakes#Texture_upload_and_pixel_reads
您可能会遇到PNG如何存储在文件中以及Java如何加载的问题。我不确定我以前是否经历过这个问题,但我知道我曾在LWJGL中加载图像时遇到过问题。这是我整理的一个类,它可以确保PNG转换为OpenGL可以理解的格式,而不会出现倾斜或颜色问题。作为警告,这对我的PNG有效,但BufferedImage可能有很多格式,我不相信这段代码涵盖所有情况。希望这能帮到你。它应该是完全独立的,除了另一个类(SimpleTexture(,我也将在这里为您提供它。请注意,BasicTextureLoader类实际上是一个Runnable,因为它最初是为在后台加载纹理而设计的。
import org.lwjgl.opengl.OpenGLException;
import javax.imageio.ImageIO;
import static org.lwjgl.opengl.GL11.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.IOException;
import java.net.URL;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* User: FreezerburnVinny
* Date: 1/1/12
* Time: 5:59 PM
*/
public class BasicTextureLoader implements Runnable {
public SimpleTexture texture;
public BufferedImage image;
public int width, height;
protected String mFile;
protected boolean mShouldRemoveBackground, mKeepBufferedImage;
public BasicTextureLoader( String file, boolean shouldRemoveBackground ) {
mFile = file;
this.texture = null;
this.width = 0;
this.height = 0;
this.mShouldRemoveBackground = shouldRemoveBackground;
this.mKeepBufferedImage = false;
}
public BasicTextureLoader( String file, boolean shouldRemoveBackground, boolean keepBufferedImage ) {
mFile = file;
this.texture = null;
this.width = 0;
this.height = 0;
this.mShouldRemoveBackground = shouldRemoveBackground;
this.mKeepBufferedImage = keepBufferedImage;
}
protected ByteBuffer convertBufferedImageToByteBuffer( BufferedImage image ) {
ByteBuffer buffer = ByteBuffer.allocateDirect( width * height * 4 );
buffer.order( ByteOrder.nativeOrder() );
byte[] bytes = ( (DataBufferByte) image.getRaster().getDataBuffer() ).getData();
switch( image.getType() ) {
case BufferedImage.TYPE_3BYTE_BGR:
convertBGRBufferedImageToByteBuffer( buffer, bytes );
break;
case BufferedImage.TYPE_4BYTE_ABGR:
convertABGRBufferedImageToByteBuffer( buffer, bytes );
break;
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
convertBGRBufferedImageToByteBuffer( buffer, bytes );
break;
case BufferedImage.TYPE_INT_ARGB:
convertARGBBufferedImageToByteBuffer( buffer, bytes );
break;
case BufferedImage.TYPE_INT_BGR:
convertBGRBufferedImageToByteBuffer( buffer, bytes );
break;
case BufferedImage.TYPE_INT_RGB:
convertRGBBufferedImageToByteBuffer( buffer, bytes );
break;
case 12:
convertRGBBufferedImageToByteBuffer( buffer, bytes );
break;
default:
throw new OpenGLException( "Unsupported image type: " + image.getType() );
}
return buffer;
}
protected void convertARGBBufferedImageToByteBuffer( ByteBuffer buffer, byte[] bytes ) {
byte backgrounda = (byte) 255;
byte backgroundr = (byte) 255;
byte backgroundg = (byte) 255;
byte backgroundb = (byte) 255;
for( int i = 0; i < bytes.length; i+=4 ) {
byte alpha = bytes[ i ];
byte red = bytes[ i + 1 ];
byte green = bytes[ i + 2 ];
byte blue = bytes[ i + 3 ];
if( mShouldRemoveBackground ) {
if( i == 0 ) {
backgrounda = alpha;
backgroundr = red;
backgroundg = green;
backgroundb = blue;
}
else if( alpha == backgrounda && red == backgroundr &&
green == backgroundg && blue == backgroundb ) {
alpha = 0;
}
}
buffer.put( red );
buffer.put( green );
buffer.put( blue );
buffer.put( alpha );
}
buffer.rewind();
}
protected void convertABGRBufferedImageToByteBuffer( ByteBuffer buffer, byte[] bytes ) {
byte backgrounda = (byte) 255;
byte backgroundr = (byte) 255;
byte backgroundg = (byte) 255;
byte backgroundb = (byte) 255;
// System.err.println( buffer.limit() );
for( int i = 0; i < bytes.length; i+=4 ) {
byte alpha = bytes[ i ];
byte blue = bytes[ i + 1 ];
byte green = bytes[ i + 2 ];
byte red = bytes[ i + 3 ];
if( mShouldRemoveBackground ) {
if( i == 0 ) {
backgrounda = alpha;
backgroundr = red;
backgroundg = green;
backgroundb = blue;
}
else if( alpha == backgrounda && red == backgroundr &&
green == backgroundg && blue == backgroundb ) {
alpha = 0;
}
}
buffer.put( red );
buffer.put( green );
buffer.put( blue );
buffer.put( alpha );
}
buffer.rewind();
}
protected void convertBGRBufferedImageToByteBuffer( ByteBuffer buffer, byte[] bytes ) {
byte backgrounda = (byte) 255;
byte backgroundr = (byte) 255;
byte backgroundg = (byte) 255;
byte backgroundb = (byte) 255;
for( int i = 0; i < bytes.length; i+=3 ) {
byte blue = bytes[ i ];
byte green = bytes[ i + 1 ];
byte red = bytes[ i + 2 ];
byte alpha = (byte) 0xFF;
buffer.put( red );
buffer.put( green );
buffer.put( blue );
if( mShouldRemoveBackground ) {
if( i == 0 ) {
backgrounda = alpha;
backgroundr = red;
backgroundg = green;
backgroundb = blue;
}
else if( alpha == backgrounda && red == backgroundr &&
green == backgroundg && blue == backgroundb ) {
alpha = 0;
}
}
buffer.put( alpha );
}
buffer.rewind();
}
protected void convertRGBBufferedImageToByteBuffer( ByteBuffer buffer, byte[] bytes ) {
byte backgrounda = (byte) 255;
byte backgroundr = (byte) 255;
byte backgroundg = (byte) 255;
byte backgroundb = (byte) 255;
for( int i = 0; i < bytes.length; i+=3 ) {
byte red = bytes[ i ];
byte green = bytes[ i + 1 ];
byte blue = bytes[ i + 2 ];
byte alpha = (byte) 0xFF;
buffer.put( red );
buffer.put( green );
buffer.put( blue );
if( mShouldRemoveBackground ) {
if( i == 0 ) {
backgrounda = alpha;
backgroundr = red;
backgroundg = green;
backgroundb = blue;
}
else if( alpha == backgrounda && red == backgroundr &&
green == backgroundg && blue == backgroundb ) {
alpha = 0;
}
}
buffer.put( alpha );
}
buffer.rewind();
}
protected int nextPowerOf2( int num ) {
int ret = 2;
while( ret < num ) ret *= 2;
return ret;
}
protected int genTextureFromBufferedImage( BufferedImage image ) {
int tex = -1;
try {
// width = nextPowerOf2( image.getWidth() );
// height = nextPowerOf2( image.getHeight() );
width = image.getWidth();
height = image.getHeight();
ByteBuffer imageBuffer = convertBufferedImageToByteBuffer( image );
tex = glGenTextures();
glBindTexture( GL_TEXTURE_2D, tex );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, imageBuffer );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}
catch( IndexOutOfBoundsException e ) {
e.printStackTrace();
glDeleteTextures( tex );
tex = -1;
}
catch( OpenGLException e ) {
e.printStackTrace();
glDeleteTextures( tex );
tex = -1;
}
return tex;
}
protected int genTexture( String file ) {
try {
if( System.getProperty( "os.name" ).toLowerCase().contains( "win" ) ) {
String fileName = System.getProperty( "user.dir" );
fileName = fileName.substring( 2 );
fileName = "file:" + fileName + "\" + file;
image = ImageIO.read( new URL( fileName ) );
}
else {
String fileName = "file:" + System.getProperty( "user.dir" ) + "/" + file;
// System.err.println( fileName );
image = ImageIO.read( new URL( fileName ) );
}
return genTextureFromBufferedImage( image );
}
catch( IOException e ) {
e.printStackTrace();
}
// If we reach here, an error happened
return -1;
}
@Override
public void run() {
texture = new SimpleTexture( GL_TEXTURE_2D, genTexture( mFile ) );
if( !mKeepBufferedImage ) {
image.flush();
image = null;
}
texture.setWidth( width );
texture.setHeight( height );
}
}
以及SimpleTexture类:
import org.lwjgl.opengl.GL11;
/**
* Author: FreezerburnVinny
* Date: 1/5/12
* Time: $(TIME}
*/
public class SimpleTexture extends Texture {
private int mTarget, mName;
private double mWidth, mHeight;
private double mx1, mx2, my1, my2;
public SimpleTexture( int target, int name ) {
this.mTarget = target;
this.mName = name;
this.mWidth = 0.0;
this.mHeight = 0.0;
this.mx1 = 0.0;
this.mx2 = 1.0;
this.my1 = 0.0;
this.my2 = 1.0;
}
public SimpleTexture( int target, int name, double width, double height ) {
this.mTarget = target;
this.mName = name;
this.mWidth = width;
this.mHeight = height;
}
public SimpleTexture( int target, int name, double width, double height,
double x1, double x2, double y1, double y2 ) {
this.mTarget = target;
this.mName = name;
this.mWidth = width;
this.mHeight = height;
this.mx1 = x1;
this.mx2 = x2;
this.my1 = y1;
this.my2 = y2;
}
public double getTexCoordx1() { return mx2; }
public double getTexCoordy1() { return my1; }
public double getTexCoordx2() { return mx2; }
public double getTexCoordy2() { return my2; }
public double getTexCoordx3() { return mx1; }
public double getTexCoordy3() { return my2; }
public double getTexCoordx4() { return mx1; }
public double getTexCoordy4() { return my1; }
public double getWidth() { return mWidth; }
public double getHeight() { return mHeight; }
public int getName() { return mName; }
public int getTarget() { return mTarget; }
public void setWidth( double width ) { this.mWidth = width; }
public void setHeight( double height) { this.mHeight = height; }
public void bind() {
if( Texture.lastBound != mName ) {
GL11.glBindTexture( mTarget, mName );
Texture.lastBound = mName;
}
}
public boolean isValidTexture() {
if( mName == -1 ) return false;
return GL11.glIsTexture( mName );
}
@Override
public void restart() {
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public void stopAt( int frame ) {
}
@Override
public void stopAfterFullCycle() {
}
@Override
public int numFrames() {
return 1;
}
}
编辑:哇,差点忘了SimpleTexture依赖于Texture类。给你:
/**
* Author: FreezerburnVinny
* Date: 1/10/12
* Time: $(TIME}
*/
public abstract class Texture {
protected static int lastBound = -1;
public abstract double getTexCoordx1();
public abstract double getTexCoordx2();
public abstract double getTexCoordx3();
public abstract double getTexCoordx4();
public abstract double getTexCoordy1();
public abstract double getTexCoordy2();
public abstract double getTexCoordy3();
public abstract double getTexCoordy4();
public abstract double getWidth();
public abstract double getHeight();
public abstract int getName();
public abstract int getTarget();
public abstract void setWidth( double width );
public abstract void setHeight( double height );
public abstract void bind();
public abstract boolean isValidTexture();
public abstract void restart();
public abstract void pause();
public abstract void resume();
public abstract void stopAt( int frame );
public abstract void stopAfterFullCycle();
public abstract int numFrames();
}