我在尝试使用 LWJGL 开发游戏时遇到了一些奇怪的闪烁问题。我正在尝试按照本教程中的建议使用 GL_TEXTURE_RECTANGLE_ARB:http: //youtu.be/Xj_qZ3Cw08Q。
这是该问题的简短视频:http: //youtu.be/WUr_8aqoOrk。
在我看来,随着相机的移动(通过 glTransformf()),为每个图块绘制的精灵偶尔会显示一些相邻的纹理,就像这里引起问题的原因:LWJGL random lines inbenween shapes。但是,我已经将 MIN_FILTER 和 MAG_FILTER 设置为 GL_NEAREST,所以这似乎不是问题。
我已经尽可能简化了游戏,以在没有太多无关细节的情况下演示问题。您可以在此处获取 .zip:https ://dl.dropbox.com/u/22396951/Copy%20of%20Fezless.zip 。
编辑:这是快速(呃)一目了然的代码。
Fezless.java
package com.mrmikeanderson.fezless;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import de.matthiasmann.twl.utils.PNGDecoder;
import static org.lwjgl.opengl.ARBTextureRectangle.*;
import static org.lwjgl.opengl.GL11.*;
public class Fezless {
int w, h;
boolean running;
final int FPS = 60;
double t = 0.0;
final double dt = 1.0 / 60.0;
Sprite currentSprite;
Player player;
Map<String, Sprite> spriteMap;
int tiles;
float camX, camY, shakeX, shakeY;
String[] tileGrid = { "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "grass", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "air", "air", "tardis", "air", "air", "air", "air", "air", "air", "air", "air", "air", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "air", "air", "air", "air", "dirt", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "air", "air", "air", "air", "deadplant", "dirt", "air", "air", "air", "air",
"air", "air", "plant", "air", "air", "air", "air", "air", "air", "air", "air", "stone", "dirt", "dirt", "stone", "air", "air", "air", "deadplant", "dirt", "dirt", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "grass", "air", "air", "air", "air", "air", "air", "air", "stone", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "grass", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "grass", "grass", "grass", "air", "air", "air", "air", "air", "air", "deadplant", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone",
"stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "plant", "air", "air", "air", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "grass", "grass", "air", "air", "air", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "grass", "air", "air", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "dirt", "air", "air", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt",
"dirt", "dirt", "air", "air", "air", "air", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "dirt", "air", "air", "air", "air", "air", "air", "stone", "stone", "stone", "stone", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "dirt", "dirt", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "stone", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "stone", "stone", "stone", "dirt", "stone",
"stone", "stone", "stone", "stone", "stone", "stone", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "air", "dirt", "dirt", "dirt", "dirt", "stone", "stone", "stone", "stone", "stone", "stone" };
public Fezless(int w, int h) {
this.w = w;
this.h = h;
}
public void start() {
init();
running = true;
run();
}
void init() {
initDisplay();
initGL();
initSprites();
player = new Player(13f, 4f, this);
// level = new Level(player, this);
camX = 14f;
camY = -6f;
shakeX = shakeY = 0f;
}
void initDisplay() {
try {
Display.setDisplayMode(new DisplayMode(w, h));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
}
}
void initGL() {
glMatrixMode(GL_PROJECTION);
double w = Display.getWidth() / 8;
double h = Display.getHeight() / 8;
glOrtho(-w / 2, w / 2, h / 2, -h / 2, -1f, 1f);
glClearColor(.4f, .769f, 1f, 1f);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_MODELVIEW);
}
void initSprites() {
spriteMap = new HashMap<String, Sprite>();
int texture = glGenTextures();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture);
InputStream in = null;
try {
in = new FileInputStream("res/tiles.png");
PNGDecoder decoder = new PNGDecoder(in);
ByteBuffer buffer = BufferUtils.createByteBuffer(4 * decoder.getWidth() * decoder.getHeight());
decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
buffer.flip();
in.close();
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
tiles = loadSpritesheet();
spriteMap.put("air", new Sprite("air", 0, 0, 8, 8));
spriteMap.put("gonzo", new Sprite("gonzo", 0, 8, 8, 16));
spriteMap.put("grass", new Sprite("grass", 8, 0, 8, 8));
spriteMap.put("plant", new Sprite("plant", 16, 8, 8, 8));
spriteMap.put("deadplant", new Sprite("deadplant", 16, 16, 8, 8));
spriteMap.put("dirt", new Sprite("dirt", 8, 8, 8, 8));
spriteMap.put("stone", new Sprite("stone", 8, 16, 8, 8));
spriteMap.put("platform", new Sprite("platform", 24, 0, 8, 8));
spriteMap.put("obsidian", new Sprite("obsidian", 16, 0, 8, 8));
spriteMap.put("tardis", new Sprite("tardis", 32, 0, 16, 24));
}
void run() {
while (running && !Display.isCloseRequested()) {
shakeX = (float) Math.sin(t + 100);
shakeY = (float) Math.cos(t);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(-camX * 8 + shakeX, camY * 8 + shakeY, 0f);
for (int x = 0; x < 25; x++) {
for (int y = 0; y < 21; y++) {
drawSprite(tileGrid[x + y * 25], x, y);
}
}
player.render();
t += dt;
Display.update();
Display.sync(FPS);
Display.setVSyncEnabled(true);
}
cleanUp(false);
}
public void drawSprite(String name, float x, float y) {
x *= 8;
y *= 8;
currentSprite = spriteMap.get(name);
int u = currentSprite.x;
int v = currentSprite.y;
int u2 = currentSprite.x2;
int v2 = currentSprite.y2;
int w = currentSprite.w;
int h = currentSprite.h;
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tiles);
glBegin(GL_QUADS);
glTexCoord2f(u, v);
glVertex2f(x, y);
glTexCoord2f(u, v2);
glVertex2f(x, y + h);
glTexCoord2f(u2, v2);
glVertex2f(x + w, y + h);
glTexCoord2f(u2, v);
glVertex2f(x + w, y);
glEnd();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
}
int loadSpritesheet() {
int texture = glGenTextures();
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture);
InputStream in = null;
try {
in = new FileInputStream("res/tiles.png");
PNGDecoder decoder = new PNGDecoder(in);
ByteBuffer buffer = BufferUtils.createByteBuffer(4 * decoder.getWidth() * decoder.getHeight());
decoder.decode(buffer, decoder.getWidth() * 4, PNGDecoder.Format.RGBA);
buffer.flip();
in.close();
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, decoder.getWidth(), decoder.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
return texture;
}
void cleanUp(boolean error) {
Display.destroy();
System.exit(error ? 1 : 0);
}
public static void main(String[] args) {
Fezless f = new Fezless(800, 600);
f.start();
}
}
播放器.java
package com.mrmikeanderson.fezless;
import java.awt.Rectangle;
public class Player {
Fezless f;
float x, y;
boolean falling = false;
String sprite;
Rectangle boundingBox;
public Player(float x, float y, Fezless f) {
this.x = x;
this.y = y;
this.f = f;
sprite = "gonzo";
}
public void render() {
f.drawSprite(sprite, x, y);
}
}
精灵.java
package com.mrmikeanderson.fezless;
public class Sprite {
String name;
int x, y, x2, y2, w, h;
public Sprite(String name, int x, int y, int w, int h) {
this.name = name;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
x2 = x + w;
y2 = y + h;
}
}