0

我正在实现一个 Z 缓冲区来确定应该在一个充满三角形的简单场景中绘制哪些像素。我有一个三角形、一个顶点、一个向量(当然是数学的 (x, y, z) 类型)的结构表示,以及一个将单个像素绘制到屏幕上的函数。这是我拥有的结构:

struct vertex{
  float x, y, z; 
  ... //other members for lighting, etc. that will be used later and are not relevant here 
}; 

struct myVector{
  float x, y, z; 
}; 

struct triangle{
  ... //other stuff 
  vertex v[3]; 
}; 

不幸的是,当我扫描将我的三角形转换为屏幕时,它依赖于计算深度来确定可见和要绘制的内容,我得到了不正确/不切实际的 Z 值(例如,三角形中某个点的深度超出了所有 3 个顶点的深度范围)!我一遍又一遍地查看我的代码,无法弄清楚我的数学是否有问题或者我在某个地方有粗心的错误,所以我将尝试准确地展示我正在尝试做的事情,希望其他人可以看到一些我不。(而且我仔细研究过确保浮点值仍然是浮点值,我正确地传递了参数等等,所以这真的令人困惑!)

总的来说,我的扫描转换算法会像这样(伪代码)在扫描线上填充像素:

for all triangles{  
  ... //Do edge-related sorting stuff, etc...get ready to fill pixels 

  float zInit;      //the very first z-value, with a longer calculation 
  float zPrev;      //the "zk" needed when interpolating "zk+1" across a scan line 

  for(xPos = currentX at left side edge; xPos != currentX at right side edge; currentX++){

    *if this is first pixel acorss scan line, calculate zInit and draw pixel/store z if depth is less   
     than current zBuffer value at this point. Then set zPrev = zInit. 

    *otherwise, interpolate zNext using zPrev. Draw pixel/store z if depth < current zBuffer value at 
     this point. Then set zPrev = zNext. 

  }
... //other scan conversion stuff...update x values, etc. 
}

为了获得每条扫描线的 zInit 值,我考虑平面方程 Ax + By + Cz + D = 0 并重新排列它以获得 z = -1*(Ax + By + D)/C,其中 x 和 y 是分别作为跨扫描线的当前 x 值和当前扫描线值本身插入。

对于扫描线上的后续 z 值,我内插为 zk+1 = zk - A/C,其中 A 和 C 来自平面方程。

为了获得这些 z 计算的 A、B 和 C,我需要由vertex v[3]当前三角形的 3 个顶点(数组)定义的平面的法线向量。为了得到这个法线(我在代码中命名为 planeNormal),我定义了一个叉积函数:

 myVector cross(float x1, float y1, float z1, float x2, float y2, float z2)
 {
   float crX = (y1*z2) - (z1*y2);
   float crY = (z1*x2) - (x1*z2);
   float crZ = (x1*y2) - (y1*x2);

   myVector res;
   res.x = crX;
   res.y = crY;
   res.z = crZ;
   return res;   
 }

为了获得平面方程/我的 z 计算的 D 值,我使用平面方程 A(x-x1) + B(y-y1) + C(z-z1) = 0,其中 (x1, y1, z1)只是平面中的一个参考点。我只是选择了三角形顶点 v[0] 作为参考点并重新排列:

Ax + By + Cz = Ax1 + By1 + Cz1 因此,D = Ax1 + By1 + Cz1

所以,最后,为了得到用于 z 计算的 A、B、C 和 D,我对每个三角形都这样做了,其中trianglelist[nt]是场景的整个三角形数组中当前索引 nt 处的三角形:

float pA = planeNormal.x;
float pB = planeNormal.y;
float pC = planeNormal.z;
float pD = (pA*trianglelist[nt].v[0].x)+(pB*trianglelist[nt].v[0].y)+(pC*trianglelist[nt].v[0].z);

从这里,在我描述的扫描转换算法中,我计算了 zs:

zInit = -1*((pA*cx)+(pB*scanLine)+(pD))/(pC);  //cx is current x value; scanLine is current y value
...
...
float zNext = zPrev - (pA/pC);

唉,在所有这些仔细的工作之后,有些事情发生了!在某些三角形中,深度值是真实的(符号除外)。对于由顶点 (200, 10, 75)、(75, 200, 75) 和 (15, 60, 75) 给出的三角形,所有深度都为 -75。对于所有顶点都在相同深度的其他三角形,情况也是如此。但是对于顶点 (390, 300, 105), (170, 360, 80), (190, 240, 25),所有的 z 值都超过 300!第一个是 310.5,其余的只是变大,最大值在 365 左右。当最深的顶点位于 z = 105 时,不应该发生这种情况!!!那么,在所有的漫无边际,任何人都可以看到可能导致这种情况的原因吗?如果这是与符号相关的事情,我不会感到惊讶,但是在哪里(毕竟,绝对值在恒定深度情况下是正确的)?

4

2 回答 2

1

正确的方程式是:

n = cross (v[2] - v[0], v[1] - v[0]);
D = - dot (n, v[0]);

注意减号。

于 2012-11-16T21:01:57.530 回答
1

you should have a look at www.scratchapixel.com, particularly this lesson:

http://scratchapixel.com/lessons/3d-advanced-lessons/perspective-and-orthographic-projection-matrix/

It contains a self-contained program that shows you how to project vertices.

于 2012-11-16T21:25:47.237 回答