直线扫描算法

rel="File-List" href="file:///E:%5Ctmp%5CAPPLIC%7E1%5Cmsohtml1%5C02%5Cclip_filelist.xml"> rel="Edit-Time-Data" href="file:///E:%5Ctmp%5CAPPLIC%7E1%5Cmsohtml1%5C02%5Cclip_editdata.mso"> 每次想写点东西的时候,都不知道该写点什么,想起来在AI for Game Developer上看到使用bresenham算法来做tile地形中的追逐和逃避算法,正好可以写点关于直线画法的东西,这也是计算机图形学中的基本图元生成算法了,还是比较简单的。很多人都写过,网上资料也很多,这里也就不怕再重复一次了。

数值微分(DDA)算法

首先从最直观的DDA算法说起,已知过端点P0 (x0, y0), P1(x1, y1)的直线段Ly = kx + b,容易得知直线斜率为:k = (y1-y0)/(x1-x0),(假设x1 x0)。

我们假设|k| 1,这样x每增加1y将增加k,并且保证x每增加1y的增量不能大于1;如果|k| > 1,则应该将xy互换。由于k是浮点数,因此算法中需要将y舍入为int型,并圆整到最接近的位置。

算法本身也很简单,C++形式的算法实现如下:

void drawLineDDA(int x0, int y0, int x1, int y1, long color)

{ 

       int dx = x1- x0;

itn dy = y1- y0;

       float k = dy/dx;

float y = y0;

       for (int x = x0; x <= x1; x++){

setpixel (x, int(y+0.5), color);

y=y+k;

     }

}

很明显DDA算法在每次迭代中的x, y值是上一步的值加上一个增量获得的,因此它是一个增量算法。

但是这种方法直观,但效率太低,因为每一步需要一次浮点乘法和一次舍入运算。

Bresenham画线算法

Bresenham画线算法是使用最广泛的直线生成算法,DDA算法确定下一个点坐标的方式是根据直接斜率k得到,由于k是浮点数,因此将不得不使用浮点运算,并将浮点数转换整数。而Bresenham算法的思想则是根据误差项来决定下一个像素是取正右方还是右上方(|k| < 1的情况),因此就不需要求斜率k的除法运算和浮点数运算,提高了效率。

假设以确定了第i个点Pi(xi, yi),接下来需要确定第i+1个点P(x, y),那么根据直线方程有:

y = yi + k;

如果delta(i+1) = y – (yi + 0.5f) < 0,那么表明PC点较近,取C点,否则取D点。左图中P将取为D点。当i=0时有,delta(1) = y1 – (0+0.5) = -0.5 (k < 1)。由此我们可以得到画线的方法。

1) 求出k = (y1 – y0)/(x1 – x0),初始化delta = -0.5;

2) 迭代x,每次delta += k,如果delta 0则表明需要取右上方的点,使y1,同时使e1

算法代码如下:

int dx = x1- x0;

itn dy = y1- y0;

       float k = dy/dx, delta = -0.5f;

       for (int x = x0, y = y0; x <= x1; x++){

setpixel (x, y, color);

delta +=k;

if(delta >= 0){delta -= 1; y++;}

     }

}

考察上面的方法,依然需要计算k和使用浮点数;好像比起DDA没有什么改进嘛,只不过是将浮点运算搬到了误差项delta上而已,其实我们距目标已经非常近了,从算法中可以看到只用到误差项delta的符号来判断,因此可以做如下的简单替换:

e = delta*dx*2

现在一切水落石出了,我们已经可以消除浮点数了,也不必计算k了。好了,下面是完整的Bresenham算法程序:

void drawLineBresenham(int x0, int y0, int x1, int y1, long color)

{ 

       int dx = x1- x0;

itn dy = y1- y0;

if(dx >= dy){ // |k| <= 1

       int e = -dx;

       for (int x = x0, y = y0; x <= x1; x++){

setpixel (x, y, color);

e += 2*dy;

if(e >= 0){e -= 2*dx; y++;}

    }

}

else{ // |k| > 1

       int e = -dy;

       for (int x = x0, y = y0; y <= y1; y++){

setpixel (x, y, color);

e += 2*dx;

if(e >= 0){e -= 2*dy; x++;}

    }

}

}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值