我一直在寫一個簡單的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