首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何将ZBuffer与简单多边形一起使用?

如何将ZBuffer与简单多边形一起使用?
EN

Stack Overflow用户
提问于 2017-05-05 05:52:50
回答 2查看 1.3K关注 0票数 1

我一直在写一个简单的3d渲染器,并且一直在研究绘制顺序。引擎将3d多边形(按正确绘制顺序的3d点组)渲染到2d空间,返回表示给定多边形投影的2d点列表。我这样做的方法可能有点非正统,因为我想看看我自己是否能够做到这一点,所以我将我的代码附加在下面进行投影:

代码语言:javascript
复制
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;
}

目前,我通过对三维多边形进行排序来确定绘制顺序,然后按照最远的多边形到最近的多边形的顺序进行绘制。但是,由于绘制顺序完全基于多边形上最近点到相机的距离来确定,因此有时会有几种情况使算法无法正常工作,如此视频所示:

https://youtu.be/olTOTOCw42M

我对Z Buffer做了很多研究,其概念非常简单--实际上与我正在做的非常相似。据我所知,对于每个渲染的像素,对同一像素上渲染的所有点进行比较,并显示与相机最接近的z深度。但是,考虑到在这种情况下,我处理的唯一点是构成每个多边形的角的点,我不知道一个好的方法来比较多边形中包含的任何点的z深度,而不仅仅是角点。

对于这个问题,我有两种可能的解决方案:

1)将每个多边形分割为多个较小的多边形。当我在python中模拟渲染器时,我从来没有添加Z深度排序,但我确实将每个多边形划分为多个较小的多边形,以便可以轻松地单独照亮每个多边形,结果如下所示:

http://imgur.com/a/U3Xke

然而,这是非常昂贵的,因为许多投影点被多次投影,因为它们的值是通过计算相邻多边形的投影来确定的。也许有一种合法的方法可以做到这一点,但对我来说,这似乎太粗糙了,以至于不能正确。

2)找到每个3d多边形所在的平面,将其绑定到多边形的形状上,然后求解通过某个视角定向的各个扫描线与这些平面的交点,然后选择与相机的z深度最接近的交点显示在该扫描线的像素处。这样,每个像素将被单独渲染,而不是每个多边形的点被投影,然后使用java的多边形填充方法进行填充。然而,我不确定如何“绑定”一个平面,使它不会延伸到多边形的边界之外,这对我来说有点棘手,因为目前的数学对我来说有点太高级了。如果这是应该这样做的方式,我可以学习它,我只是想事先确保这是一个可行的方法。

3)将每个多边形分割成一组多个点而不是更小的多边形:我认为这种方法是有缺陷的,因为好的渲染所需的点数(即每个像素一个3d点,不需要在同一多边形形状上渲染多个3d点到完全相同的像素上,或者具有太少的3d点使得像素在渲染过程中被“跳过”)随z深度而变化,并且每次移动相机时,计算放置这些点的位置的公式似乎很难表达,并且运行成本很高。

EN

回答 2

Stack Overflow用户

发布于 2017-05-05 18:03:12

使用排序表,这是Playstation 1使用的算法。

将Z范围划分为N个大小相等的部分。

创建指向polygons.

  • Every帧的N个指针的数组,将该数组清为零。

  • 对于要绘制的每个三角形:
  • 计算相应的Z索引i。
  • 将其插入到Orderi中,就像使用链接的索引一样

  • 反向遍历数组(从后到前),在遍历

时绘制三角形

C代码

代码语言:javascript
复制
Triangle *Order[256];

void Clear() {
    memset(Order,0,sizeof(Order));
}

void Insert(Triangle *tri) {
    int index = (tri->averageZ-zNear) * 256 / (zFar - zNear);
    tri->next = Order[index];
    Order[index] = tri;
}

void Paint() {
    for(int i=255;i>=0;i--)
        for(Triangle *tri=Order[i];tri;tri=tri->next)
            DrawTriangle(tri);
}

来源: Fabien "ryg“Giesen (以Farbrausch成名)有一个题为”当光速不够快“的演讲,他在演讲中提出了这个想法,包括这段代码。

票数 1
EN

Stack Overflow用户

发布于 2017-05-05 18:15:40

我认为你曲解了Z缓冲区的想法,而最接近事实的是你的解决方案3) --将多边形分割成单个像素。

Z缓冲区以像素为单位工作,是的,有很多Z比较,但这就是它的工作方式。您不能将其简化为仅使用多边形的特定顶点。

我假设你有一些网格,比如说“颜色”结构,你要用你的渲染来填充。这将是您的目标图像。您需要添加另一个具有相同大小的浮点数网格-这将是您的Z缓冲区。在开始的时候,你用一些大的值填充你的Z缓冲区,比如1000000。

暂时忽略多边形的排序-Z缓冲区将为您解决此问题。您可以稍后添加排序来测试不同绘制顺序之间的性能差异,但这并不是使其工作所必需的。

现在,您需要光栅化阶段,在此阶段,您将传递多边形角并返回此多边形覆盖的所有像素的列表。你可以使用扫描线来计算它,正如你在2)中提到的那样。我建议你只为一个三角形编写光栅化,并将所有的多边形分割成三角形,这将使你的代码更简单。如果你愿意,你可以从这个阶段返回一个像素列表(list在这种情况下会很慢,但对于学习目的来说是很好的。在这个阶段直接填充像素网格会更好,而不是在内存中累积这些数据),但您需要一个重要的更改-像素需要正确的Z值,除了X和Y。

当你有这样的光栅化像素列表时,你只需要把它们放到像素网格中,这就是Z测试发生的地方。迭代每个像素并:使用像素屏幕位置(X,Y)从Z缓冲区读取当前Z值。如果该值大于当前像素的Z,则在颜色缓冲区中写入像素颜色,并在Z缓冲区中写入像素Z。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43793357

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档