直线光栅化算法

在数学上,直线有无穷个点,蛋仔计算机屏幕上显示直线时,需要进行光栅化处理,用离散的像素点逼近这条直线,需要知道这些点的x,y坐标

1.数值微分法DDA

数值微分法根据直线的微分方程来绘制直线

已知直线段两个端点P0(x0,y0),P1(x1,y1)。
则可求得直线的斜率:k = (y2 - y1) / (x2 - x1)
在k,b均求出的条件下,只要知道一个x值,我们就能计算出一个y值。y = kx + b

如果横坐标x每次增加1(我们称其为步进为1,即x = x +1),那么y的步进就为k+b。
 x = x + 1
 y = y + (k + b)
 
 同样知道一个y值也能计算出x值,此时y的步进为1,x的步进为(1-b)/k。
  y = y + 1
  x = x +(1 - b) / k
  
据计算出的x值和y值,向下取整,得到坐标(x’,y’),并在(x’,y’)处绘制直线段上的一点。

为进一步简化计算,通常可令b取0,将起点看作(0,0)。
设当前点的坐标为(xi ,yi),下一个像素点的坐标为(xi+1,yi+1)
则用DDA算法求解(xi+1,yi+1)的计算公式可以概括为:
	xi+1 = xi + xStep (1)
	yi+1 = yi + yStep (2)
 
 由于光栅显示器的显示栅格问题, xStep或yStep只能取单位步长这里分为两种情况(为了用更多的离散点):
 我们一般通过计算 Δx 和 Δy 来确定xStep和yStep:
 (1)如果 |k|<=1,Δx >= Δy ,说明x轴的最大差值大于y轴的最大差值,x轴方向为步进的主方向,
			xStep = 1,yStep = k
			xi+1=xi+1
			yi+1=yi+k
 (2)如果 |k|>1,Δy > Δx,说明y轴的最大差值大于x轴的最大差值,y轴方向为步进的主方向,
			yStep = 1,xStep = 1 / k。
			xi+1=xi+(1/k)
			yi+1=yi+1

在这里插入图片描述

//DDA算法实现
void LineDDA(int x1,int y1,int xn,int yn)
{
    int dm=0;
    if(abs(xn-x1)>=abs(yn-y1));
        dm=abs(xn-x1);//x为步长方向
    else
        dm=abs(yn-y1);//y为步长方向
    float dx=(float)(xn-x1)/dm;//x为步长方向时,dx值为1
    float dy=(float)(yn-y1)/dm;//y为步长方向时,dy值为1
    float x=x1;
    float y=y1;
    for(int i=0;i<dm;i++)
    {
        putpixel((int)(x+0.5),(int)(y+0.5));//伪代码,绘点
    	x+=dx;
        y+=dy;
    }
    
}

中点画线算法

相比于Breanham算法,中点画线算法更加直接(非效率)。中点算法的重要假设是,我们能绘出没有间隔的最细的直线。两对角像素之间的连接是没有间隔的。

设线段端点为:(x1, y1),(x2, y2),∆x和∆y为水平和垂直偏移量
∆y=y2-y1
∆x=x2-x1

直线的一般方程:
f(x,y)=Ax+By+c=0
其中:A=-(∆y)  B=∆x  c=-B∆x
f(x,y)=(y1-y2)x+(x2-x1)y+x1y2-x2y1=0

假设已经确定了要显示的点(xk , yk),那么,需要确定下一个点( xk + 1 , yk + 1)是绘制在B(xk+1,yk)还是A(xk+1,yk+1)上,设M为AB的中点,Q为线段与AB边的交点。若Q在M的上面,则应取像素点A作为( xk + 1 , yk + 1 )否则取像素B

image

平面任一点( x,y)与直线的相对位置可由(x,y)的符号确定:
	f(x,y)<0,(x,y)位于直线下方
	f(x,y)=0,(x,y)位于直线上
	f(x,y)>0,(x,y)位于直线上方

