Java中的3D透视投影问题

时间:2019-03-06 19:33:10

标签: java 3d projection cube

几天前,我决定从头开始用Java创建3D引擎。因此,我为向量,矩阵和简单的GUI编写了所有类。

数学是从Youtube视频(javidx9开发)中获得启发的,但是由于我使用的是列向量,而不是相同的坐标系,所以我不得不更改一些东西。

我的问题是最终结果不是我所期望的,并且在搜索我的代码和互联网之后,我既找不到为什么它不起作用,也找不到如何使其起作用。结果应该是旋转的多维数据集,但这是发生了什么:(对不起,我无法直接放置图像)

First imageSecond imageThird imageFourth image

这里是项目的所有类:

主类:

import pckg.GUI;

public class Engine3D{
    public static void main(String[] args){
        GUI gui = new GUI();
        gui.setVisible();
    }
}

GUI类:

    package pckg;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;

import math.Vector;
import pckg.tests.Cube;

public class GUI{
    private JFrame main;
    private JPanel jpMain;
    private SwingWorker<Void,Void> worker;

    private int width=500,height=500, minW=100, minH=100;
    private Vector viewPoint, viewDir;
    private double fov=90,far=1000,near=0.1,time=0;

    public GUI(){
        viewPoint = new Vector(0,0,0);
        viewDir = new Vector(0,0,1);

        worker = new SwingWorker<Void,Void>(){
            protected Void doInBackground() throws Exception{
                while(true){
                    synchronized(worker){
                        wait(25);
                    }
                    time+=0.025;
                    jpMain.repaint();
                }
            }
        };

        initializeMain();
    }

    private void initializeMain(){
        main = new JFrame();
        main.setSize(width, height);
        main.setMinimumSize(new Dimension(minW,minH));
        main.setLocationRelativeTo(null);
        main.setTitle("3D Engine");
        main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        jpMain = new JPanel(){
            private static final long serialVersionUID = 1L;

            public void paintComponent(Graphics g){
                super.paintComponent(g);

                for(Triangle t:Cube.cube.get())
                    t.paint(g, viewPoint, viewDir, width, height, far, near, fov, time);
            }
        };
        jpMain.setBackground(Color.black);
        main.setContentPane(jpMain);
    }

    public void setVisible(){
        main.setVisible(true);
        main.setSize(width,2*height-jpMain.getHeight());
        main.addComponentListener(new SizeListener());
        worker.execute();
    }


    //Listeners

    class SizeListener implements ComponentListener{
        public void componentMoved(ComponentEvent e){}
        public void componentShown(ComponentEvent e){}
        public void componentHidden(ComponentEvent e){}

        public void componentResized(ComponentEvent e){
            width = jpMain.getWidth();
            height = jpMain.getHeight();
        }
    }
}

向量类:

package math;

public class Vector{
    private double x,y,z;

    public Vector(){
        x=0;y=0;z=0;
    }
    public Vector(double x,double y,double z){
        this.x=x;
        this.y=y;
        this.z=z;
    }

    public double[] get(){
        return new double[]{x,y,z};
    }
    public double getX(){
        return x;
    }
    public double getY(){
        return y;
    }
    public double getZ(){
        return z;
    }

    public Vector add(Vector arg){
        x+=arg.x;
        y+=arg.y;
        z+=arg.z;
        return this;
    }
    public Vector sub(Vector arg){
        x-=arg.x;
        y-=arg.y;
        z-=arg.z;
        return this;
    }
    public Vector cross(Vector arg){
        x = y*arg.z-z*arg.y;
        y = z*arg.x-x*arg.z;
        z = x*arg.y-y*arg.x;
        return this;
    }
    public Vector multiply(double a){
        x*=a;
        y*=a;
        z*=a;
        return this;
    }
    public Vector divide(double a){
        if(a!=0){
            x/=a;
            y/=a;
            z/=a;
        }
        return this;
    }
    public Vector apply(Matrix mat){
        if(mat.isSquare(3)){
            double[][] coef=mat.get();
            x = coef[0][0]*x+coef[0][1]*y+coef[0][2]*z;
            y = coef[1][0]*x+coef[1][1]*y+coef[1][2]*z;
            z = coef[2][0]*x+coef[2][1]*y+coef[2][2]*z;
            return this;
        }else
            throw new ArithmeticException("The size of the matrix must be 3x3");
    }
    public Vector normalize(){
        double norm = norm();
        if(norm != 0 && norm !=1)
            divide(norm);
        return this;
    }

