本文整理自西安交通大学软件学院祝继华老师的计算机图形学课件,请勿转载
多边形的扫描转换与区域填充
多边形两种重要的表示方法:
-
顶点表示:用多边形的顶点序列来表示
直观,占内存少,便于几何变换,不能用于面着色
-
点阵表示:用位于多边形内部的像素几何刻画
便于帧缓冲器表示图形且是面着色所需的多边形表示形式
多边形的扫描转换:把多边形的顶点表示转换为点阵表示
扫描线算法
基本思想
按扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,即完成填充工作
下面均以此图为例说明
数据结构
-
活性边表(AET):把与当前扫描线相交的边称为活性边
并把它们按与扫描线交点x坐标递增的顺序存放在一个链表中,结点内容如下:
- x:当前扫描线与边的交点坐标;
- △x:从当前扫描线到下一条扫描线间x的增量;
- y m a x y_{max} ymax:该边所交的最高扫描线号 y m a x y_{max} ymax。
这里是上图上方第二条线对应的活性边表
-
新边表(NET)
需为每条扫描线建立一个新边表(链表),存放着在扫描过程中第一次出现的边。
若某边的较低端点为ymin,则该边就放在扫描线ymin的新边表中
增量的计算
假定当前扫描线与多边形某一条边的交点的X坐标为x,则下一条扫描线与该边的交点不要重计算,只要加一个增量△x。
设该边的直线方程为:ax+by+c=0;
若
y
=
y
i
,
x
=
x
i
y=y_i,x=x_i
y=yi,x=xi;则当
y
=
y
i
+
1
y= y_i+1
y=yi+1时,
x
i
+
1
=
1
a
(
−
b
⋅
y
i
+
1
−
c
i
)
=
x
i
−
b
a
x_{i+1}=\frac{1}{a}(-b\cdot y_{i+1}-c_i)=x_i-\frac{b}{a}
xi+1=a1(−b⋅yi+1−ci)=xi−ab
其中
Δ
=
−
b
a
\Delta = -\frac{b}{a}
Δ=−ab为常数
交点的取舍
只需检查该顶点连接的两条边的另外两个端点的y值。按这两个y值中大于交点y值的个数是0,1,2来决定
- 如扫描线与多边形相交的边分别处于扫描线的两侧,则计一个交点,如P3,P1。
- y值大于交点y值的个数是1
- 如扫描线与多边形相交的边分处扫描线同侧
- 如 y i < y i + 1 , y i < y i − 1 y_i<y_{i+1}, y_i<y_{i-1} yi<yi+1,yi<yi−1, 则计两个交点如P2 ,p5
- 如 y i > y i + 1 , y i > y i − 1 y_i>y_{i+1}, y_i>y_{i-1} yi>yi+1,yi>yi−1,则计0个交点,如P4,P6 。
- 如扫描线与多边形边界重合, 则计一个交点。
算法过程
对每一条扫描线 y = i y=i y=i
- step1:获取新边表NET;
- step2: 把新边表NET[i]中的边结点,用插入排序法插入活性边表AET,使活性边表按X坐标递增顺序排序;
- step3:遍历AET表,把配对交点之间的区间(左闭右开)上的各象素(X,Y),用drawpixel(x,y,color)改写象素颜色值;
- step4:遍历AET表,把Ymax=i的结点从AET表中删除,并把Ymax>i的结点的X值递增△X;
- step5:重复各扫描线。
例子
边界标志算法
基本思想
帧缓冲器中对多边形的每条边进行直线扫描转换,亦即对多边形边界所经过的象素打上标志,然后再采用和扫描线算法类似的方法将位于多边形内的各个区段着上所需颜色
实现步骤
- 扫描线依从左到右顺序访问该扫描线上的像素
- 使用一个布尔量inside来指示当前点是否在多边形内的状态:初值为假,访问到边界像素时,取反
- 对于被访问的像素,如inside为真,则填充颜色
// 多边形定义 polydef; int color;
void edgemark_fill(polydef, color)
{ // 对多边形polydef每条边进行直线扫描转换;
inside = FALSE;
for (每条与多边形polydef相交的扫描线y ){
for (扫描线上每个象素x ){
if(象素 x 被打上边标志)
inside = ! (inside);
if(inside!= FALSE)
drawpixel (x, y, color);
else drawpixel (x, y, background);
}
}
}
评价
- 用软件实现时,扫描线算法与边界标志算法的执行速度几乎相同
- 由于边界标志算法不必建立维护边表以及对它进行排序,所更适合硬件实现,其执行速度比有序边表算法快一至两个数量级
区域填充算法
- **区域:**已表示成点阵形式的填充图形,象素集
- 区域可采用两种表示形式:
- 内点表示
- 枚举出区域内部的所有像素
- 内部的所有像素着同一个颜色
- 边界表示
- 枚举出边界上所有的像素
- 边界上的所有像素着同一颜色
- 内点表示
- 区域填充:先将区域的一点赋予指定的颜色,然后将该颜色扩展到整个区域的过程
- 区域填充算法要求区域是连通的
- 连通性:
- 4连通:两个像素点是上下或左右相连
- 8连通:两个像素点是上下或左右相连的,或者是对角线方向相连的
种子填充算法
设G为一内点表示的区域,(x,y)为区域内一点,old_color为G的原色。现取(x,y)为种子点对区域G进行填充:种子象素入栈,当栈非空时,执行如下三步操作
- 栈顶象素出栈
- 将出栈象素置成new_color
- 按右、上、左、下的顺序检查与出栈象素相邻的四个象素,若其中某个象素在边界内且未置成new_color ,则把该象素入栈
// 内点表示的4连通区域的递归填充算法:
void FloodFill4(int x,int y,int oldcolor,int newcolor){
if(getpixel(x,y)==oldcolor){ //属于区域内点oldcolor
drawpixel(x,y,newcolor);
FloodFill4(x,y+1,oldcolor,newcolor);
FloodFill4(x,y-1,oldcolor,newcolor);
FloodFill4(x-1,y,oldcolor,newcolor);
FloodFill4(x+1,y,oldcolor,newcolor);
}
}
例子
多边形由P0P1P2P3P4构成,P0(1,5)P1(5,5)P2(7,3)P3(7,1)P4(1,1)
设种子点为(3,3),搜索的方向是右、下、左、上。依此类推,最后像素被选中并填充的次序如图中箭头所示
区域填充的扫描线算法
基本思想
- 首先填充种子点所在扫描线上的位于给定区域的一个区段
- 然后确定与这一区段相连通的上、下两条扫描线上位于给定区域内的区段,并依次保存下来
- 反复这个过程,直到填充结束
实现
- 建立一个存放每条扫描线各填充区段右端点的堆栈,最初把种子像素压入堆栈
- 沿扫描线对出栈像素左、右像素进行填充直至遇到边界像素,即每出栈一个像素就对区域内包含该像素的整个连续区段进行填充
- 检查与当前扫描线相邻的上下两条扫描线的有关像素是否全为边界像素或已填充的像素。若存在非边界、未填充的像素则把未填充的每一连续区段最右像素作为新种子像素入栈
- 重复一至三步,直到所有的区域都填充完成