How can I fix the skewing thats going in my graphics renderer caused because of my fov?

21 Views Asked by At

I am creating a 3D-graphics renderer in Java using only awt, and I have run into a problem with my fov. When the fov is set to something reasonable like 30degrees all the objects within the scene skew drastically towards the top-left corner of the screen.

public class Camera {
    private float aspectRatio = 1.33f;
    private float fov = (float) Math.toRadians(30);         
    private float near = 0.01f;         
    private float far = 1000;       
    private float moveSpeed = 0.02f;

    private Vector3D cameraPos = new Vector3D(0, 0, 0);
    private Vector3D cameraFront = new Vector3D(0, 0, 1);
    private Vector3D cameraUp = new Vector3D(0, 1, 0);

    private float yaw = 1.58f;
    private float pitch = 0;

    public Matrix4x4 perspectiveViewMatrix() {
        var vM = viewMatrix(cameraPos, cameraPos.add(cameraFront), cameraUp);
        return perspectiveMatrix().multiply(vM);
    }

    private Matrix4x4 perspectiveMatrix() {
        return new Matrix4x4(new float[][] { 
                                { (float) (1 / (aspectRatio * Math.tan(fov / 2))), 0, 0, 0 },
                { 0, (float) (1 / (Math.tan(fov / 2))), 0, 0 },
                                { 0, 0, far / (far - near), -(far * near) / (far - near) },
                { 0, 0, 1, 0 } });
    }

    private Matrix4x4 viewMatrix(Vector3D cameraPos, Vector3D cameraTarget, Vector3D up) {
        Vector3D cDirection = cameraPos.subtract(cameraTarget).normalize();
        Vector3D cRight = up.cross(cDirection).normalize();
        Vector3D cUp = cDirection.cross(cRight);

        return new Matrix4x4(new float[][] { 
                                { cRight.getX(), cRight.getY(), cRight.getZ(), 0 },
                { cUp.getX(), cUp.getY(), cUp.getZ(), 0 },
                { cDirection.getX(), cDirection.getY(), cDirection.getZ(), 0 }, 
                                { 0, 0, 0, 1 } })
                .multiply(Matrix4x4.translation(cameraPos));
    }

    public void moveCamera(InputHandler input, List<Object3D> objects) {
        float sense = 0.01f;
        for (Direction key : input.getKeys()) {
            switch (key) {
            // move y-axis
            case UP -> moveYaxis(-moveSpeed);
            case DOWN -> moveYaxis(moveSpeed);

            // move z-axis
            case FORWARD -> moveZaxis(-moveSpeed);
            case BACKWARD -> moveZaxis(moveSpeed);

            // move x-axis
            case LEFT -> moveXaxis(-moveSpeed);
            case RIGHT -> moveXaxis(moveSpeed);

            // rotate-y
            case C_LEFT -> changeYaw(sense);

            case C_RIGHT -> changeYaw(-sense);

            // rotate-x
            case C_UP -> changePitch(sense);
            case C_DOWN -> changePitch(-sense);
            }
        }
        
    }

    private void moveYaxis(float i) {
        cameraPos.addtoY(i);
    }

    private void moveZaxis(float i) {
        cameraPos.addTo(cameraFront.scale(i));
    }

    private void moveXaxis(float i) {
        cameraPos.addTo(cameraFront.cross(cameraUp).normalize().scale(i));
    }
    
    private void updateCamFront() {
        float dirX = (float) (Math.cos(yaw) * Math.cos(pitch));
        float dirY = (float)  Math.sin(pitch);
        float dirZ = (float) (Math.sin(yaw) * Math.cos(pitch));

        Vector3D direction = new Vector3D(dirX, dirY, dirZ);
        cameraFront = direction.normalize();
    }
    
    private void changeYaw(float sense) {
        yaw += sense;
        updateCamFront();
    }

    private void changePitch(float sense) {
        pitch += sense;
        if (pitch > 89) {
            pitch = 89;
        }

        if (pitch < -89) {
            pitch = -89;
        }
        updateCamFront();
    }
}
public class Renderer extends JPanel{
    private static final long serialVersionUID = -4576361545526054095L;

    private transient Camera camera;
    private transient Scene scene;
    
    Renderer(Camera c, Scene s){
        setBackground(Color.gray); 
        camera = c;
        scene = s;
    }
    
    public void render() {
        repaint();
    }
        
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        // Enable anti-aliasing for smoother rendering
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,                  
                                         RenderingHints.VALUE_ANTIALIAS_ON);

        // Set the fill color to red
        g2d.setColor(Color.yellow);

        Matrix4x4 pvm = camera.perspectiveViewMatrix();

        for (Object3D obj : scene.getObjects()) {
            Mesh mesh = obj.getMesh();
            List<Vector3D> vertices = mesh.getVertices();

            Matrix4x4 mat = pvm.multiply(obj.getModelMatrix());

            for (Triangle tri : mesh.getTriangles()) {
                Vector4D result1 = mat.multiply(vertices.get(tri.v1).homogeneous());
                Vector4D result2 = mat.multiply(vertices.get(tri.v2).homogeneous());
                Vector4D result3 = mat.multiply(vertices.get(tri.v3).homogeneous());

                double pt1x = result1.getX() / result1.getW();
                double pt1y = result1.getY() / result1.getW();

                double pt2x = result2.getX() / result2.getW();
                double pt2y = result2.getY() / result2.getW();

                double pt3x = result3.getX() / result3.getW();
                double pt3y = result3.getY() /  result3.getW();

                // Create a GeneralPath for the current triangle
                GeneralPath path = new GeneralPath();
                path.moveTo(pt1x, pt1y);
                path.lineTo(pt2x, pt2y);
                path.lineTo(pt3x, pt3y);
                path.closePath();

                // Fill the triangle
                g2d.fill(path);
            }
        }
    }
}

I tried smaller values and 0.001radians seem to be the closest to what I want; but that value seems way to small to have set up the camera right.

0

There are 0 best solutions below