我一直在写一个简单的3d渲染器,并一直在研究绘制顺序。该引擎将3d多边形(按正确绘图顺序的3d点组)渲染到2d空间中,返回代表给定多边形投影的2d点列表。我这样做的方法可能有点非正统的,因为我想看看我是否能够做我自己,所以我重视我的代码如下预测:如何用简单的多边形使用ZBuffer?
public class Camera {
/*position is the position of the camera, x, y, z;
cameraRotation is the rotation of the camera, in the order of rotation about x, rotation about y, rotation about z
the camera initially faces the +x direction
*/
private double focalAngle;
private double[] position, cameraRotation, cameraDirectionVector, cameraXVector, cameraZVector;
private double[][][] rotationMatrices = new double[3][3][3];
private double[][] compoundedRotationMatrices;
public Camera(double[] positionIn, double[] cameraRotationIn, double focalAngleIn){
position = positionIn;
focalAngle = focalAngleIn;
cameraRotation = cameraRotationIn;
updateRotation();
}
private void updateRotation(){
updateRotationMatrices();
updateCameraDirectionVector();
}
private void updateRotationMatrices(){
compoundedRotationMatrices = Matrix.getCompoundedRotationMatrix(cameraRotation[0], cameraRotation[1], cameraRotation[2]);
}
private void updateCameraDirectionVector(){
double[] xVector = {1,0,0};
double[] yVector = {0,-1,0};
double[] zVector = {0,0,1};
cameraDirectionVector = Matrix.vecMultiply(compoundedRotationMatrices, xVector);
cameraXVector = Matrix.vecMultiply(compoundedRotationMatrices, yVector);
cameraZVector = Matrix.vecMultiply(compoundedRotationMatrices, zVector);
}
public ArrayList<int[][]> getPolygonProjections(ArrayList<double[][]> polySets, double screenWidth, double screenHeight){
ArrayList<int[][]> outPoints = new ArrayList();
for(int i = 0; i < polySets.size(); i++){
int[][] polyPoints = new int[2][polySets.get(i).length];
/*in the calculation of proejctions, divide by zeros and NaNs can pop up,
polygonsLegitimate boolean keeps track of whether the polygon being drawn can be drawn without error,
and the while loop stops calcuating the polygon once it determines it cannot be properly drawn
*/
boolean polygonsLegitimate = true;
int j = 0;
while(j < polyPoints[0].length && polygonsLegitimate){
int[] xy = getVectorProjection(polySets.get(i)[j], screenWidth, screenHeight);
if(xy != null){
polyPoints[0][j] = xy[0];
polyPoints[1][j] = xy[1];
}else{
polygonsLegitimate = false;
}
j++;
}
if(polygonsLegitimate){
outPoints.add(polyPoints);
}
}
return outPoints;
}
private int[] getVectorProjection(double[] vector, double screenWidth, double screenHeight){
double[] subVector = Vector.subtract(vector, position);
double zDepth = getZDepthOfVector(subVector);
if(zDepth > 0){
double sliceSize = getSliceSizeAtDepth(zDepth);
double cameraXProj = Vector.dot(subVector, cameraXVector);
double cameraZProj = Vector.dot(subVector, cameraZVector);
double xPercent = (cameraXProj+(sliceSize/2))/sliceSize;
double zPercent = (cameraZProj+(sliceSize/2))/sliceSize;
int[] xy = {(int)(xPercent * screenWidth),(int)((((1-zPercent) * screenWidth))-(screenHeight/2))};
return xy;
}
return null;
}
public double getZDepthOfVector(double[] vector){
return Vector.dot(cameraDirectionVector, vector);
}
private double getSliceSizeAtDepth(double zDepth){
return 2.0*Math.cos(focalAngle)*zDepth;
}
目前,我决定通过排序绘制顺序三维多边形除以多边形最靠近角落到相机的距离,然后按最远多边形到最近多边形的顺序绘制。然而,由于抽签顺序是完全基于多边形到相机的最近点的距离来确定,有防止算法有时正常工作了几cornercases,如本视频:
我已经在Z缓冲区做了大量的研究,而且这个概念很简单 - 实际上与我正在做的非常相似。就我的理解而言,对于每个渲染像素,将比较同一像素上呈现的所有点,并显示距离摄像机最近的z深度。然而,考虑到在这种情况下,我正在使用的唯一要点就是组成每个多边形拐角的那些点,我不知道一个好方法来比较多边形中包含的任何点的z深度而不仅仅是在角落。
我有这个问题有两个可能的解决方案:)
1拆分每个多边形成多个更小的多边形。当我嘲笑Python中的渲染器时,我从未在Z深度排序中添加,但是我确实将每个多边形划分为多个较小的多边形,这样我可以非常容易地分别点亮每个多边形,结果如下所示:
然而,这是非常昂贵的,因为许多投影点被多次投影,因为它们的值是通过计算相邻多边形的投影来确定的。也许有一个合理的方法来解决这个问题,但对我来说,看起来太粗糙是没有道理的。
2)找到每个三维多边形所在的平面,将其限定为多边形的形状,然后求解与这些平面通过视角定向的各个扫描线的交点,然后选择与距摄像机最近的z深度显示在该扫描线的像素处。这样,不是每个多边形的投影点用java的多边形填充方法填充,每个像素将被单独渲染。然而,我不确定如何“绑定”一架飞机,使其不会超出多边形的边界,对我来说理解起来有点棘手,因为目前数学对我而言有点过于先进。如果这是应该完成的方式,我可以学习,我只是想确保这是一种可行的方法。 3)将每个多边形分成许多点的集合,而不是分成更小的多边形:我认为这种方法会有缺陷,因为一个好的渲染所需的点的数量(即每个像素一个3d点,不需要相同多边形形状上的多个3d点渲染到完全相同的像素上,或者具有太少的3d点以致像素在渲染过程中被“跳过”)随Z深度和用于计算将这些点放置在何处的公式每次移动相机似乎都很难制定并且运行起来很昂贵。
这对我的问题有更多的了解,但我怎样才能单独计算在某个像素上绘制的多边形上的3d点的Z值?我知道一个光线是通过与给定像素的x和y位置相交的角度从相机的位置投射出来的,但是我怎样才能确定这个光线通过像素与多边形相交的Z深度(这只是一个列表3D点在每个角落)? – pete2fiddy
为了便于说明,在选项3中,我实际上是在讨论如何将3d多边形点分成许多3d点,以便在投影后将一个点用于屏幕上的一个像素。然而,我不确定如何去解决这个问题(而且这看起来也不正确) - 从我所知道的情况来看,不是将多边形分成多个3d点,而是要让我画出它因为我现在只使用它的角点,然后确定多边形的每个绘制像素之后的多边形的Z深度,然后进行Z深度比较。 – pete2fiddy
您可以通过插值Z值从多边形顶点计算像素Z. Raycast对此会慢很多。从现在开始,将您的聚合物分解成三角形并考虑三角形。现在你正在绘制一个三角形,它的三个角都在屏幕上,每个都有X,Y,Z值。对于此三角形内的每个像素,可以使用像素到角点的距离来插值Z值。它被称为重心坐标,你可以在这里阅读它https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes – kolenda