TinyRenderer之画线算法

画线算法是指在屏幕上根据两个点画出一条直线,我们会一步一步通过一些简单的算法深入到bresenham画线算法当中去;

首先提出问题,给定两个点,我们如何在光栅格图像上画一条直线?最先想到的方式就是根据给出的两个点,在中间做线性插值,将步进值设置为0.01,代码很简单:

voiddrawLineOptionStep(int x0, int y0, int x1, int y1, TGAImage& image,TGAColor& color) {
for(float t = 0.0f; t < 1.0f; t += 0.01f) {
    intx = x0 + (x1 - x0) * t;
    inty = y0 + (y1 - y0) * t;
    image.set(x,y, color);
    }
}

这样能够得到一条直线

但是这样会有个问题:线段长一点就需要用更小的步进值进行计算,要不就会画成一行点

很容易想到,我们为什么不每一行都进行计算呢?然后我们编写下面这段代码:

voiddrawLinePixelStep(int x0, int y0, int x1, int y1, TGAImage& image,TGAColor& color) {
for(int x = x0; x <= x1; x++) {
    floatt = (x - x0) / (float)(x1 - x0);
    inty = y0 + (y1 - y0) * t;
    image.set(x,y, color);
    }
}
int main(int argc,char** argv) {
 
    TGAImageimg(width, height, TGAImage::RGB);
     
    drawLinePixelStep(13,20, 80, 40, img, WHITE);
    drawLinePixelStep(20,13, 40, 80, img, RED);
    drawLinePixelStep(80,40, 13, 20, img, RED);
    //翻转图像,让我们定义的原点在左下角
    img.flip_vertically();
    img.write_tga_file("output.tga");
    return0;
}

输出是这样的

可以看到,白色线正常,红色线又变成了虚线,很显然红色线每次对x进行步进一个像素,但是y方向上的对应跨度就有点大了,导致采样不足,形成了虚线。

我们还可以发现还有一条红线不见了,以为这个函数需要x方向上x0<x1,不然根本就不会进入循环。

为了解决这两个问题,我们加入一些条件判断,挑选坡度较小的维度进行像素步进,保证线段的连续,但是并且判断该维度上的大小,如果不满足就调转两个点在进行绘制。

编写代码如下:

void line(int x0,int y0, int x1, int y1, TGAImage& image, TGAColor& color) {
    boolsteep = false;
    //保证
    if(abs(x1 - x0) < abs(y1 - y0)) {
        steep= true;
        std::swap(x0,y0);
        std::swap(x1,y1);
    }
    if(x0 > x1) {
        std::swap(x0,x1);
        std::swap(y0,y1);
    }
    floatdistant = (float)(x1 - x0);
    for(int x = x0; x <= x1; x++) {
    floatt = (x - x0) / distant;
    inty = y0 + t * (y1 - y0);
    //如果改变了参考轴,那么还要翻转回来
    if (steep)
        image.set(x, y, color);
    else
        Image.set(y, x, color);
    }
}

这样就能保证绘制直线的连续了

但是这个计算中有太多浮点运算了,要知道这是个非常底层的操作,可能会被大量调用,所以需要考虑效率。

能不能拿不用浮点运算,并且少点条件判断?

带着这个思想我们就可以进入bresenham画线算法了

这里推荐Bresenham 直线算法 - 知乎 (zhihu.com) 烧烤摊摊主老蔡的文章,里面有图片很好

最终代码如下:

voidbresenhamLine(Vec2i t0, Vec2i t1, TGAImage& image, TGAColor& color) {
    boolsteep = false;
    intx0 = t0.x, x1 = t1.x;
    inty0 = t0.y, y1 = t1.y;
    if(abs(x1 - x0) < abs(y1 - y0)) {
        steep= true;
        std::swap(x0,y0);
        std::swap(x1,y1);
    }
    if(x0 > x1) {
        std::swap(x0,x1);
        std::swap(y0,y1);
    }
    intdx = x1 - x0;
    intdy = y1 - y0;
    intderror = 2 * abs(dy);
    interror = 0;
    inty = y0;
    for(int x = x0; x <= x1; x++) {
        if(steep) {
            image.set(y,x, color);
        }
        else{
            image.set(x,y, color);
        }
        error+= derror;
        if(error > dx) {
            y+= (y1 > y0 ? 1 : -1);
            error-= 2 * dx;
        }
    }
}

虽然不是最终的版本,但是这样会比较好理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值