摘要:本文以DDA算法,中点直线算法和Bresenham直线算法为模型,主要从时空的角度讨论三种算法的优缺点。并以C语言为工具编制了相应的程序以实现该算法。
关键词: 光栅化 DDA算法 中点直线算法 Bresenham直线算法
0 引言
显示器的屏幕由可以发光的像素点组成,从放大的几何位置看,所有这些像素点构成一个矩形的阵列,利用计算机控制各像素点按我们指定的要求发光, 就构成了我们需要的图形。现实中我们要讨论的图形却常常是用几何参数来描述的,哪些像素点是正好在图形上或最靠近图形,通过这些像素点描绘出相应的几何体的图形。在点阵图形系统使用中,每建立或修改一幅图形,一般要用数百次甚至数千次直线等其他基本运算。因此这些算法不仅必须要建立视觉效果比较好的图形,还必须要执行得尽可能地快。
1 算法比较的三个指标
评价一个转换算法的优劣可以通过如下三个方面来进行:
(1) 所显示图形的精度,转换出的点阵图形毕竟只是对原始图形的近似, 有一定的误差, 这个误差的大小可根据实际需要而定。
(2) 算法的时间复杂性, 也就是算法的速度。
(3) 算法的空间复杂性, 即算法运行过程所需要的内存空间的大小。
2 光栅直线算法
光栅显示器可以看作是一个像素的矩阵,在光栅显示器上显示任何一个图形,实际上都是一些具有一种或多种颜色和灰色像素的集合。由于光栅图形只是近似的实际图形,如何使光栅图形最完美的逼近实际图形,便是光栅图形学要研究的内容。
数字设备画直线是通过画直线两个端点间的各个离散的点来完成。每个分散的点的位置通过直线方程来计算。对于光栅显示系统,直线颜色装载在相应象素位置的帧缓存中。屏幕坐标只能为整数,因此,画点的位置只能近似实际直线上点的位置。例如计算的点的位置(10.48,20.51)近似为(10,21)。这样画出的直线会出现锯齿样,在低分辨率的显示系统上尤其明显。
数学上的直线是没有宽度的,是无数个点构成的集合,显然,光栅显示器只能近似的显示直线。当我们对直线进行光栅显示时,需要在显示器有限个像素中,确定最佳逼近该直线的一组像素,并且按扫描线顺序,对这些像素进行写操作,这个过程称为用显示器绘制直线或直线的扫描转换,又称光栅化。
由于在一个图形中,可能包含成千上万条直线,所以要求绘制算法应可能的快。为比较三种光栅直线算法,下面先后介绍三种算法的基本原理与实现方法。
2.1数值微分法(DDA算法)
DDA是数值微分分析式(digital differential analyzer)的缩写,是根据直线增量来产生直线的,DDA算法的本质是用数值方法解微分方程,通过同时对x和y各增加一个小增量,计算下一步的x,y值。
DDA算法具体实现的方法:已知过端点P0(x0, y0), P1(x1, y1)的直线段
L:y=kx+b
直线斜率为
先考虑|k| ≤1的情形。在这种情况下,x每增加1,y最多增加1。
即:当x每递增1,y递增k(即直线斜率);
当 |k| >1时,必须把x,y地位互换
在这种情况下,y每增加1,x最多增加1。
即:当y每递增1,x递增1/k(即直线斜率倒数);
上面是从左端点开始计算的,若以右端点作为起始点
当|k| ≤1时:取Dx=-1, yi+1=yi-k
当|k| ≥1时:取Dy=-1, xi+1=xi-1/k 2.2 中点直线算法假定直线斜率k在0~1之间,当前像素点为(xp,yp),则下一个像素点有两种可选择点P1(xp+1,yp)或P2(xp+1,yp+1)。若P1与P2的中点(xp+1,yp+0.5)称为M,Q为理想直线与x=xp+1垂线的交点。当M在Q的下方时,则取P2应为下一个像素点;当M在Q的上方时,则取P1为下一个像素点。这就是中点画线法的基本原理。图1. 中点画线法每步迭代涉及的像素和中点示意图
下面讨论中点画线法的实现。过点(x0,y0)、(x1, y1)的直线段L的方程式为F(x, y)=ax+by+c=0,其中,a=y0-y1, b=x1-x0, c=x0y1-x1y0,欲判断中点M在Q点的上方还是下方,只要把M代入F(x,y),并判断它的符号即可。为此,我们构造判别式: d=F(M)=F(xp+1, yp+0.5)=a(xp+1)+b(yp+0.5)+c
所以:
当d<0时,M在L(Q点)下方,取P2为下一个像素;
当d>0时,M在L(Q点)上方,取P1为下一个象像素;
当d=0时,选P1或P2均可,约定取P1为下一个象素;
注意到d是xp, yp的线性函数,可采用增量计算,提高运算效率:
若当前像素处于d>=0情况,则取正右方像素P1(xp+1, yp),要判下一个像素位置,应计算 d1=F(xp+2, yp+0.5)=a(xp+2)+b(yp+0.5)=d+a,增量为a。
若d<0时,则取右上方像素P2(xp+1, yp+1)。要判断再下一像素,则要计算d2= F(xp+2, yp+1.5)=a(xp+2)+b(yp+1.5)+c=d+a+b,增量为a+b。画线从(x0, y0)开始,d的初值 d0=F(x0+1, y0+0.5)=F(x0, y0)+a+0.5b,因F(x0, y0)=0,所以d0=a+0.5b。
由于我们使用的只是d的符号,而且d的增量都是整数,只是初始值包含小数。因此,我们可以用2d代替d来摆脱小数,这样就写出仅包含整数运算的算法程序。
2.3 2.3 Bresenham直线算法
算法原理:过各行各列像素中心构造一组虚拟网格线。按直线从起点到终点的顺序计算直线与各垂直网格线的交点,然后确定该列像素中与此交点最近的像素。该算法的巧妙之处在于采用增量计算,使得对于每一列,只要检查一个误差项的符号,就可以确定该列的所求像素。
如图2所示,设直线方程为yi+1=yi+k(xi+1-xi)+k。假设列坐标像素已经确定为xi,其行坐标为yi。那么下一个像素的列坐标为xi+1,而行坐标要么为yi,要么递增1为yi+1。是否增1取决于误差项d的值。误差项d的初值d0=0,x坐标每增加1,d的值相应递增直线的斜率值k,即d=d+k。一旦d≥1,就把它减去1,这样保证d在0、1之间。当d≥0.5时,直线与垂线x=xi+1交点最接近于当前像素(xi,yi)的右上方像素(xi+1,yi+1);而当d<0.5时,更接近于右方像素(xi+1,yi)。为方便计算,令e=d-0.5,e的初值为-0.5,增量为k。当e≥0时,取当前像素(xi,yi)的右上方像素(xi+1,yi+1);而当e<0时,取(xi,yi)右方像素(xi+1,yi)。
图2 Bresenham算法所用误差项d
3 三种光栅直线算法的比较
我们就以从(20,20)到(30,50)分别用DDA算法,中点直线算法和Bresenham直线算法画直线,并运行30000次。