小白谈计算机图形学(二)画圆篇之中点画圆法,Bresenham画圆算法,椭圆实操,线型处理详解
引言
与直线不同,圆上每点的斜率都是变化的,这一讲我们来介绍圆的画法,首先注意到圆具有八对称性,即只需研究 1 8 \frac{1}{8} 81圆周即可。
如何画圆
基本思想
-
隐函数方程: ( x i , y i = r 2 − x i 2 ) (x_i,y_i=\sqrt{r^2-x_i^2} ) (xi,yi=r2−xi2 )
-
参数方程:
{ x = r cos θ y = r sin θ \left\{ \begin {aligned} x = r\cos\theta\\ y=r\sin\theta\\ \end {aligned} \right. {x=rcosθy=rsinθ
(But计算量大,不可取)
中点画圆法
中点画圆基本思路
高效的画圆法必须避免三角函数计算和开方乘方计算,用构造函数 F ( x , y ) = x 2 + y 2 − r 2 F(x,y)=x^2+y^2-r^2 F(x,y)=x2+y2−r2
{ F ( x , y ) = 0 ( 圆 上 的 点 ) F ( x , y ) > 0 ( 圆 外 的 点 ) F ( x , y ) < 0 ( 圆 内 的 点 ) \left\{ \begin {aligned} F(x,y)=0(圆上的点)\\ F(x,y)>0(圆外的点)\\ F(x,y)<0(圆内的点)\\ \end {aligned} \right. ⎩⎪⎨⎪⎧F(x,y)=0(圆上的点)F(x,y)>0(圆外的点)F(x,y)<0(圆内的点)
选取衡量标准,判断下一点是在 ( x i + 1 , y i ) (x_i+1,y_i) (xi+1,yi)还是 ( x i + 1 , y i − 1 ) (x_i+1,y_i-1) (xi+1,yi−1),用 m ( x i + 1 , y i − 0.5 ) m(x_i+1,y_i-0.5) m(xi+1,yi−0.5)代入得判别式:
d = F ( x m , y m ) = F ( x i + 1 , y i − 0.5 ) = ( x i + 1 ) 2 + ( y i − 0.5 ) 2 − R 2 \begin{aligned} d&=F(x_m,y_m)\\ &=F(x_i+1,y_i-0.5) \\ &=(x_i+1)^2+(y_i-0.5)^2-R^2 \end{aligned} d=F(xm,ym)=F(xi+1,yi−0.5)=(xi+1)2+(yi−0.5)2−R2
- d ≤ 0 d\leq0 d≤0时,下一点为 P u ( x i + 1 , y i ) P_u(x_i+1,y_i) Pu(xi+1,yi),而我们真正关心的是求出误差项的递推公式,下面来推导:
d n e w = F ( x i + 2 , y i − 0.5 ) = ( x i + 2 ) 2 + ( y i − 0.5 ) 2 − R 2 = ( x i + 1 ) 2 + ( y i − 0.5 ) 2 − R 2 + 2 x i + 3 = d o l d + 2 x i + 3 \begin{aligned} d_{new}&=F(x_i+2,y_i-0.5)\\ &=(x_i+2)^2+(y_i-0.5)^2-R^2\\ &=(x_i+1)^2+(y_i-0.5)^2-R^2+2x_i+3\\ &=d_{old}+2x_i+3 \end{aligned} dnew=F(xi+2,yi−0.5)=(xi+2)2+(yi−0.5)2−R2=(xi+1)2+(yi−0.5)2−R2+2xi+3=dold+2xi+3 - 同理, d > 0 d>0 d>0时:
d n e w = F ( x i + 2 , y i − 1.5 ) = ( x i + 2 ) 2 + ( y i − 1.5 ) 2 − R 2 = ( x i + 1 ) 2 + ( y i − 0.5 ) 2 − R 2 + ( 2 x i + 3 ) + ( 2 − 2 y i ) = d o l d + 2 ( x i − y i ) + 5 \begin{aligned} d_{new}&=F(x_i+2,y_i-1.5)\\ &=(x_i+2)^2+(y_i-1.5)^2-R^2\\ &=(x_i+1)^2+(y_i-0.5)^2-R^2+(2x_i+3)+(2-2y_i)\\ &=d_{old}+2(x_i-y_i)+5 \end{aligned} dnew=F(xi+2,yi−1.5)=(xi+2)2+(yi−1.5)2−R2=(xi+1)2+(yi−0.5)2−R2+(2xi+3)+(2−2yi)=dold+2(xi−yi)+5 - 这里讨论的是按顺时针方向生成第一个八分圆。则第一个象素是: ( 0 , r ) (0,r) (0,r),故得 d 0 d_0 d0:
d 0 = F ( 1 , R − 0.5 ) = 1 + ( R − 0.5 ) 2 − R 2 = 1.25 − R \begin{aligned} d_0&=F(1,R-0.5)\&=1+(R-0.5)^2-R^2\&=1.25-R \end{aligned} d0=F(1,R−0.5)=1+(R−0.5)2−R2=1.25−R
中点画圆改进
- 由于只用 d d d的正负,因此用 d − 0.25 d-0.25 d−0.25代替 d d d 以摆脱小数,更新后的公式为:
{ d 0 = 1 − R d n e w = d o l d + 2 x i + 3 ( d ≤ 0 ) d n e w = d o l d + 2 ( x i − y i ) + 5 ( d > 0 ) \left\{ \begin{aligned} &d_0= 1-R\\ &d_{new}=d_{old}+2x_i+3(d\leq0) \\ &d_{new}=d_{old}+2(x_i-y_i)+5(d > 0) \end{aligned} \right. ⎩⎪⎨⎪⎧d0=1−Rdnew=dold+2xi+3(d≤0)dnew=dold+2(xi−yi)+5(d>0) - 这里研究的圆心都在 ( 0 , 0 ) (0,0) (0,0)点,画任一 ( x c , y c ) (x_c,y_c) (xc,yc)点即是分别向 x , y x,y x,y方向进行了平移。
缺点:有一点走样
Bresenham画圆算法
Bresenham基本思路
考虑 1 4 \frac{1}{4} 41 象限的四分圆,此处以第一象限为例,每一点的下一个象素有三种选择,正右方 ( H ) (H) (H),右下方 ( D ) (D) (D),正下方 ( V ) (V) (V)。观察发现以 D D D的位置作为讨论依据最为科学。
Bresenham上手计算
判断 D D D点(注意是与圆上的距离不是圆心的距离)的位置:
Δ D = ( x + 1 ) 2 + ( y + 1 ) 2 − r 2 \Delta D=(x+1)^2+(y+1)^2-r^2 ΔD=(x+1)2+(y+1)2−r2
接着判断D与E,D与F和圆周的相对距离大小判断涂色:
-
Δ D < 0 \Delta D<0 ΔD<0, D D D在圆内, δ H D = ∣ Δ H ∣ − ∣ Δ D ∣ = 2 Δ D + 2 y − 1 \delta HD=|\Delta H|-|\Delta D|=2\Delta D+2y-1 δHD=∣ΔH∣−∣ΔD∣=2ΔD+2y−1,若小于等于 0 0 0则选 H H H,大于 0 0 0则选 D D D。
-
Δ D > 0 \Delta D>0 ΔD>0, D D D在圆外, δ D V = ∣ Δ D ∣ − ∣ Δ V ∣ = 2 ( Δ D − x ) − 1 \delta DV=|\Delta D|-|\Delta V|=2(\Delta D-x)-1 δDV=∣ΔD∣−∣ΔV∣=2(ΔD−x)−1,若小于等于 0 0 0则选 D D D,大于 0 0 0则选 V V V。
-
Δ D ( x p + 1 , y p − 1 ) \Delta D(x_p+1,y_p-1) ΔD(xp+1,yp−1)的初值为 Δ D = 1 + ( 1 − r ) 2 − r 2 = 2 − 2 r \Delta D=1+(1-r)^2-r^2=2-2r ΔD=1+(1−r)2−r2=2−2r
故公式为:
{ Δ D = 2 − 2 r δ H D = 2 Δ D + 2 y − 1 δ D V = 2 Δ D − 2 x − 1 Δ D < 0 { δ H D ≤ 0 ⇒ H 点 : Δ D n e w = Δ D o l d + 2 ( x + 1 ) + 1 δ H D > 0 ⇒ D 点 : Δ D n e w = Δ D o l d + 2 ( x + 1 ) − 2 ( y − 1 ) + 2 Δ D > 0 { δ H D ≤ 0 ⇒ D 点 : Δ D n e w = Δ D o l d + 2 ( x + 1 ) − 2 ( y − 1 ) + 2 δ H D > 0 ⇒ V 点 : Δ D n e w = Δ D o l d + 2 ( y − 1 ) + 1 Δ D = 0 ⇒ D 点 : Δ D n e w = Δ D o l d + 2 ( x + 1 ) − 2 ( y − 1 ) + 2 \left\{ \begin{aligned} &\Delta D=2-2r\\ &\delta HD=2\Delta D+2y-1 \&\delta DV=2\Delta D-2x-1\\ &\Delta D<0 \left\{ \begin{aligned} &\delta HD\leq0\Rightarrow H点:\Delta D_{new}=\Delta D_{old}+2(x+1)+1 \\ &\delta HD>0\Rightarrow D点:\Delta D_{new}=\Delta D_{old}+2(x+1)-2(y-1)+2 \end{aligned} \right.\\ &\Delta D>0 \left\{ \begin{aligned} &\delta HD\leq0 \Rightarrow D点:\Delta D_{new}=\Delta D_{old}+2(x+1)-2(y-1)+2 \\ &\delta HD>0 \Rightarrow V点:\Delta D_{new}=\Delta D_{old}+2(y-1)+1 \end{aligned} \right.\\ &\Delta D=0\Rightarrow D点:\Delta D_{new}=\Delta D_{old}+2(x+1)-2(y-1)+2 \\ \end{aligned} \right. ⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧ΔD=2−2rδHD=2ΔD+2y−1δDV=2ΔD−2x−1ΔD<0{δHD≤0⇒H点:ΔDnew=ΔDold+2(x+1)+1δHD>0⇒D点:ΔDnew=ΔDold+2(x+1)−2(y−1)+2ΔD>0{
δHD≤0⇒D点:ΔDnew=ΔDold+2(x+1)−2(y−1)+2δHD>0⇒V点:ΔDnew=ΔDold+2(y−1)+1ΔD=0⇒D点:ΔDnew=ΔDold+2(x+1)−2(y−1)+2
-
优点:每次在三个点中判断,走样较小,可以一次处理四分之一圆。
缺点:算法复杂。
如何画椭圆
开动脑筋,进行类比~
类同于中点画圆法,区别是画第一象限的四分之一椭圆弧,关键是找到临界点。
椭圆实操
找到切线斜率为 1 1 1的点 p ( x p , y p ) p(x_p,y_p) p(xp,yp),利用中点画线法进行处理:
{ x p = a 2 a 2 + b 2 y p = b 2 a 2 + b 2 \left\{ \begin{aligned} &x_p= \frac{a^2}{\sqrt{a^2+b^2}}\\ &y_p= \frac{b^2}{\sqrt{a^2+b^2}} \end{aligned} \right. ⎩⎪⎪⎪⎨⎪⎪⎪⎧xp=a2+b2 a2yp=a2+b2 b2
将 1 4 \frac{1}{4} 41椭圆分成 ( 0 , b ) (0,b) (0,b)到 p p p点, ( a , 0 ) (a,0) (a,0)到 p p p点两块,故公式为:
{ ( 0 , b ) 到 p 点 : { d 0 = b 2 + ( − b + 0.25 ) a 2 d n e w = d o l d + ( 2 x + 3 ) b 2 ( d ≤ 0 ) d n e w = d o l d + ( 2 x + 3 ) b 2 + ( 2 − 2 y ) a 2 ( d > 0 ) ( a , 0 ) 到 p 点 : { d 0 = ( − a + 0.25 ) b 2 + a 2 d n e w = d o l d + ( 2 y + 3 ) a 2 ( d ≤ 0 ) d n e w = d o l d + ( 2 y + 3 ) a 2 + ( 2 − 2 x ) b 2 ( d > 0 ) \left\{ \begin{aligned} &(0,b)到p点: \left\{ \begin{aligned} &d_0=b^2+(-b+0.25)a^2\\ &d_{new}=d_{old}+(2x+3)b^2(d\leq0)\\ &d_{new}=d_{old}+(2x+3)b^2+(2-2y)a^2(d>0) \end{aligned} \right.\\ &(a,0)到p点: \left\{ \begin{aligned} &d_0=(-a+0.25)b^2+a^2\\ &d_{new}=d_{old}+(2y+3)a^2(d\leq0)\\ &d_{new}=d_{old}+(2y+3)a^2+(2-2x)b^2(d>0) \end{aligned} \right. \end{aligned} \right. ⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧(0,b)到p点:⎩⎪⎨⎪⎧d0=b2+(−b+0.25)a2dnew=dold+(2x+3)b2(d≤0)dnew=dold+(2x+3)b2+(2−2y)a2(d>0)(a,0)到p点:⎩⎪⎨⎪⎧d0=(−a+0.25)b2+a2dnew=dold+(2y+3)a2(d≤0)dnew=dold+(2y+3)a2+(2−2x)b2(d>0)
线型处理
线画图元的基本属性有线型、宽度和颜色。
线宽处理
线刷子
- 当斜率 k ∈ [ − 1 , 1 ] k∈[-1,1] k∈[−1,1]之间,刷子置成垂直方向(水平方向刷),刷子的中点对准直线一端点,然后让刷子中心往直线的另一端移动,即可刷出具有一定宽度的线。
- 同理,当斜率 k ∉ [ − 1 , 1 ] k\notin [-1,1] k∈/[−1,1]之间时,把刷子置成水平方向(竖直方向刷)。
-
引起的问题:汇合外角缺口
改进
加圆帽,通过对每个方帽添加一个填充的半圆而得到。圆弧的圆心在线的端点其直径与线宽度相等。
方形刷子
把边宽为指定线宽的正方形的中心沿直线作平行移动,用方形刷子绘制的线条比用线刷子绘制的要粗一些(对角线的原因)。
线刷子与方形刷子的对比:
- 线刷子在45°角附近线条在水平和垂直方向之间切换,最细
- 方形刷子正相反由于线宽正好为对角线而显著变粗
超链接
如果你还想了解其他内容:
小白谈计算机图形学(一)如何画线
小白谈计算机图形学(二)如何画圆
小白谈计算机图形学(三)二维图形裁剪
小白谈计算机图形学(四)二维三维图形变换—1