Реализация алгоритма сканирования

Мне было поручено реализовать версию алгоритма сканирования линии для назначения. Эта программа работает путем чтения списка вершин и цветов из текстового файла. Три вершины и три цвета выталкиваются из очереди за раз, затем рисуются края треугольника. До сих пор моя программа делает это в значительной степени отлично. Перед этим нам было поручено задание, которое рисовало прямоугольники, и сразу после запуска тестов стало ясно, что программе необходимо перевернуть координаты y, чтобы изображение было правильно нарисовано.

В этом и заключается проблема. Я нашел несколько алгоритмов для функции заполнения треугольника сканирования Вот, но я заметил, что мне придется изменить их, чтобы учесть тот факт, что изображение перевернуто. В сетке компьютерной графики более высокие значения y направлены вниз. При организации трех вершин данного треугольника обычно нужно назначить вершину с наименьшим значением y в качестве вершины, а вершину с наивысшим значением y в качестве дна. Я перевернул это, чтобы учесть переворот изображения.

Независимо от того, что я делаю, я не могу заставить мое изображение заполняться правильно. Он будет работать с этими функциями как есть, пока я не переверну значения y, когда отдельные пиксели прорисованы, а вершины отсортированы ожидаемым образом, где более низкие значения y направлены вверх. Однако результирующее изображение переворачивается вертикально.

Следующие функции в значительной степени составляют всю программу, кроме рисования линий, которое, я думаю, не нуждается в исправлении.

void FrameBuffer::drawTriangle(const Vertex& v0, const Vertex& v1, const Vertex& v2, const Color& c0, const Color& c1, const Color& c2, Functional status) {
//Start by organizing vertices from top to botttom (need to rearrange colors as well)
Vertex vTop, vMid, vLow;
Color cTop, cMid, cLow;

vTop = v0;  cTop = c0;
if (v1[Y] > vTop[Y]) {
vMid = vTop;    cMid = cTop;
vTop = v1;      cTop = c1;
}
else {
vMid = v1;      cMid = c1;
}

if (v2[Y] > vTop[Y]) {
vLow = vMid;    cLow = cMid;
vMid = vTop;    cMid = cTop;
vTop = v2;      cTop = c2;
}
else if (v2[Y] > vMid[Y]) {
vLow = vMid;    cLow = cMid;
vMid = v2;      cMid = c2;
}
else {
vLow = v2;      cLow = c2;
}

//Draw the edges of the triangle
drawLine(vTop, vMid, cTop, cMid, status);
drawLine(vTop, vLow, cTop, cLow, status);
drawLine(vMid, vLow, cMid, cLow, status);

//Fill the triangle; first case is flat bottom
if (vMid[Y] == vLow[Y]) {
fillFlatBottom(vTop, vMid, vLow, status);
}
//Second case is flat top
else if (vTop[Y] == vMid[Y]) {
fillFlatTop(vTop, vMid, vLow, status);
}
//General case; triangle can be split into flat bottom above a flat top
else {
Vertex vNew;        //This represents the point on the triangle across from the "midpoint"vNew[Y] = vMid[Y];
vNew[X] = vTop[X] + ((vMid[Y] - vTop[Y]) / (vLow[Y] - vTop[Y])) * (vLow[X] - vTop[X]);
vNew[Z] = (vLow[Z] * (vNew[X] - vTop[X]) * (vNew[Y] - vMid[Y]) + vTop[Z] * (vNew[X] - vMid[X]) * (vNew[Y] - vLow[Y]) + vMid[Z] * (vNew[X] - vLow[X]) * (vNew[Y] - vTop[Y]) - vMid[Z] * (vNew[X] - vTop[X]) * (vNew[Y] - vLow[Y]) - vLow[Z] * (vNew[X] - vMid[X]) * (vNew[Y] - vTop[Y]) - vTop[Z] * (vNew[X] - vLow[X]) * (vNew[Y] - vMid[Y])) / ((vNew[X] - vTop[X]) * (vNew[Y] - vMid[Y]) + (vNew[X] - vMid[X]) * (vNew[Y] - vLow[Y]) + (vNew[X] - vLow[X]) * (vNew[Y] - vTop[Y]) - (vNew[X] - vTop[X]) * (vNew[Y] - vLow[Y]) - (vNew[X] - vMid[X]) * (vNew[Y] - vTop[Y]) - (vNew[X] - vLow[X]) * (vNew[Y] - vMid[Y]));

fillFlatBottom(vTop, vMid, vNew, status);
fillFlatTop(vMid, vNew, vLow, status);
}
}

