【计算机图形学】壹 · 光栅图形学之直线段的扫描转换算法

有两点 P 0 ( x 0 ,   y 0 ) ,   P 1 ( x 1 ,   y 1 ) P_{0}(x_{0},~y_{0}),~P_{1}(x_{1},~y_{1}) 确定一条线段 L ( P 0 ,   P 1 ) L(P_0,~P_1) ,设线段所在直线方程为 y = k x + b y=kx+b 其中斜率 k = ( y 1 y 0 ) / ( x 1 x 0 ) k=(y_1-y_0)/(x_1-x_0) ,截距 b = y 0 k x 0 b=y_0-kx_0

注:以下算法中 k 1 |k| \leq 1 ,对于 k > 1 |k| > 1 的情况可同理推导得出。

1 直线方程法

1.1 基本思想:

根据直线的表达式确定线段路径上点的像素位置。

1.2 算法描述

  1. x x 坐标:将区间 [ x 0 ,   x 1 ] [x_0,~x_1] 均分,每段长 d d ,假设端点为 t i = x 0 + i d t_i=x_0+i*d t 0 = x 0 , t n = x 1 t_0=x_0, t_n=x_1 .(注: t i t_i 要为整数)
  2. 计算 y y 坐标 y i = k t i + b y_i=kt_i+b
  3. 取整:因为像素坐标都是整数,所以取整: y i = ( i n t ) ( y i + 0.5 ) y_{i}^{'}=(int)(y_i+0.5)

1.3 算法评价

  • 主要运算:乘法+加法+浮点数运算(取整)
  • 计算量大,这种方法一般不使用。

2 数值微分法(DDA算法)

2.1 基本思想

x x 的左端点 x 0 x_0 开始,向x右端点步进,步长为1(像素),按 y = k x + b y=kx+b 计算相应的 y y 坐标,并取像素点 ( x ,   r o u n d ( y ) ) (x,~round(y)) 作为当前点的坐标。

2.2 算法描述

x x 步长为 Δ x \Delta x ,则 x i + 1 = x i + Δ x x_{i+1}=x_i+\Delta x ,于是: y i + 1 = k x i + 1 + b                   = k x i + k Δ x + b         = y i + k Δ x y_{i+1}=kx_{i+1}+b\\~~~~~~~~~~~~~~~~~=kx_{i}+k\Delta x+b\\~~~~~~~=y_i+k\Delta x
Δ x = 1 \Delta x=1 时, y i + 1 = y i + k y_{i+1}=y_i+k .

  1. 起始点 ( x 0 ,   y 0 ) (x_0,~y_0)
  2. 计算下一个点 ( x i + 1 ,   y i + 1 ) = ( x i + 1 ,   r o u n d ( y i + k ) ) (x_{i+1},~y_{i+1})=(x_i+1,~round(y_i+k))
  3. 循环执行步骤2,直至 x i = x 1 x_i=x_1 ,结束。
    在这里插入图片描述

其中, r o u n d ( y i + k ) = ( i n t ) ( y i + k + 0.5 ) round(y_i+k)=(int)(y_i+k+0.5) .
在这里插入图片描述

2.3 算法评价

  • 如博文开篇所述,上述算法仅仅适用于 k 1 |k| \leq 1 的情况,在这种情况下, x x 每增加1, y y 最多增加1。对于 k > 1 |k|>1 的情况,必须把 x x y y 的地位互换, x x 随着 y y 的变化而变化, y y 每增加1, x x 相应增加 1 / k 1/k

  • 主要运算:加法+浮点数运算(取整),仍有浮点数运算,不利于硬件实现。

3 中点画线法

依旧分析 k 1 |k| \leq 1 的情况。

3.1 基本思想

根据上文,我们可以知道,当 x i + 1 = x i + 1 x_{i+1}=x_i+1 y i + 1 y_{i+1} 经过四舍五入之后只有两种情况:

  1. y p + 1 = r o u n d ( y i + 1 ) < y p + 0.5       y p + 1 = y p y_{p+1}=round(y_{i+1})<y_{p}+0.5~~\Rightarrow ~~y_{p+1}=y_{p}
  2. y p + 1 = r o u n d ( y i + 1 ) y p + 0.5       y p + 1 = y p + 1 y_{p+1}=round(y_{i+1})\geq y_{p}+0.5~~\Rightarrow ~~y_{p+1}=y_{p}+1

(假设第 i i 个像素点为 ( x p ,   y p ) (x_p,~y_p) ,则它的下一个像素点为 ( x p + 1 ,   y p + 1 ) (x_{p+1},~y_{p+1}) ,注意区分 y i y_i y p y_p
可以发现,下一个像素点 y p + 1 y_{p+1} 的取值,与 y p y_{p} y p + 1 y_{p}+1 的中点( y p + 0.5 y_p+0.5 )有很大的关系,所以我们可以直接用这个中点去判断 y p + 1 y_{p+1} 的取值。
在这里插入图片描述
如图所示,假设 A A 点为当前像素点,而且已确定,接下来的像素点确定如下:

  • 下一个像素点 B B ,在 B 1 B_1 B 2 B_2 之间选择,由于 B B_中 在直线下方,所以直线更靠近 B 2 B_2 点,则下一个像素点 ( x p + 1 ,   y p + 1 ) (x_{p+1},~y_{p+1}) 选择 B 2 B_2
  • 下一个像素点 C C ,在 C 1 C_1 C 2 C_2 之间选择,由于 C C_中 在直线上方,所以直线更靠近 C 1 C_1 点,则下一个像素点 ( x p + 2 ,   y p + 2 ) (x_{p+2},~y_{p+2}) 选择 C 1 C_1
  • 下一个像素点 D D ,在 D 1 D_1 D 2 D_2 之间选择,由于 D D_中 刚好在直线上,所以直线距离 D 1 D_1 D 2 D_2 同样远,则下一个像素点 ( x p + 3 ,   y p + 3 ) (x_{p+3},~y_{p+3}) 可以选择任一点。(一般提前统一好,选择任一点都可以)

3.2 算法描述

设函数 F ( x , y ) = y k x b F(x,y)=y-kx-b ,则: d = F ( x M , y M ) = F ( x p + 1 , y p + 0.5 ) = y p + 0.5 k ( x p + 1 ) b d=F(x_M,y_M)=F(x_p+1,y_p+0.5)=y_p+0.5-k(x_p+1)-b
根据下述方法更新下一个像素点的相关值:

  • d p < 0 线 x p + 1 = x p + 1 ,   y p + 1 = y p + 1 d_p<0\Rightarrow 中点在直线下方\Rightarrow x_{p+1}=x_p+1,~y_{p+1}=y_{p}+1 ,知道下一个像素点之后,可求 d p + 1 = F ( x p + 2 , y p + 1.5 )                   = y p + 1.5 k ( x p + 2 ) b                                   = [ y p + 0.5 k ( x p + 1 ) b ] + 1 k = d p + 1 k          d_{p+1}=F(x_p+2,y_p+1.5)\\~~~~~~~~~~~~~~~~~=y_p+1.5-k(x_p+2)-b\\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~=[y_p+0.5-k(x_p+1)-b]+1-k\\=d_p+1-k~~~~~~~~
  • d > 0 线 x p + 1 = x p + 1 ,   y p + 1 = y p d > 0\Rightarrow 中点在直线上方\Rightarrow x_{p+1}=x_p+1,~y_{p+1}=y_{p} ,同理可推:
    d p + 1 = d p k d_{p+1}=d_p-k
  • d = 0 d = 0 d > 0 d > 0
  1. 初始点 P 0 ( x 0 , y 0 ) P_0(x_0,y_0), 计算 d 0 = F ( x 0 + 1 , y 0 + 0.5 ) = y 0 + 0.5 k ( x 0 + 1 ) b = 0.5 k d_0=F(x_0+1,y_0+0.5)=y_0+0.5-k(x_0+1)-b=0.5-k
  2. 根据 { d p < 0 x p + 1 = x p + 1 ,   y p + 1 = y p + 1 , d p + 1 = d + 1 k d i 0 x p + 1 = x p + 1 ,   y p + 1 = y p ,        d p + 1 = d k         \left\{\begin{matrix} d_p<0 \Rightarrow x_{p+1}=x_p+1,~y_{p+1}=y_{p}+1,d_{p+1}=d+1-k\\d_i \geq 0\Rightarrow x_{p+1}=x_p+1,~y_{p+1}=y_{p},~~~~~~d_{p+1}=d-k~~~~~~~ \end{matrix}\right.
    更新。
  3. 循环步骤2,直至 x p = x 1 x_p=x_1 ,结束。

3.3 算法评价

  • 该算法只涉及加法。但在计算 d 0 d_0 时,涉及到( 0.5 0.5 )浮点数计算,可以将 2 d 2d 代替 d d 以消除浮点数计算。

3.4 算法实现

C++实现代码(任意k值)

4 Bresenham算法

4.1 基本思想

在这里插入图片描述
如上图所示, x i + 1 = x i + 1 x_{i+1}=x_i+1 y i + 1 = y i + k y_{i+1}=y_i+k ,现在设一个误差项 d d

  • d 0 = 0 d_0=0
  • 如果只在 x x 方向上走一步: d i + 1 = d i + k d_{i+1}=d_i+k
  • 一旦 y y 方向上走了一步, d i + 1 = d i + k 1 d_{i+1}=d_{i}+k-1

那么我们就可以比较 d d 0.5 0.5 ,来确定下一个像素点的坐标。

  • d p < 0.5 d_{p}<0.5: 直线更接近下面的点,所以下一个像素点: x p + 1 = x p + 1 ,   y p + 1 = y p x_{p+1}=x_p+1,~y_{p+1}=y_p 。同时更新 d p + 1 = d p + k d_{p+1}=d_{p}+k
  • d p 0.5 d_{p}\geq0.5: 直线更接近上面的点,所以更新 x p + 1 = x p + 1 ,   y p + 1 = y p + 1 x_{p+1}=x_p+1,~y_{p+1}=y_p+1 。同时更新 d p + 1 = d p + k 1 d_{p+1}=d_{p}+k-1

4.2 算法描述

  1. d 0 = 0 d_0=0
  2. d p + 1 = d p + k d_{p+1}=d_{p}+k ,判断 d i d_i
    • d p < 0.5 d_p<0.5 x p + 1 = x p + 1 ,   y p + 1 = y p x_{p+1}=x_p+1,~y_{p+1}=y_p
    • d p 0.5 d_p \geq 0.5 x p + 1 = x p + 1 ,   y p + 1 = y p + 1 x_{p+1}=x_p+1,~y_{p+1}=y_p+1 d p = d p 1 d_{p}=d_{p}-1
  3. 重复步骤2,直到画到 P 1 P_1 ,结束。

4.3 算法评价

  • 改进一:用 e = d 0.5 e=d-0.5 代替 d d e 0 = 0.5 e_0=-0.5 ,这样可以减少一点运算量。
  • 改进二:同中点画线算法,用 2 e Δ x 2e\Delta x 代替 e e
  • 该算法与中点画线法差不多,个人认为只是计算 d d 的顺序不同:中点画线法是确定像素点之后更新 d d ,而Bresenham算法是先计算 d d ,再去确定像素点。两者大同小异。

猜你喜欢

转载自blog.csdn.net/Vici__/article/details/105059581