刚学习了计算机图形学这门课程,为奠定根基的算法所倾倒,特此记录一二。
直线—中点 Bresenham 算法
DDA算法在效率上较低的原因是需要计算 k,并以之作为累加项。一个直观的改进方式,是在整个运算过程中将涉及到的数值乘以 dx (或dy),转化为整型进行运算。
中点 Bresenham 算法采用一种不同的观点来解决这一问题-判别式。我们先考虑一般的直线方程,在图形学中我们一般给出的已知条件是两个端点:
此判别方程的意义,在于直线上的所有点均使 F(x,y)=0。
现在,我们考虑离散的情形,在|k| < 1时,为判断如下图所示的与P相邻的像素点Pd和Pu中的哪一个归属于直线,我们取两者的中点M作为判决依据。将其坐标代入判别式
(1) F(M) < 0, 取上方点 Pu
(2) F(M) > 0, 取下方点 Pd
因此,若(Xi, Yi)为直线上的点,在判定下一个点(Xi+1,Yi+1)并在(Xi+1,Yi+1)和(Xi+1,Yi)中进行选择时,可以根据中点坐标(Xi+1,Yi+0.5)代入判别式进行判定。
F(Xi,Yi) = a Yi + b Xi + c = 0
F(Xi+1,Yi+0.5) = a Yi + 0.5a + b Xi + b +c
= 0.5a + b
(1) F(M) = 0.5a + b < 0, 取上方点 Pu (Xi+1,Yi+1)
(2) F(M) = 0.5a + b > 0, 取下方点 Pd (Xi+1,Yi)
两个问题:
(1) F(Xi,Yi)=0 不一定总能满足,如何解决?
(2) 尽管 a, b, c 均为整数(?),仍有一个0.5为小数。
问题(2)容易解决,仅需在计算过程中乘2即可;
问题(1)则较为麻烦。我们知道在起始点时,F(Xi,Yi)=0 总是成立的,在后续计算中我们将偏差 F(Xi,Yi)=di 累计表达为 F(Xi,Yi)-di=0,再累积起来通过一个变量来考虑的。然而,因为前面的推导基于 +1(x方向) 和 +0.5(y方向),所以对该变量要进行调整。具体通过递推完成。
递推式
因为不能保证各像素点处的F(Xi,Yi)=0,因此只能从起始点处考虑 F(X0,Y0)=0,然后依次考虑F(X0+1,Y0+0.5), F(X0+2,Y0+?),… 此处我们可以看出,必须要分两种情况。
当 F(X0+1,Y0+0.5) < 0,选择(Xi+1,Yi+1)时,再判断的下一个中点应为(Xi+2,Yi+1.5);
当 F(X0+1,Y0+0.5) > 0,选择(Xi+1,Yi)时,再判断的下一个中点应为(Xi+2,Yi+0.5);
注意每一步只有两种可能,所以这个模式是重复出现的,换句话说可用迭代的方式实现两个中点间的递推关系。
累积误差项的递推(di+1 < 0)
累积误差项的递推关系,反映了相邻中点间的递推关系。
补充细节:
算法整体描述:
- 计算初始值△x、△y、d=△x-2△y、x=x0、y=y0。
- 绘制点(x,y)。判断d的符号。若d<0,则(x,y)更新为(x+1,y+1),d更新为d+2△x-2△y;否则(x,y)更新为(x+1,y), d更新为d-2△y。
(这种递推关系类似数学归纳法,相邻中点间构建关系) - 当直线没有画完时,重复上一步骤,否则结束。
例题:
matlab代码
% P,Q为直线段端点,pixs_line为计算出的像素点
function pixs_line = bresenham_line(P,Q)
% 判断斜率 k 是否绝对值小于1
% 如果斜率大于1, X,Y坐标互换
if abs(Q(2