直线扫描转换算法

DDA画线算法

一.算法介绍

DDA是一种增量算法,也就是说通过对前一个点在X和Y轴方向上加上一个增量,从而得到一个新点得坐标。这个算法要求先算出直线的斜率,然后从起点开始,确定最佳逼近于直线

的y坐标。假设起点的坐标为整数,直线方程为y=kx+b,k的取值在0到1之间,x每递增1,y相应地递增k。因为像素的坐标是整数,所以y需要进行取整处理。对新坐标行四舍五入得到

整型y值,确定一个要渲染得像素点。从而画出一条直线。

二.算法解析:

已知过端点 P0(x0, y0),P1(x1, y1) 的直线段 L(P0, P1);直线斜率为 k = (y1 - y0) / (x1 - x0)。于是 yi+1 = kxi+1 + b。

1.x每递增1,y相应地递增k,每次计算点的坐标,我只需要x+1,y+k就行了,避免了乘除,而只有加减。

           ↓

2.通过增量得到下一个点后,可知此坐标肯定会与实际像素点有偏差(实际的直线并不是和像素点完全重合的)。所以采取四舍五入得方式来平衡差值,但是

C/C++中取整是直接舍去小数值,所以采取加上0.5再取整,即int(y+0.5)。从而达到一个四舍五入的目的。

 

三、代码

// 数值微分法,伪代码。通过此代码可理解算法的基本思路

void DDAline(int x0, int y0, int x1,int y1)

{

  int dx,dy,x=x0,y=y0;

  double k;

  dx=x1-x0,dy=y1-y0;

  k=dy / dx;

  draw( x , y );

  while( x <= x1 )

  {

    x+=1;

    y+=k;

    draw( x, int(y+0.5) );

  }

}

中点画线算法

      图1                    图2

引用这2个图是因为实际像素为图1,图2中为了方便算法的描绘,采用像素中心来代替整个像素点。所以不要单纯由图2产生错误理解,要联系实际的物理分布。

一、算法介绍

这里的中点指的是直线与虚拟网格交点Q的上下像素点的连线中点M。将M与Q点进行比较,若Q与M重合或者在M之下,则渲染P1点。else,渲染上方

的P2点。从而渲染出一条直线。

二、算法解析

