Bresenham布雷森曼算法
【计算机图形学】画线算法——Bresenham算法(任意斜率)_bresenham算法原理-CSDN博客
一个朴素的观点:直线穿过两个像素点,哪个像素点离直线比较近,就用哪个像素点来表示这个直线的点。
为什么不直接用直线公式计算点的位置,然后取整呢?y=round(m*x+c)
因为这会涉及到三个问题:
斜率m的计算使用了除法,这回引入额外风险和计算量
m是浮点数,浮点数的乘法的计算量会比整数高
需要使用round()
函数取整,涉及到大小比较
而Bresenham算法可以避免上诉三个问题
定义:
m = x 2 − x 1 y 2 − y 1 = Δ x Δ y p k = Δ x ( d 1 − d 2 ) m=\frac{x_2-x_1}{y_2-y_1}=\frac{\Delta x}{\Delta y}\\
p_k=\Delta x(d_1 - d_2)
m = y 2 − y 1 x 2 − x 1 = Δ y Δ x p k = Δ x ( d 1 − d 2 )
推导:
f(x) & = & m(x_k+1)+b &\\
d_1 & = & f(x_k+1)-y_k & = &m(x_k+1)+b-y_k\\
d_2 & = & (y_k + 1) - f(x_k+1) & = &y_k+1-m(x_k+1)-b\\
d_1-d_2 & = & 2m(x_k+1)-2y_k-2b+1
p k = Δ x ( d 1 − d 2 ) = 2 x k Δ y − 2 y k Δ x + c p_k=\Delta x(d_1-d_2)=2x_k\Delta y - 2y_k\Delta x + c
p k = Δ x ( d 1 − d 2 ) = 2 x k Δ y − 2 y k Δ x + c
其中c = 2 Δ y + Δ x ( 2 b − 1 ) c=2\Delta y+\Delta x(2b-1) c = 2 Δ y + Δ x ( 2 b − 1 )
通过p k p_k p k 来判断像素点的取值:
\text{if }p_k<0,\text{ i.e. }d_1<d_2,\text{then }y_{k+1}=&y_k\\
\text{if }p_k\geqslant 0,\text{ i.e. }d_1\geqslant d_2,\text{then }y_{k+1}=&y_k+1\\
同理:
p k + 1 − p k = ( x k + 1 − x k ) 2 Δ y − 2 ( y k + 1 − y k ) Δ x p_{k+1}-p_k=(x_{k+1}-x_k)2\Delta y-2(y_{k+1}-y_k)\Delta x
p k + 1 − p k = ( x k + 1 − x k ) 2 Δ y − 2 ( y k + 1 − y k ) Δ x
当0 < m < 1 0<m<1 0 < m < 1 时,x k + 1 = x k + 1 x_{k+1}=x_k+1 x k + 1 = x k + 1 ,则:
p k + 1 − p k = 2 Δ y − 2 ( y k + 1 − y k ) Δ x p_{k+1}-p_k=2\Delta y-2(y_{k+1}-y_k)\Delta x
p k + 1 − p k = 2 Δ y − 2 ( y k + 1 − y k ) Δ x
其中y k + 1 − y k y_{k+1}-y_k y k + 1 − y k 的值取决于p k p_k p k :
p k + 1 = { p k + 2 Δ y , when p k < 0 , i.e. y k + 1 = y k p k + 2 Δ y − 2 Δ x , when p k ⩾ 0 , i.e. y k + 1 = y k + 1 p_{k+1}=
\begin{cases}
p_k+2\Delta y , &\text{when }p_k <0,\text{i.e. }y_{k+1}=y_k\\
p_k+2\Delta y - 2\Delta x, &\text{when }p_k \geqslant 0,\text{i.e. }y_{k+1}=y_k+1
\end{cases}
p k + 1 = { p k + 2 Δ y , p k + 2 Δ y − 2 Δ x , when p k < 0 , i.e. y k + 1 = y k when p k ⩾ 0 , i.e. y k + 1 = y k + 1
初始化:
其实端点( x 0 , y 0 ) (x_0,y_0) ( x 0 , y 0 ) 处的决策参数p 0 p_0 p 0 为:
\begin{flalign}
d_1=&y-y_0=m(x_0+1)+b-y_0=m\\
d_2=&(y_0+1)-y=y_0+1-m(x_0+1)-b=1-m\\
d_1-d_2=&2m-1\\
p_0=&\Delta x(d_1-d_2)=(2m-1)\Delta x=2\Delta y-\Delta x
\end{flalign}
为什么p k p_k p k 不这么算?因为p k p_k p k 这样算会使用斜率m,这就和最开始提出的问题没有太大差别了。
以上都是基于0 < m < 1 0<m<1 0 < m < 1 讨论的,在整个二维平面则需要讨论四种情况
0 < m < 1 0<m<1 0 < m < 1
1 < m 1<m 1 < m
− 1 < m < 0 -1<m<0 − 1 < m < 0
m < − 1 m<-1 m < − 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 void rst::rasterizer::draw_line (Eigen::Vector3f begin, Eigen::Vector3f end) { auto x1 = begin.x (); auto y1 = begin.y (); auto x2 = end.x (); auto y2 = end.y (); Eigen::Vector3f line_color = {255 , 255 , 255 }; int x,y,dx,dy,dx1,dy1,px,py,xe,ye,i; dx=x2-x1; dy=y2-y1; dx1=fabs (dx); dy1=fabs (dy); px=2 *dy1-dx1; py=2 *dx1-dy1; if (dy1<=dx1) { if (dx>=0 ) { x=x1; y=y1; xe=x2; } else { x=x2; y=y2; xe=x1; } Eigen::Vector3f point = Eigen::Vector3f (x, y, 1.0f ); set_pixel (point,line_color); for (i=0 ;x<xe;i++) { x=x+1 ; if (px<0 ) { px=px+2 *dy1; } else { if ((dx<0 && dy<0 ) || (dx>0 && dy>0 )) { y=y+1 ; } else { y=y-1 ; } px=px+2 *(dy1-dx1); } Eigen::Vector3f point = Eigen::Vector3f (x, y, 1.0f ); set_pixel (point,line_color); } } else { if (dy>=0 ) { x=x1; y=y1; ye=y2; } else { x=x2; y=y2; ye=y1; } Eigen::Vector3f point = Eigen::Vector3f (x, y, 1.0f ); set_pixel (point,line_color); for (i=0 ;y<ye;i++) { y=y+1 ; if (py<=0 ) { py=py+2 *dx1; } else { if ((dx<0 && dy<0 ) || (dx>0 && dy>0 )) { x=x+1 ; } else { x=x-1 ; } py=py+2 *(dx1-dy1); } Eigen::Vector3f point = Eigen::Vector3f (x, y, 1.0f ); set_pixel (point,line_color); } } }