Как реализовать перспективную проекцию - PullRequest
0 голосов
/ 16 июня 2019

У меня есть мини-проект Ray-Tracing, который я начал писать на Java.

Я реализовал все сам, без каких-либо библиотек, таких как OpenGl, все было реализовано мной с нуля - Камера, Renderer, Image Writer и т. Д. Дело в том, что когда я помещаю объект, скажем, сферу, которая не расположена в центре экрана, она приобретает какую-то эллиптическую форму.то же самое для (конечного) цилиндра - по какой-то причине он растягивается радиально.

Процесс рендеринга происходит следующим образом: моя Камера определяется с исходной точкой, направлениями и векторами вверх, правый вектор - это поперечное направление произведения X вверх,для каждой точки в плоскости обзора я строю луч от этой точки до начала камеры и снимаю его, чтобы найти пересечения с каждой геометрией в сцене.для каждой геометрии я беру ближайшую точку пересечения, вычисляю ее цвет и записываю ее цвет в пиксель, расположенный в этой точке плоскости обзора (из которой я построил луч).

Теперь яЯ много читал о перспективном проектировании, и я пытался реализовать его, используя эту ссылку: http://ogldev.atspace.co.uk/www/tutorial12/tutorial12.html Но не смог его использовать.Я реализовал это в своем классе Transform, и я пытался умножить матрицу проекции M на любую ближайшую точку пересечения p, которую я нашел в моей сцене.К сожалению, результат по какой-то причине остается прежним.

ПРИМЕЧАНИЕ. Умножение M * p - это новый вектор4 (v1, v2, v3, v4), такой, что он трансформируется в новый вектор3 (v1 / v4)., v2 / v4, v3 / v4).Таким образом, возвращаемое значение этого умножения - это вектор 3.

Чтобы вычислить угол поля зрения θ, я рассчитал угол между двумя лучами и двумя сторонами плоскости обзора.

ЗДЕСЬ НЕКОТОРЫЙ КОД
Ссылка на GitHub, если здесь слишком грязно: https://github.com/elyasaf755/3D-Engine

Мои поля класса Transform и соответствующие функции :

public class Transform {
    private static double _zNear;
    private static double _zFar;
    private static double _width;//screen width
    private static double _height;//screen height
    private static double _fov;//field of view

    private Vector3D _translation;
    private Vector3D _rotation;
    private Vector3D _scale;

    public Matrix4 getTransformation(){
        Matrix4 translationMatrix = new Matrix4().initTranslation(
                this._translation.getPoint().getX().getCoord(),
                this._translation.getPoint().getY().getCoord(),
                this._translation.getPoint().getZ().getCoord()
        );

        Matrix4 rotationMatrix = new Matrix4().initRotation(
                _rotation.getPoint().getX().getCoord(),
                _rotation.getPoint().getY().getCoord(),
                _rotation.getPoint().getZ().getCoord()
        );

        Matrix4 scaleMatrix = new Matrix4().initScale(
                _scale.getPoint().getX().getCoord(),
                _scale.getPoint().getY().getCoord(),
                _scale.getPoint().getZ().getCoord()
        );

        return translationMatrix.mult(rotationMatrix.mult(scaleMatrix));
    }

    public Matrix4 getProjectedTransformation(){

        Matrix4 transformationMatrix = getTransformation();
        Matrix4 projectionMatrix = new Matrix4().initProjection(_fov, _width, 
                                                    _height, _zNear, _zFar);

        return projectionMatrix.mult(transformationMatrix);
    }
}

Поля класса My Matrix4 и соответствующие функции :

public class Matrix4 {
    private double[][] _m;

    public Matrix4 initProjection(double fov, double width, double height, 
                                             double zFar, double zNear){

        double ar = width / height;
        double tanHalfFov = Math.tan(Math.toRadians(fov / 2));
        double zRange = zNear - zFar;

        setRow(0,1 / (tanHalfFov * ar),0,           0,                     0);
        setRow(1,0,                    1/tanHalfFov,0,                     0);
        setRow(2,0,                    0,           (-zNear - zFar) / zRange,2 
                                                     * zFar * zNear / zRange);
        setRow(3,0,                    0,           1,                     0);

        return this;
    }

    public Matrix4 mult(Matrix4 matrix) {
        Matrix lhs = new Matrix(_m);
        Matrix rhs = new Matrix(matrix.getMatrix());

        Matrix result = lhs.mult(rhs);//simple matrix multiplication


        return new Matrix4(result.get_matrix());
     }
}

Поля класса My Matrix и соответствующие функции :

public class Matrix {
    private double[][] _matrix;
    private int _numOfRows;
    private int _numOfCols;

    public Matrix mult(Matrix matrix) {
        if (_numOfCols != matrix.getRows())
            return null;

        Matrix result = new Matrix(_numOfRows, matrix.getColumns());


        for (int i = 0; i < _numOfRows; i++)
        {
            for (int j = 0; j < matrix.getColumns(); j++)
            {
                BigDecimal sum = new BigDecimal(0, MathContext.UNLIMITED);

                for (int k = 0; k < matrix.getRows(); k++)
                {
                    BigDecimal temp = new BigDecimal(
                                      _matrix[i][k]*matrix.get_element(k, j), 
                                      MathContext.UNLIMITED);
                    sum = sum.add(temp);
                }

                Coordinate temp = new Coordinate(sum.doubleValue());

                result.set_element(i, j, temp.getCoord());
            }
        }

        return result;
    }
}

Поля класса «Моя камера» и соответствующие функции :

public class Camera {
private Point3D _origin;
private Vector3D _direction;
private Vector3D _up;
private Vector3D _right;

public Ray constructRayThroughPixel(int Nx, int Ny, int i, int j, double 
                screenDistance, double screenWidth, double screenHeight){
    //Fix pixel locations
    i = Nx - i - 1;
    //j = Ny - j;


    Point3D p0 = get_origin();
    Vector3D direction = get_direction();
    Vector3D up = get_up();
    Vector3D right = get_right();

    //Image center point
    Point3D Pc = p0.add(direction.scale(screenDistance));

    //Pixel ratios
    double Rx = screenWidth / Nx; //Pixel width
    double Ry = screenHeight / Ny; //Pixel height

    //Center pixel
    double Xi = (i - Nx / 2.0)*Rx + Rx / 2.0;
    double Yj = (j - Ny / 2.0)*Ry + Ry / 2.0;

    Point3D p_ij;
    if (Xi == 0 && Yj == 0){
        p_ij = new Point3D(Pc);
    }
    else if (Xi == 0){
        p_ij = new Point3D(Pc.add(Vector3D.ZERO.subtract(up.scale(Yj))));
    }
    else if (Yj == 0){
        p_ij = new Point3D(
               Pc.add((right.scale(Xi)).subtract(Vector3D.ZERO)));
    }
    else{
        p_ij = new Point3D
               (Pc.add((right.scale(Xi)).subtract(up.scale(Yj))));
    }

    Vector3D v_ij = p_ij.subtract(p0);

    return new Ray(p0, v_ij.normalized());
}

}

Я ожидаю, что любой объект сохранит свою первоначальную форму после рендеринга, независимо от его положения.

Когда сфера не центрирована When the sphere is not centered

Когда сфера центрирована When the sphere is centered

Одна вещь, которую яМожно сделать камеру действительно очень далеко от объектов, поэтому я не заметил бы этих участков.Но я хочу исправить это с нуля.

...