1.设坐标的单位为1,斜率k<1,起点和终点为 (x0, y0)和(x1, y1),则F(x, y) = ax + by + c, 其中a = y0 - y1, b = x1 - x0,c = x0y1 - x1y0(a,b,c是如何得到的?这是基础的数学知识,

令F(x, y)=0则得出直线方程,代入 (x0, y0)和(x1, y1)可求出a,b,c

2.对于直线上的点,F(x, y) = 0;直线上方的点,F(x, y) > 0;直线下方的点,F(x, y) < 0.  因此,判断M在Q的上方还是下方,只需将M点带入方程计算判别式:

d = F(M) = F(xp + 1, yp + 0.5) = a(xp + 1) + b(yp + 0.5) + c( 下一个点x轴加1,y轴取中点所以加0.5
             ↓
当d >= 0时,取正右方像素p1也就是取直线下方的点。( 此处易产生误解,要记住代入的是中点,F(M)>=0说明中点在直线之上或在直线上所以取靠近直线的点,也就是下方的点
在这种情况下欲判断再下一个像素应取哪个,应计算:d1 = F(xp +1 + 1, yp + 0.5) = a(xp + 1 + 1) + b(yp + 0.5) + c  = d + a,故增量为a。
解释: 要渲染的点是直线下方的点,也就是(xp + 1, yp),所以再下一个中点就是(xp + 1 +1, yp + 0.5)。因为从直线在中点之下就可推算出k<=0.5,所以x轴增加2,y轴增加还是<=1,
从而上下像素点的中点y值还是0.5,所以yp+0.5为中点值。
             ↓
当d<0时,此时则取右上方的像素p2,这种情况下要判断再下一个像素,则应该计算:
d2 = F(xp +1 + 1, yp + 1 + 0.5) = a(xp + 1 + 1) + b(yp + 1 + 0.5) + c  = d + a + b,d的增量为 a + b。
解释: 下一个点的坐标是(xp + 1, yp + 1),所以再下一个中点就是(xp + 1 +1, yp + 1 + 0.5),原因同理,可通过画图来看,更加直观。
            ↓
d的初始值,第一个像素应取左端点(x0, y0),相应的判别式为:d0 =F(x0 + 1, y0 + 0.5) = a(x0 + 1) + b(yp + 0.5) + c=  ax0 + by0 + c + a + 0.5b= F(x0, y0) + a + 0.5b
由于(x0, y0)在直线上,所以F(x0, y0) = 0,因此,d的初始值为a + 0.5b。d的初始值包含小数,因此可以用2d来代替d。

解释:为何要用2d代替d?这是为了来摆脱浮点运算,只进行整数的加法,相比于DDA算法进一步提高了效率。

            

三、代码

以下代码为只包含整数运算的方法:

void MidPointLine(int x0, int y0, int x1, int y1, int color)

{

     int a, b, delta1, delta2, d, x, y;

     a = y0 - y1;

     b = x1 - x0;

     d = 2 * a + b;

     delta1 = 2 * a;

     delta2 = 2 * (a + b);

     x = x0;

     y = y0;

     DrawPixel(x, y, color);

     while (x < x1)

     {

          if (d < 0)

          {

               ++x;

               ++y;

               d += delta2;

          }

         else

          {

               ++x;

               d += delta1;

          }

          DrawPixel(x, y, color);

     }

}
 
Bresenham画线算法
一、算法介绍
该算法是通过各行、各列像素中心构造一组虚拟网格线,按照直线起点到终点的顺序,计算直线与各垂直网格线的交点,然后根据误差项的符号确定该列像素点中与此交点最近的元素。

二、算法解析

y

1.如图,假设每次x + 1,而y要么不变,要么递增1,是否递增1取决于误差项d的值(如上图所示)。因为直线起始点在像素中心,所以误差项d的初始值为0,x下标每增加1,d的值相应递增

直线的斜率值,即d = d + k.  一旦d >= 1,将它的值减去1,使得d的值总保持在0到1之间。

当d >= 0.5时,直线与x + 1列垂直网格线交点最接近于当前像素的右上方像素点(x + 1, y + 1),d < 0.5时则为正右方像素(x + 1, y)。

2.为了将算法也改进为整数加减,所以要进行变动。

改进1:令 e = d - 0.5,当e >= 0时 ,下一个像素值y递增1,e < 0时y不递增。此时e初始值为-0.5。每走一步e=e+k,当渲染后需再次对e进行判定,以保证其相对区间。

当e>0时,e=e-1。(e>0时,此时渲染的为直线上方的点,这时可看作上方的点变为起始点,为了通过0来判别,所以对e进行减1操作。从而顺利完成后续的判别工作

3.到此还没有结束,可以发现的是,我们只用到了e的符号来进行判别。所以可以进一步总结。

改进2:用 2*Δx*e 来代替e,此处的Δx=0.5。所以,e的初始值为-Δx,每走一步e=e+2Δy,当e>0,e-2Δx。

最后总结一下具体步骤:

1.输入直线的两个端点P0(x0,y0)和P1(x1,y1)

2.计算初始值Δx、Δy、e=-Δx、x=x0,y=y0

3.绘制点(x,y)

4.e更新为e+2Δy,判断e的符号,若e>0,则(x,y)更新为(x+1,y+1),同时将e更新为e-2Δx;else 将(x,y) 更新为(x+1,y)

5.当直线没有画完时,重复3和4,否则结束。

三、代码

//伪代码如下

void Bresenham(int x0, int y0, int x1, int y1, int color)

{

   int x, y, dx, dy;

   float k, e;

   dx = x1 - x0;

   dy = y1 - y0;

   e = -dx

   x = x0;

   y = y0;

   for (int i = 0; i <= dx; ++i)

   {

      DrawPixel(x, y, color);

      x += 1;

      e = e + 2 * dy;

      if (e >= 0)   // 伪代码,实际比较浮点数不这样进行比较

      {

          y += 1;

          e = e - 2 * dx;

      }

   }

}

 

三种算法的对比


一、DDA

采用的是浮点数,不易于硬件实现。

二、中点画线算法

只有整数运算,不含乘除,可用硬件实现。相对于DDA提高了效率

三、Bresenham算法

1.在渲染点的循环中没有计算直线的斜率,不用做除法。

2.不用浮点数,只做整数加法和乘2运算,乘2运算可用硬件位移实现。

3.应用范围不拘束于直线的方程形式

4.Bresenham算法”其实“比较像数值微分法,也是增量算法,但是相对来说更利于硬件实现。

猜你喜欢

转载自www.cnblogs.com/jingrui/p/9644296.html
今日推荐