我的Z缓冲区计算有什么问题?

时间:2012-11-16 20:41:14

标签: c++ depth-buffer zbuffer

我正在实现一个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值,I插值为zk + 1 = zk - A / C,其中A和C来自平面方程。

为了得到这些z计算的A,B和C,我需要由当前三角形的3个顶点(数组vertex v[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;   
 }

为了得到平面方程/ my 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时,这不应该发生!所以,在所有漫无目的之后,任何人都可以看到可能导致这种情况的原因吗?如果它是与标志相关的东西,我会不会感到惊讶,但在哪里(毕竟,绝对值在恒定深度的情况下是正确的)?

2 个答案:

答案 0 :(得分:1)

正确的公式是:

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

请注意减号。

答案 1 :(得分:1)

你应该看看www.scratchapixel.com,特别是这一课:

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

它包含一个自包含程序,向您展示如何投影顶点。