//Draw from bottom up (something is happening here that causes errors)
void FrameBuffer::fillFlatTop(const Vertex& v0, const Vertex& v1, const Vertex& v2, Functional status) {
double xSlope1 = -(v2[X] - v0[X]) / (v2[Y] - v0[Y]);
double xSlope2 = -(v2[X] - v1[X]) / (v2[Y] - v1[Y]);

Vertex curV0 = v2;
Vertex curV1 = v2;

//v0 is the highest point with the highest y value and v2 is the lowest (for this case) with the lowest y value
while (curV0[Y] < v0[Y] && curV1[Y] < v0[Y]) {
Color curC0 = image.get(curV0[X], curV0[Y]);
Color curC1 = image.get(curV1[X], curV1[Y]);

drawLine(curV0, curV1, curC0, curC1, status);

curV0[Y] += 1;  curV0[X] -= xSlope1;
curV1[Y] += 1;  curV1[X] -= xSlope2;
}
}

//Draw from top down (something is happening here that causes errors)
void FrameBuffer::fillFlatBottom(const Vertex& v0, const Vertex& v1, const Vertex& v2, Functional status) {
double xSlope1 = -(v1[X] - v0[X]) / (v1[Y] - v0[Y]);
double xSlope2 = -(v2[X] - v0[X]) / (v2[Y] - v0[Y]);

Vertex curV0 = v0;
Vertex curV1 = v0;

//v0 is the highest point with the highest y value and v1 (for this case) is the lowest with the lowest y value
while (curV0[Y] >= v1[Y] && curV1[Y] >= v1[Y]) {
Color curC0 = image.get(curV0[X], curV0[Y]);
Color curC1 = image.get(curV1[X], curV1[Y]);

drawLine(curV0, curV1, curC0, curC1, status);

curV0[Y] -= 1;  curV0[X] += xSlope1;
curV1[Y] -= 1;  curV1[X] += xSlope2;
}
}

Чтобы обеспечить некоторую перспективу, это результирующее изображение, когда ничего не заполнено: http://imgur.com/a/VOpWJ

Это происходит, когда выполняется только fillFlatBottom: http://imgur.com/a/nexR9

Это происходит, когда выполняется только fillFlatTop: http://imgur.com/a/flRCK

Что именно я делаю не так? Очевидно, что линейный алгоритм не вызывает этой проблемы. Либо я неправильно вычисляю точку напротив средней точки (это vNew), либо я как-то испортил алгоритмы заполнения.

2

Решение

Давайте просто посмотрим на одну из функций: fillFlatTop теперь. Идея в том, что для двух точек CurV0 а также curV1 двигаться по двум сторонам треугольника от самой нижней вершины v2 в верхние вершины v0 а также v1соответственно.

Давайте проверим это:

  • За curV0как y идет от того из v2 в v0, y изменения по v0.y - v2.y, Но вы хотите x изменить на равный v0.x - v2.xтак что для каждого 1 изменить в yхочешь иметь (v0.x - v2.x) / (v0.y - v2.y)

  • За curV1так же, как указано выше, но заменяет 0с 1s.

Если вы посмотрите на свой код, наклоны верны (есть двойной отрицательный результат, но он работает правильно).