    public double dot(Vector arg){
        return x*arg.x+y*arg.y+z*arg.z;
    }
    public double norm(){
        return Math.sqrt(x*x+y*y+z*z);
    }

    public Vector clone(){
        return new Vector(x,y,z);
    }
    public String toString(){
        return "{"+x+", "+y+", "+z+"}";
    }
}

矩阵类:

package math;

import java.util.Arrays;

public class Matrix{
    private double[][] coef;

    private Matrix(int row,int col){
        if(row>0 && col>0)
            coef = new double[row][col];
        else
            throw new IllegalArgumentException("Both the number of rows and columns must be strictly positive");
    }
    public Matrix(double[][] data){
        if(data != null){
            int len;
            if(data[0]!=null)
                if(data.length==0)
                    throw new IllegalArgumentException("Both the number of rows and columns must be strictly positive");
                else
                    len = data[0].length;
            else
                throw new NullPointerException();
            if(len==0)
                throw new IllegalArgumentException("Both the number of rows and columns must be strictly positive");
            for(int i=1; i<data.length;i++){
                if(data[i]!=null){
                    if(data[i].length!=len)
                        throw new IllegalArgumentException("All the elements of the argument must have the same lenght");
                }else
                    throw new NullPointerException();
            }

            coef = new double[data.length][len];
            for(int i=0;i<data.length;i++)
                for(int k=0;k<len;k++)
                    coef[i][k] = data[i][k];
        }else
            throw new NullPointerException();
    }

    public static Matrix fill(int row,int col,double value){
        Matrix mat = new Matrix(row,col);
        for(int i=0;i<row;i++)
            for(int k=0;k<col;k++)
                mat.coef[i][k]=value;
        return mat;
    }
    public static Matrix identity(int size){
        Matrix mat = fill(size,size,0);
        for(int i=0;i<size;i++)
            mat.coef[i][i]=1;
        return mat;
    }
    public static Matrix diag(double[] data){
        Matrix mat = fill(data.length,data.length,0);
        for(int i=0;i<data.length;i++)
            mat.coef[i][i] = data[i];
        return mat;
    }

    public int[] size(){
        return new int[]{coef.length,coef[0].length};
    }
    public double[][] get(){
        double[][] ret = new double[coef.length][coef[0].length];
        for(int i=0;i<coef.length;i++)
            for(int k=0;k<coef[0].length;k++)
                ret[i][k] = coef[i][k];
        return ret;
    }

    public double get(int row,int col){
        return coef[row][col];
    }

    public boolean isSquare(int size){
        return coef.length==size && coef[0].length==size;
    }

    public Matrix add(Matrix mat){
        int[] dim = size();
        if(Arrays.equals(dim,mat.size())){
            for(int i=0;i<dim[0];i++)
                for(int k=0;k<dim[1];k++)
                    coef[i][k]+=mat.coef[i][k];
            return this;
        }else
            throw new ArithmeticException("Both matrices must have the same size");
    }
    public Matrix multiply(Matrix mat){
        int[] dim1=size(), dim2=mat.size();
        if(dim1[1]==dim2[0]){
            Matrix ret = Matrix.fill(dim1[0],dim2[1],0);
            for(int i=0;i<dim1[0];i++)
                for(int k=0;k<dim2[1];k++)
                    for(int j=0;j<dim1[0];j++)
                            ret.coef[i][k] += coef[i][j]*mat.coef[j][k];
            return ret;

        }else
            throw new ArithmeticException("The sizes of the matrices do not match");
    }
    public Matrix multiply(double x){
        int[] dim = size();
        for(int i=0;i<dim[0];i++)
            for(int k=0;k<dim[1];k++)
                coef[i][k]*=x;
        return this;

    }

