我正在实现一个 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 时,不应该发生这种情况!!!那么,在所有的漫无边际,任何人都可以看到可能导致这种情况的原因吗?如果这是与符号相关的事情,我不会感到惊讶,但是在哪里(毕竟,绝对值在恒定深度情况下是正确的)?