Теперь давайте посмотрим на fillFlatBottom, То же, что и выше:

  • За curV0 двигаясь от v0 в v1изменить в y является v1.y - v0.y соответствует v1.x - v0.x изменить в xтак что для каждого 1 изменить в y ты хочешь x изменить на (v1.x - v0.x) / (v1.y - v0.y),

  • За curV1так же, как и выше, с 2с вместо 1s

Сравнивая это с вашим кодом, кажется, что у вашего склона неправильный знак, или если вы хотите использовать то же соглашение, что и fillFlatTop Вы должны оставить склоны такими, какие они есть, но изменить приращения (x += ...) в сторону уменьшения (x -= ...) опять же иметь двойной негатив 🙂

0

Другие решения

Вот код, который я использую для рисования заполненного треугольника. Он основан на алгоритме, описанном на этой странице: Треугольные Наполнители
. Код является частью класса, основанного на шаблонах, и использует библиотеку std и некоторый контейнер изображений, но вы можете легко изменить код в соответствии со своими потребностями:

struct spFPoint
{
float x;
float y;
};

//-----------------------
// draw triangle filled
//-----------------------
template <class T>
static void TriangleFilled(spImage<T> *img, T val, int x1, int y1, int x2, int y2, int x3, int y3)
{
// this piece of code is used only for sorting
spFPoint pt;
pt.x = x1;
pt.y = y1;
vector <spFPoint> triangle;
triangle.push_back(pt);
pt.x = x2;
pt.y = y2;
triangle.push_back(pt);
pt.x = x3;
pt.y = y3;
triangle.push_back(pt);
stable_sort(triangle.begin(), triangle.end(), yAscFPoint);
// here comes the actual algorithm
spFPoint A, B, C;
float   dx1, dx2, sx1, sx2, y;
A = triangle[0];
B = triangle[1];
C = triangle[2];
B.y-A.y > 0 ? dx1 = (B.x-A.x)/(B.y-A.y) : dx1=0;
C.y-A.y > 0 ? dx2 = (C.x-A.x)/(C.y-A.y) : dx2=0;
sx1 = sx2 = A.x;
y = A.y;
// upper half
for (; y <= B.y; y++, sx1+=dx1, sx2+=dx2)
ScanLine(img, val, y, sx1, sx2);  // or your function for drawing horizontal line
// lower half
C.y-B.y > 0 ? dx1 = (C.x-B.x)/(C.y-B.y) : dx1=0;
sx1 = B.x;
y   = B.y;
for (; y <= C.y; y++, sx1+=dx1, sx2+=dx2)
ScanLine(img, val, y, sx1, sx2); // or your function for drawing horizontal line
}
//----------------------
// draw scanline (single color)
//----------------------
template <class T>
static void ScanLine(spImage<T> *img, T val, int y, int x1, int x2)
{
if (y < 0 || y > img->Height()-1) return;
if ((x1 < 0 && x2 < 0) || (x1 > img->Width()-1 && x2 > img->Width()-1 )) return;
if (x1 > x2)   // swap coordinates
{
x1 = x1^x2;
x2 = x1^x2;
x1 = x1^x2;
}
if (x1 < 0) x1 = 0;
if (x2 > img->Width()-1) x2 = img->Width()-1;
for (int j = x1; j <= x2; j++)
img->Pixel(y, j) = val;     // draw point
}
//----------------------
// sorting function
//----------------------
inline static bool yAscFPoint(spFPoint lhs, spFPoint rhs) { return lhs.y < rhs.y;}

Если ваше изображение или холст для рисования перевернуты, перед вызовом функции TriangleFilled инвертируйте координаты y. Это проще сделать до вызова функции, затем внутри функции:

   y1 = img->Height()-1 - y1;
y2 = img->Height()-1 - y2;
y3 = img->Height()-1 - y3;

Но, от характера вашего задания

Эта программа работает, читая в списке вершин и цветов из
текстовый файл.

кажется, что вам нужно выполнить заливку / заливку Гуро, и это немного более длинный кусок кода.

0

По вопросам рекламы [email protected]