一、直线生成基本思路
图形图像是由屏幕上不同亮度不同颜色的光点(像素)组成。在光栅显示器的荧光屏上生成一个对象,实质上是往帧缓存寄存器的相应单元中填入数据。
所以:对直线进行光栅化的时候,只能在显示器所给定的有限个像素组成的点阵中确定最佳逼近于该直线的一组像素,用这些像素表示该直线。
所以:生成直线的主要工作是:快速找出距离直线最近的网格点,用这些网格点对应的像素表示该直线。
如果我们使用斜截式方程来表示直线,会用到一次乘法和一次 加法,在计算机底层乘法是费时的,那么有什么方法可以改进?
变乘法为加法!
直线是象素集合,生成算法的最终目的,就是为了寻找能更准确逼近直线的象素点。
所以,要确定直线上每个点,那么,如:m<1时,从起始点xs,xs+1, xs+2, xs+3 ……到xe的每个点(xi,yi) ,需要确定其对应的象素值。
所以,每一个(xi,yi)即准确值,都要寻找对应其的象素值(xi,yi,r),即,最接近其准确值的整数值,最简单-取整 。
假设(xi,yi)已经确定了它的对应整数象素点,则下面就要找个规则确定下一个点即(xi+1, yi+1)的对应象素点。
对应于上述情况,即m<1的情况, xi+1=xi+1,即yi+1需要确定,即需要确定上述说的准则,即给定一个判定式,由判定式来确定yi+1的选择。
而判定式为了计算方便也可以有更简便的方式来计算。故判定式也可以由递推式来确定。
二、基本增量算法(DDA)
基本增量算法也叫数字微分算法(Digital Differential Analyzer)
1、基本思想:
舍入法求解最佳逼近;利用微分思想,即每一个点坐标都可以由前一个坐标变化一个增量得到。
2、直线的表示:
设直线的起点坐标为(xs,ys),终点坐标为(xe, ye),
令Δx= xe- xs, Δy= ye-ys,则直线参数方程为
将参数区间[0,1]分为n等分,即t每次增量为 Δt= 1/n
3、提高速度的方法
乘法用加法实现,每一个点坐标都可以由前一个坐标变化一个增量得到。
设(xi,yi)是第i 步得的直线上的点,则直线上第个i+1 点是(xi+1,yi+1),其中
该算法在x或y变化比较大的方向的增量绝对值为1,而另一方向上的增量绝对值小于等于1。
当Δx> Δy>0,即直线斜率小于1,应使x方向每次增加1,y方向最多增加1,此时区Δt=1/Δx;
同理,当Δy>Δx>0,直线斜率大于1,取Δt=1/Δy,所
4、直线的DDA算法程序
void dda(Graphics g, int x1, int x2, int y1, int y2) { int k; //应该走几步 float x, y, dx, dy; //变化的增量 k = Math.abs(x2-x1); if (Math.abs(y2-y1)>k){ k = Math.abs(y2-y1); } //k=max(x2-x1,y2-y1) dx = (float)(x2-x1)/k; dy = (float)(y2-y1)/k;//变化的增量 x = (float)x1; y = (float)y1;//起始点 for (int i = 0; i <k ; i++ ){ g.drawLine((int)(x+.5f), (int)(y+.5f), (int)(x+.5f),(int)(y+.5f)); //画点,并完成四舍五入 x = x+dx; y = y+dy; } }
基本增量算法需要进行浮点数运算,产生一个像素需要做两次加法和两次取整运算,效率低且取整运算不利于硬件实现。
那有什么办法能够改进,不使用浮点数运算,使用整数加法运算呢?
三、Bresenham算法
核心思想
该算法的思想是通过各行、各列像素中心构造一组虚拟网络线。按照直线起点到终点的顺序,计算直线与各垂直网络线的交点,然后根据误差项的符号确定该列像素中于此交点最近的像素。
每次x+1,y的递增(减)量为0或1,它取决于实际直线与最近光栅网格点的距离,这个距离最大误差为0.5。
误差项d的初值为0,d=d+k,一旦d>=1,就把它减去1,保证d的相对性,且在0、1之间。
关键是将这个算法的效率也搞到整数加法,如何提高到整数加法?
算法改进
改进1:
令e=d-0.5
改进2:
由于算法中只用到误差项的符号,于是可以用e*2*Δx来替换e
算法步骤
Bresenham算法很像DDA算法,都是加斜率,
但DDA算法是每次求一个新的y以后取整来画,而Bresenham算法是判断符号来决定上下两个点。
所以Bresenham算法集中了DDA算法的优点,而且应用范围广泛。
Bresenham算法程序
未改进前的算法。缺点:有除法和浮点数
m=(double)dy/(double)dx; e = m–0.5;//e表示直线在网格纵轴上的交点与中点的差值 for(i=0; i<dx; i++) { g.drawLine(x,y,x,y); if(e>=0)//e>=0,y选择上方的像素点 { y=y+1; e=e–1; } //e<0,y选择下方的像素点 x=x+1; e=e+m; } //其中dy=ye–ys,dx=xe–xs。
改进后的算法:
void bresenham(Graphics g, int xs, int ys, int xe, int ye) { int dx = xe - xs; int dy = ye - ys; int e = 2 * dy - dx; int x = xs; int y = ys; for (int i = 0; i < dx; i++) { g.drawLine(x, y, x, y); if (e >= 0)//e>=0,y选择上方的像素点 { y = y + 1; e = e - 2 * dx; } //e<0,y选择下方的像素点 x = x + 1; e = e + 2*dy; } }
Bresenham算法的优点:
1、不必计算直线之斜率,因此不做除法;
2、不用浮点数,只用整数;
3、只做整数加减法和乘2运算,而乘2运算可以用硬件移位实现。
Bresenham算法速度很快,并适于用硬件实现。
来源:https://www.cnblogs.com/wkfvawl/p/11621653.html