我试图垂直旋转透视图,但图像/纹理在窗口中间变形。原点位于窗口中间,正x轴位于右侧,正y轴位于底部,正z轴位于计算机屏幕中。 yDepth为0,窗口中间的z为无穷大(高度/ 2)。创建问题的行是Render3D.java类中的双yp = yDepth * rotCos + z * rotSin。简而言之,yp和z反向相关以给出进入屏幕的深度/距离的印象,并且因此,z在原点处变为0,导致失真。我怎样才能解决这个问题?有没有更好的方法呢?
注意:使用32 px x 32 px纹理/图像作为纹理图像或使用任何方形图像,但将渲染类中的行private static final int TEXTURE_WIDTH = 32更改为private static final int TEXTURE_WIDTH = Texture.image。的getWidth()。谢谢。
以下是课程:
Love.java:
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Love extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
Love game = new Love();
JFrame frame = new JFrame("Love");
JPanel panel = new JPanel(new BorderLayout());
panel.add(game, BorderLayout.CENTER);
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
game.start();
}
public Love() {
Dimension size = new Dimension(width, height);
setSize(size);
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
input = new InputHandler();
addKeyListener(input);
addFocusListener(input);
addMouseListener(input);
addMouseMotionListener(input);
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
imagePixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
screen = new Screen(width, height);
game = new Game();
}
public void start() {
tick++;
if (gameover) {
return;
}
thread = new Thread(this);
thread.start();
}
public void stop() {
if (gameover) {
return;
}
gameover = true;
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
public void run() {
int frames = 0;
double unprocessedSeconds = 0;
long lastTime = System.nanoTime();
double secondsPerTick = 1 / 60.0;
int tickCount = 0;
requestFocus();
while (!gameover) {
long now = System.nanoTime();
long passedTime = now - lastTime;
lastTime = now;
if (passedTime < 0) passedTime = 0;
if (passedTime > 100000000) passedTime = 100000000;
unprocessedSeconds += passedTime / 1000000000.0;
boolean ticked = false;
while (unprocessedSeconds > secondsPerTick) {
tick();
unprocessedSeconds -= secondsPerTick;
ticked = true;
tickCount++;
if (tickCount % 60 == 0) {
System.out.println(frames + " fps");
lastTime += 1000;
frames = 0;
}
}
if (ticked) {
render();
frames++;
} else {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void tick() {
game.tick(InputHandler.key);
}
public void render() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
screen.draw(game);
for (int i = 0; i < width * height; i++) {
imagePixels[i] = screen.pixels[i];
}
Graphics g = bs.getDrawGraphics();
g.drawImage(image, 0, 0, width, height, null);
g.dispose();
bs.show();
}
public static int width = 640;
public static int height = 480;
private InputHandler input;;
private BufferedImage image;
private int tick = 0;
private boolean gameover = false;
private int[] imagePixels;
private Thread thread;
private Screen screen;
private Game game;
public static int r;
}
Game.java:
import java.awt.event.KeyEvent;
public class Game {
public Game() {
player = new Player();
}
public void tick(boolean[] key) {
tick++;
boolean up = key[KeyEvent.VK_UP];
boolean down = key[KeyEvent.VK_DOWN];
player.move(up, down);
}
public int tick;
public Player player;
}
Player.java:
public class Player {
public void move(boolean up, boolean down) {
double rotationSpeed = 0.00275;
if (up) {
rotationa -= rotationSpeed;
}
if (down) {
rotationa += rotationSpeed;
}
rotation += rotationa;
rotationa *= 0.4;
}
public double rotation, rotationa;
}
Render.java:
public class Render3D extends Render {
private static final int TEXTURE_WIDTH = 32;
private static final int TEXTURE_FACTOR = TEXTURE_WIDTH - 1;
int ceiling;
public Render3D(int width, int height) {
super(width, height);
zBuffer = new double[width * height];
zBufferWall = new double[width];
}
public void draw(Game game) {
for (int x = 0; x < width; x++) {
zBufferWall[x] = 0;
}
double floorPosition = TEXTURE_WIDTH;
double ceilingPosition = TEXTURE_WIDTH;
double rotation = game.player.rotation;
rotSin = Math.sin(rotation);
rotCos = Math.cos(rotation);
for (int y = 0; y < height; y++) {
double yDepth = (y - height / 2.0) / height;
for (int x = 0; x < width; x++) {
double xDepth = (x - width / 2.0) / height;
z = (floorPosition) / yDepth;
ceiling = 0;
if (yDepth < 0) {
z = (ceilingPosition) / -yDepth;
ceiling = 1;
}
double yp = yDepth * rotCos + z * rotSin;
double zp = z * rotCos - yDepth * rotSin;
z = (floorPosition) / yp;
ceiling = 0;
if (yp < 0) {
z = (ceilingPosition) / -yp;
ceiling = 1;
}
xDepth *= z;
int xPix = (int) (xDepth);
int zPix = (int) (z);
zBuffer[x + y * width] = z / 4;
if (ceiling == 0) {
pixels[x + y * width] = Texture.texture.pixels[((xPix & TEXTURE_FACTOR)) + (zPix & TEXTURE_FACTOR) * Texture.image.getWidth()];
} else {
pixels[x + y * width] = Texture.texture.pixels[((xPix & TEXTURE_FACTOR)) + (zPix & TEXTURE_FACTOR) * Texture.image.getWidth()];
}
}
}
}
public void renderDistanceLimiter() {
for (int i = 0; i < width * height; i++) {
int colour = pixels[i];
int brightness = (int) (renderDistance / (zBuffer[i]));
if (brightness < 0) {
brightness = 0;
}
if (brightness > 255) {
brightness = 255;
}
int r = (colour >> 16) & 0xff;
int g = (colour >> 8) & 0xff;
int b = (colour) & 0xff;
r = r * brightness / 255;
g = g * brightness / 255;
b = b * brightness / 255;
pixels[i] = r << 16 | g << 8 | b;
}
}
private double z;
public double[] zBuffer;
public double[] zBufferWall;
private double rotSin, rotCos;
public double renderDistance = 5000;
}
Screen.java:
public class Screen extends Render {
private static final int WIDTH = 640;
public Screen(int width, int height) {
super(width, height);
render3D = new Render3D(WIDTH, height);
}
public void draw(Game game) {
for (int i = 0; i < width * height; i++) {
pixels[i] = 0;
}
render3D.draw(game);
render3D.renderDistanceLimiter();
draw(render3D, 0, 0);
}
private Render3D render3D;
}
Render3D.java:
public class Render3D extends Render {
private static final int TEXTURE_WIDTH = 32;
private static final int TEXTURE_FACTOR = TEXTURE_WIDTH - 1;
int ceiling;
public Render3D(int width, int height) {
super(width, height);
zBuffer = new double[width * height];
zBufferWall = new double[width];
}
public void draw(Game game) {
for (int x = 0; x < width; x++) {
zBufferWall[x] = 0;
}
double floorPosition = TEXTURE_WIDTH;
double ceilingPosition = TEXTURE_WIDTH;
double rotation = game.player.rotation;
rotSin = Math.sin(rotation);
rotCos = Math.cos(rotation);
for (int y = 0; y < height; y++) {
double yDepth = (y - height / 2.0) / height;
for (int x = 0; x < width; x++) {
double xDepth = (x - width / 2.0) / height;
z = (floorPosition) / yDepth;
ceiling = 0;
if (yDepth < 0) {
z = (ceilingPosition) / -yDepth;
ceiling = 1;
}
double yp = yDepth * rotCos + z * rotSin;
double zp = z * rotCos - yDepth * rotSin;
z = (floorPosition) / yp;
ceiling = 0;
if (yp < 0) {
z = (ceilingPosition) / -yp;
ceiling = 1;
}
xDepth *= z;
int xPix = (int) (xDepth);
int zPix = (int) (z);
zBuffer[x + y * width] = z / 4;
if (ceiling == 0) {
pixels[x + y * width] = Texture.texture.pixels[((xPix & TEXTURE_FACTOR)) + (zPix & TEXTURE_FACTOR) * Texture.image.getWidth()];
} else {
pixels[x + y * width] = Texture.texture.pixels[((xPix & TEXTURE_FACTOR)) + (zPix & TEXTURE_FACTOR) * Texture.image.getWidth()];
}
}
}
}
public void renderDistanceLimiter() {
for (int i = 0; i < width * height; i++) {
int colour = pixels[i];
int brightness = (int) (renderDistance / (zBuffer[i]));
if (brightness < 0) {
brightness = 0;
}
if (brightness > 255) {
brightness = 255;
}
int r = (colour >> 16) & 0xff;
int g = (colour >> 8) & 0xff;
int b = (colour) & 0xff;
r = r * brightness / 255;
g = g * brightness / 255;
b = b * brightness / 255;
pixels[i] = r << 16 | g << 8 | b;
}
}
private double z;
public double[] zBuffer;
public double[] zBufferWall;
private double rotSin, rotCos;
public double renderDistance = 5000;
}
Texture.java:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Texture {
public static Render texture = loadTexture("Texture.png"); //The texture has to be 32 px by 32 px.
public static Render loadTexture(String filename) {
try {
image = ImageIO.read(new File(filename));
int width = image.getWidth();
int height = image.getHeight();
Render render = new Render(width, height);
image.getRGB(0, 0, width, height, render.pixels, 0, width);
return render;
} catch (IOException e) {
throw new RuntimeException (e);
}
}
public int getWidth() {
return image.getWidth();
}
public static BufferedImage image;
}
InputHandler.java:
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
public class InputHandler implements KeyListener, FocusListener, MouseListener, MouseMotionListener {
@Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseMoved(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
}
@Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void focusGained(FocusEvent e) {
// TODO Auto-generated method stub
}
@Override
public void focusLost(FocusEvent e) {
for (int i = 0; i < key.length; i++) {
key[i] = false;
}
}
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode > 0 && keyCode < key.length) {
key[keyCode] = true;
}
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode > 0 && keyCode < key.length) {
key[keyCode] = false;
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
public static int mouseX;
public static int mouseY;
public static boolean[] key = new boolean[68836];
}