Я реализую 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 равны подключен как текущее значение х через строку сканирования и само значение текущей строки сканирования соответственно.
Для последующих значений z по линии сканирования я интерполирую как zk + 1 = zk — A / C, где A и C берутся из уравнения плоскости.
Чтобы получить A, B и C для этих вычислений z, мне нужен вектор нормали плоскости, определяемой 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;
}
Чтобы получить значение D для уравнения плоскости / моих расчетов z, я использую уравнение плоскости 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
Итак, наконец, чтобы получить A, B, C и D для расчетов z, я сделал это для каждого треугольника, где 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 !!! Итак, после всего этого бессвязного, кто-нибудь может увидеть, что могло вызвать это? Я не удивлюсь, если это связано со знаком, но где (в конце концов, абсолютные значения правильны в случаях постоянной глубины)?
Правильные уравнения:
n = cross (v[2] - v[0], v[1] - v[0]);
D = - dot (n, v[0]);
Обратите внимание на знак минус.
Вы должны взглянуть на www.scratchapixel.com, особенно этот урок:
http://scratchapixel.com/lessons/3d-advanced-lessons/perspective-and-orthographic-projection-matrix/
Он содержит автономную программу, которая показывает, как проецировать вершины.