    public String toString(){
        String str="";
        int[] dim = size();
        for(int i=0;i<dim[0];i++){
            str+="[";
            for(int k=0;k<dim[1];k++)
                str+=coef[i][k]+",";
            str=str.substring(0,str.length()-1);
            str+="]\n";
            }
        return str.substring(0,str.length()-1);
    }
}

三角形类:

package pckg;

import java.awt.Color;
import java.awt.Graphics;

import math.Matrix;
import math.Vector;

public class Triangle{
    private Vector A,B,C;

    public Triangle(Vector a, Vector b, Vector c){
        A=a.clone();B=b.clone();C=c.clone();
    }

    public double[][] get(){
        return new double[][]{A.get(),B.get(),C.get()};
    }
    public Vector[] getVect(){
        return new Vector[]{A.clone(),B.clone(),C.clone()};
    }

    public Triangle clone(){
        return new Triangle(A.clone(),B.clone(),C.clone());
    }
    public String toString(){
        return "["+A.toString()+", "+B.toString()+", "+C.toString()+"]";
    }

    public void paint(Graphics g, Vector vPoint, Vector vDir, int width, int height, double far, double near, double fov, double time){
        double F = 1/Math.tan((fov*Math.PI)/360);
        Vector vect[] = new Vector[]{A.clone(),B.clone(),C.clone()}/*, mid = new Vector(0,0,0), pos[]=new Vector[3]*/;
        Matrix proj = Matrix.diag(new double[]{((double)height/width)*F,F,far/(far-near)}),
                scale = Matrix.diag(new double[]{-width/2,-height/2,1}),
                rot = (new Matrix(new double[][] {{Math.cos(time),-Math.sin(time),0},{Math.sin(time),Math.cos(time),0},{0,0,1}})).multiply(
                        new Matrix(new double[][] {{1,0,0},{0,Math.cos(time/2),-Math.sin(time/2)},{0,Math.sin(time/2),Math.cos(time/2)}}));

        //Transformation
        for(Vector v:vect)
            v.apply(rot).add(new Vector(0,0,3));

        //Projection
        for(Vector v:vect){
            double z;
            z = v.getZ();
            v.apply(proj).sub(new Vector(0,0,near*far/(far-near))).divide(z);
            v.sub(new Vector(1,1,0)).apply(scale);
        }
        g.setColor(Color.white);
        g.drawPolygon(new int[]{((Number)(vect[0].getX()+0.5)).intValue(),((Number)(vect[1].getX()+0.5)).intValue(),((Number)(vect[2].getX()+0.5)).intValue()},
                        new int[]{((Number)(vect[0].getY()+0.5)).intValue(),((Number)(vect[1].getY()+0.5)).intValue(),((Number)(vect[2].getY()+0.5)).intValue()}, 3);
    }
}

我为尝试显示的多维数据集做了一个枚举:

package pckg.tests;

import java.util.Arrays;

import math.Vector;
import pckg.Triangle;

public enum Cube{

    cube (new Vector(1,0,0),new Vector(1,1,0),new Vector(0,1,0),new Vector(0,0,0),new Vector(0,0,1),new Vector(0,1,1),new Vector(1,1,1),new Vector(1,0,1));

    public final Triangle[] faces;

    private Cube(Vector a, Vector b, Vector c, Vector d, Vector e, Vector f, Vector g, Vector h){
        faces = new Triangle[12];

        //South
        faces[0] = new Triangle(a,b,c);
        faces[1] = new Triangle(c,d,a);
        //East
        faces[2] = new Triangle(d,c,f);
        faces[3] = new Triangle(f,e,d);
        //North
        faces[4] = new Triangle(e,f,g);
        faces[5] = new Triangle(g,h,e);
        //West
        faces[6] = new Triangle(h,g,b);
        faces[7] = new Triangle(b,a,h);
        //Up
        faces[8] = new Triangle(b,g,f);
        faces[9] = new Triangle(f,c,b);
        //Down
        faces[10] = new Triangle(a,d,e);
        faces[11] = new Triangle(e,h,a);
    }

    public Triangle[] get(){
        return Arrays.copyOf(faces, 12);
    }
}

如前所述,某些事情并没有达到我想要的状态,但是我找不到位置。任何帮助将不胜感激。

0 个答案:

没有答案