如何实施透视投影

时间:2019-06-16 10:58:16

标签: java perspectivecamera

我有一个Ray-Tracing微型项目,已经开始用Java编写。

我自己实现了所有东西,没有像OpenGl这样的任何库,一切都是我从头开始实现的,例如Camera,Renderer,Image Writer等。 问题是,当我放置一个物体(例如,一个不位于屏幕中心的球体)时,它具有某种椭圆形的形状。对于(有限)圆柱体相同-由于某种原因,它会径向拉伸。

渲染过程如下: 我的相机定义了原点,方向和向上矢量,右边的矢量是叉积方向X向上。 对于视平面中的每个点,我构造从该点到相机原点的射线,并进行拍摄以找到与场景中每个几何图形的交点。对于每个几何图形,我采用最接近的交点,计算其颜色,然后将其颜色写入位于视图平面该点的像素(我从中构造了射线)。

现在,我已经阅读了很多有关Perspective Projection的文章,并且我尝试使用此链接来实现它:http://ogldev.atspace.co.uk/www/tutorial12/tutorial12.html 但未能使用它。 我已经在Transform类中实现了它,而我试图做的就是将Projection Matrix M乘以我在场景中找到的任何最接近的交点p。 不幸的是,由于某种原因,结果保持不变。

注意:M * p的乘积是一个新的向量4(v1,v2,v3,v4),因此它已转换为一个新的向量3(v1 / v4,v2 / v4,v3 / v4)。 因此,该乘法的返回值就是vector3。

为了计算视野角θ,我计算了两束光线到视平面两侧的夹角。

这里是某些代码
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);
    }
}

我的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());
     }
}

我的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

我可以做的一件事是使相机真正远离物体,这样我就不会注意到那些伸展。但是我想从头解决这个问题。

0 个答案:

没有答案