将M点带入直线一般方程
F ( x m , y m ) = A x m + B y m + C F(x_m,y_m)=Ax_m+By_m+C F(xm,ym)=Axm+Bym+C

设决策参数p为M点的函数值:
p = f ( x k + 1 , y k + 0.5 ) = A ( x k + 1 ) + B ( y k + 0.5 ) + C p=f(x_k+1,y_k+0.5)=A(x_k+1)+B(y_k+0.5)+C p=f(xk+1,yk+0.5)=A(xk+1)+B(yk+0.5)+C

此时:
如果p<0;则M点在Q点下方,应取A点
如果p>0;则M点在Q点上方,应取B点
如果p=0;则M点直线上,取AB均可

di+1=di+? 是否能用增量表示来提高效率呢?
d是x,y的线性函数,所以增量计算是可行的

当d<0时

在这里插入图片描述

此时d1=d0+A+B

当d>=0时

在这里插入图片描述

此时d1=d0+A

当d0为直线第一个像素时,d0在直线上,所以

在这里插入图片描述

d0=A+0.5B

总结:

当d<0时    d(new)=d(old)+A+B
当d>=0时  d(new)=d(old)+A
d0=A+0.5B

用2d代替d拜托浮点运算,得出仅包含整数加法运算的算法
void MidPointLine(int x1,int y1,int xn,int yn)
{
    int dx,dy,d1,d2,d,x,y;
    dx=xn-x1;
    dy=yn-y1;
    d=dx-2dy;  //原初值的d1
    dt=2dx-2dy; //原1-k
    db=-2dy; //原-k
    x=x1;=y1;
    putpixel(x,y);
    while(x<xn)
    {
        if(d<0)
        {
            x++;
            y++;
            d+=dt;
        }
        else
        {
            x++;
            d+=db;
        }
        putpixel(x,y);
    }
    
}
Bresenham画线法

由Bresenham提出的一种精确而有效的光栅线生成算法,可用于显示线、圆和其他曲线的整数增量运算。它是目前最有效的线段生成算法。

基本思想
该算法的思想时通过各行各列像素中心构造一组虚拟网络线,按照直线起点到中点的顺序,计算直线与各垂直网格线的交点,然后根据误差项的符号确定该列像素中与此交点最近的像素。

在这里插入图片描述

初值:d0=0
d=d+k 

一旦d>=1,就让它减去1,保证d的相对性以及在0~1之间。

在这里插入图片描述
那么,在这种情况下我们是否也可以提升为整数加法提高效率呢?答案是肯定的。

在这里插入图片描述
在这里插入图片描述

算法步骤:

1.输入直线两端点(x0,y0),(x1,y1)
2.计算初始值dx,dy,e=-dx,x=x0,y=y0
3.绘制点(x,y)
4.e更新为e+2dy,判断e的符号,e>0,(x,y)更新为(x+1,y+1),同时将e更新为e-2dx,否则更新为(x+1,y)
5.直线没有画完,重复3,4,直到结束

补充:
在这里插入图片描述
采用增量形式:如果基于第k步 的决策变量dk表示第k+1步的决策变量,可以使算法更加有效:
d k + 1 = d k − 2 D y ( d k > 0 ) d_k+_1=d_k-2Dy(d_k>0) dk+1=dk2Dy(dk>0)
d k + 1 = d k − 2 ( D y − D x ) d_k+_1=d_k-2(Dy-Dx) dk+1=dk2(DyDx)
对每个x的值,只需要进行整数加法运算以及测试

//算法实现(仅考虑0<=k<=1的情况)
void BresenhamLine(int x1,int y1,int xn,int yn)
{
	int  x,y,dx,dy,d,d1,d2;
	dx=xn-x1;
	dy=yn-y1;
	d=2*dy-dx;
	d1=2*dy;
	d2=2*(dy-dx);
	x=x1;
	y=y1;
	putpixel(x,y);
	while(x<xn)
	{
		x++;
		if(d<0)
		{d+=d1;}
		else
		{y+=1;
		d+=d2;}
		putpixel(x,y);
	}
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值