java - 如何实现透视投影
问题描述
我有一个光线追踪的迷你项目,我已经开始用 Java 编写了。
我自己实现了所有东西,没有任何像 OpenGl 这样的库,一切都是我从头开始实现的——相机、渲染器、图像写入器等......问题是,当我放置一个对象时——比如说——一个球体,那就是没有定位在屏幕的中心,它变成了某种椭圆形。对于(有限)圆柱体也是如此 - 它出于某种原因径向延伸。
渲染过程是这样的:我的相机是用原点、方向和向上向量定义的,右边的向量是叉积方向 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)。所以那个乘法的返回值就是那个vector3。
为了计算视场角 θ,我计算了 2 条光线与视平面两侧之间的角度。
如果这里太乱,这里是一些代码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());
}
}
我的矩阵类字段及其相关功能:
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());
}
}
我希望任何对象在渲染后都能保持其原始形状,无论其位置如何。
我能做的一件事是让相机离物体真的很远,这样我就不会注意到那些延伸。但我想从根本上解决这个问题。
解决方案
推荐阅读
- spring-boot - ElasticSearch 中的批量更新插入异常
- install4j - install4j:“执行启动器”操作不适用于“需要管理员”执行级别
- r - 如何从一个数据框中的模式中获取另一个数据框?
- python - 具有第 i 个元素的 Torch 张量作为所有先前的乘积
- amazon-web-services - Terraform 嵌套 for_each aws_acm_certificate domain_validation_options
- python - 从复杂的多段落文档中提取单词并将其输出为多行逗号分隔文件
- r - 错误:无法加载外部实体“http://.......”
- swift - SwiftUI:拖入空列表时崩溃
- amazon-web-services - aws ec2 describe-instances 返回空数组
- angularjs - 如何避免在angularJs中从一个选项卡导航到另一个选项卡时多次触发$on?