计算机图形学 - 直线的扫描转换算法

Posted Tisfy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算机图形学 - 直线的扫描转换算法相关的知识,希望对你有一定的参考价值。


计算机图形学

直线的扫描转换

数值微分法 DDA line algorithm

对于斜率的绝对值小于1的直线,从左端点P0(x0,y0)开始,x每增加1,y就增加k

void DDA(int x0, int y0, int x1, int y1) 
    float k = 1. * (y1 - y0) / (x1 - x0);
    for (; x0 <= x1; x0++) 
        drawPixel(x0, y0);
        y0 += k;
    

中点画线法 Midpoint line algorithm

直线上点(x, y)满足F(x, y) = ax + by + c = 0(其中a = y0 - y1,b = x1 - x0,c = x0y1 - x1y0),直线下方的点算得F(x, y) < 0,直线上方的点算得F(x, y) > 0。

斜率在0~1的曲线,x每增加1,y会增加1或不变。

在确定好点(xp, yp)的位置后,要确定这个点右边的点是P1(xp + 1, yp)还是P2(xp + 1, yp + 1),可以取P1和P2的中点M(xp + 1, yp + 0.5),看F(M)的正负。
  • 如果F(M) < 0,说明点M在直线的下方,因此直线距点P2更近。
  • 如果F(M) > 0,说明直线距离点P1更进近。
  • 如果F(M) = 0,则M正好在直线上,人为规定此时选取P1(和>0时相同)
void Midpoint(int x0, int y0, int x1, int y1) 
    int a = y0 - y1, b = x1 - x0, c = x0 * y1 - x1 * y0;
    for (; x0 <= x1; x0++) 
        float f = a * x0 + b * (y0 + 0.5) + c;
        if (f < 0) y0++;
        drawPixel(x0, y0);
    

接下来我们优化f的计算:其实新的f可以通过上次的f求得。

设当前我们画了点L1,接下来该画点L2(L2是L1右边相邻的点)。记(xl1, yl1 + 0.5)为M1,记(xl2, yl2 + 0.5)为M2,记F(M1)为f1,记F(M2)为f2。
则f1 = a * xm1 + b * ym1 + c, f2 = a * xm2 + b * ym2 + c。
∴f2 - f1 = a * (xm2 - xm1) + b * (ym2 - ym1)
∴f2 = f1 + a * (xm2 - xm1) + b * (ym2 - ym1)

因为m2是m1右边的下一个点,所以xm2 - xm1 = 1
而ym2 - ym1 = 1 或 0
当m2是m1右上方的点时,ym2 - ym1 = 1,此时f1 < 0,f2 = f1 + a
当m2是m1正右边的点时,ym2 - ym1 = 0,此时f1 ≥ 0,f2 = f1 + a + b

通过以上方法我们即可用前一个点的f值求得后一个点的f值,初始值f0 = F(x0, y + 0.5) = a + 0.5b

void midPoint(int x0, int y0, int x1, int y1) 
    int a = y0 - y1, b = x1 - x0;
    float f = a + 0.5 * b; // 斜率在0~1,∴f > 0,符合初始条件中点在直线上方
    for (; x0 <= x1; x0++) 
        if (f < 0) y0++, f += a;
        else f += a + b;
        drawPixel(x0, y0);
    

每次f的增量都是整数,只有初始值的时候f = a + 0.5 * b 可能为小数。
因此我们可以令F(x, y) = 2 * a * x + 2 * b * y + 2 * c,这样就避免了小数问题。

void midPoint(int x0, int y0, int x1, int y1) 
    int a = y0 - y1, b = x1 - x0;
    int f = 2 * a + b; // 此处就可以改为int了
    for (; x0 <= x1; x0++) 
        if (f < 0) y0++, f += 2 * a;
        else f += 2 * (a + b);
        drawPixel(x0, y0);
    

Bresenham算法 Bresenham’s line algorithm

记直线方程为y=kx+b。

误差项d初始值为0,x每增加1,d就增加k
当d ≥ 1 时,就让 d 减一,保证d在0~1之间

  • 如果 d ≥ 0.5,那么应该取下方的点
  • 如果 d < 0.5,那么应该取上方的点

小改进:
令 e = d - 0.5,则e的范围是[-0.5, 0.5),初始值-0.5,e ≥ 0.5时减一,判断e的正负来判断取上方的点还是下方的点。

void bresenham(int x0, int y0, int x1, int y1) 
    float k = (y1 - y0) / (x1 - x0);
    float e = -0.5;
    for (; x0 <= x1; x0++) 
        drawPixel(x0, y0);
        e += k;
        if (e >= 0) e -= 1, y0++;
    

改进:
上述运算中同样用到了小数。但不难发现e的判断只与符号有关。
因此我们可以让 e’ = 2 * dx * e,则初始值e’ = -0.5 * 2 * dx = -dx,e’每次加上2 * dx * k = 2 * dy,e’ ≥ 0 时应减去1 * 2 * dx = 2 * dx。

void bresenham(int x0, int y0, int x1, int y1) 
    int dx = x1 - x0;
    int e = -dx;
    for (; x0 <= x1; x0++) 
        drawPixel(x0, y0);
        e += 2 * dy;
        if (e >= 0) e -= 2 * dx, y0++;
    

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/122025474

以上是关于计算机图形学 - 直线的扫描转换算法的主要内容,如果未能解决你的问题,请参考以下文章

计算机图形学 - 直线的扫描转换算法

计算机图形学之扫描转换直线-DDA,Bresenham,中点画线算法

计算机图形学——直线扫描转换

计算机图形学(三种画线算法)

光栅图形学:直线段的扫描转换算法

计算机图形学