通过最小闭环工程发现第2个绘制结果[192502192023] 中的代码忽略了一个问题:直线垂直时,直线斜率就不存在,算法就无法绘制出正确的三角形。

为了解决这一问题,给出如下的优化:

// 声明直线方程的两个变量k,b。比如k_p0p2是指直线p0p2的斜率
float k_p0p2 = 0, b_p0p2 = 0, k_p0p1 = 0, b_p0p1 = 0, k_p1p2 = 0, b_p1p2 = 0;
int i;// 声明循环控制变量i
#define UNEXIST -99999
vector<vector<float>> memo1, memo2;// 将vector作为全局变量使用
int Y;// 声明直线Y

// 进阶版算法
// FIX:解决了直线斜率不存在(垂直)无法绘制的问题
// 优化点:排除三点一线的情况
// 优化点:删除全局变量j,保留i
// 优化点:将memo改成全局变量,将直线Y改成全局变量,减少gc
void scanline_185002232023(Vec2i p0, Vec2i p1, Vec2i p2, TGAImage& image, TGAColor color)
{
    if(p0.y == p1.y == p2.y || p0.x == p1.x == p2.x)    return;

    // 更新全局变量的值
    memo1 = memo2 = {};
    k_p0p1 = k_p0p2 = k_p1p2 = 0;
    b_p0p1 = b_p0p2 = b_p1p2 = 0;

    if (p1.y > p0.y) swap(p1, p0);
    if (p2.y > p0.y) swap(p2, p0);
    if (p2.y > p1.y) swap(p2, p1);

    // 若某线段与X轴垂直,标记为不存在
    if (p0.x == p1.x)   k_p0p1 = b_p0p1 = UNEXIST;
    if (p0.x == p2.x)   k_p0p2 = b_p0p2 = UNEXIST;
    if (p1.x == p2.x)   k_p1p2 = b_p1p2 = UNEXIST;

    if (k_p0p1 != UNEXIST)
    {
        k_p0p1 = 1.0 * (p0.y - p1.y) / (p0.x - p1.x);
        b_p0p1 = p0.y - k_p0p1 * p0.x;
    }
    if (k_p0p2 != UNEXIST)
    {
        k_p0p2 = 1.0 * (p0.y - p2.y) / (p0.x - p2.x);
        b_p0p2 = p2.y - k_p0p2 * p2.x;
    }
    if (k_p1p2 != UNEXIST)
    {
        k_p1p2 = 1.0 * (p1.y - p2.y) / (p1.x - p2.x);
        b_p1p2 = p1.y - k_p1p2 * p1.x;
    }

    // 当p0p1为垂直线,只需要计算y = Y与直线p0p2的交点的横坐标 Y - b_p0p2) / k_p0p2
    if (k_p0p1 == UNEXIST && k_p0p2 != UNEXIST) 
    {
        for (Y = p0.y; Y >= p1.y; --Y) {
            memo1.push_back({ 1.0f * p0.x , (Y - b_p0p2) / k_p0p2 });
        }
    }
    else if (k_p0p1 != UNEXIST && k_p0p2 == UNEXIST) 
    {
        for (Y = p0.y; Y >= p1.y; --Y) {
            memo1.push_back({ (Y - b_p0p1) / k_p0p1 , 1.0f * p0.x });
        }
    }
    else
    {
        for (Y = p0.y; Y >= p1.y; --Y) {
            memo1.push_back({ (Y - b_p0p1) / k_p0p1 , (Y - b_p0p2) / k_p0p2 });
        }
    }
    // 绘制上半个三角形
    i = 0;
    for (Y = p0.y; Y >= p1.y; --Y) {
        line_rough(memo1[i][0], Y, memo1[i][1], Y, image, color);
        i++;
    }

    if (k_p1p2 == UNEXIST && k_p0p2 != UNEXIST)
    {
        for (Y = p1.y; Y >= p2.y; --Y) {
            memo2.push_back({ p2.x * 1.0f , (Y - b_p0p2) / k_p0p2 });
        }
    }
    else if (k_p1p2 != UNEXIST && k_p0p2 == UNEXIST)
    {
        for (Y = p1.y; Y >= p2.y; --Y) {
            memo2.push_back({ (Y - b_p1p2) / k_p1p2 , p2.x * 1.0f });
        }
    }
    else
    {
        for (Y = p1.y; Y >= p2.y; --Y) {
            memo2.push_back({ (Y - b_p1p2) / k_p1p2 , (Y - b_p0p2) / k_p0p2 });
        }
    }
    
    // 绘制下半段
    i = 0;
    for (Y = p1.y; Y >= p2.y; --Y) {
        line_rough(memo2[i][0], Y, memo2[i][1], Y, image, color);
        i++;
    }
}

这是一个用于绘制三角形的扫描线算法函数**scanline_185002232023**,具体实现步骤如下:

  1. 判断三个点是否在同一条水平或竖直线上,如果是则直接返回。
  2. 对三个点按照y坐标大小排序,将y坐标最大的点作为p0,y坐标最小的点作为p2。
  3. 计算三条线段的斜率k和截距b,并将k和b保存到全局变量中。如果某条线段垂直于x轴,则将其斜率和截距标记为不存在。
  4. 分别处理上下两部分,对于上半部分,从p0开始向p1扫描每一行,计算直线p0p1与当前行的交点,并将交点存储到一个数组memo1中。对于下半部分,同理,从p1开始向p2扫描每一行,计算直线p1p2与当前行的交点,并将交点存储到另一个数组memo2中。
  5. 对于上下两部分,分别遍历memo1和memo2数组,将每个交点作为线段的起点和终点,调用函数**line_rough**来绘制扫描线。

绘制结果:

这次的绘制是正确的。

Untitled

african_head_saomiaoxian_tichu.tga