如何修复纹理/图像的失真?

时间:2016-02-16 11:50:40

标签: java 3d rotation textures

我试图垂直旋转透视图,但图像/纹理在窗口中间变形。原点位于窗口中间,正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];
}

0 个答案:

没有答案