980 likes | 1.15k Views
计算机图形学. 武汉大学电子信息学院 王泉德 qdwang@sohu.com. 第五章 图形生成和计算. 一、 区域填充. 一个区域是指一组相邻而又相连的象素,且具有同样的属性。区域的建立和定义通常可采用两种方式: 内定义区域( Interior-Defined ):区域内部所有象素具有同一种颜色或亮度值,而区域外的所有象素具有其他颜色或亮度值。 边界定义区域( Boundary-Defined ):区域边界上所有象素均具有特定的颜色或亮度值,而在区域内、外的象素则具有不同于边界值的颜色或亮度值。.
E N D
计算机图形学 武汉大学电子信息学院 王泉德 qdwang@sohu.com
一、 区域填充 一个区域是指一组相邻而又相连的象素,且具有同样的属性。区域的建立和定义通常可采用两种方式: • 内定义区域(Interior-Defined):区域内部所有象素具有同一种颜色或亮度值,而区域外的所有象素具有其他颜色或亮度值。 • 边界定义区域(Boundary-Defined):区域边界上所有象素均具有特定的颜色或亮度值,而在区域内、外的象素则具有不同于边界值的颜色或亮度值。
给出一个区域的边界,要求对边界范围内的所有象素单元赋予指定的颜色代码称为区域填充。区域填充算法可分为两大类:给出一个区域的边界,要求对边界范围内的所有象素单元赋予指定的颜色代码称为区域填充。区域填充算法可分为两大类: • 种子填充算法:假定封闭轮廓线内某点是已知的,然后算法开始搜索与种子点相邻且位于轮廓线内的点。种子填充算法只适用于光栅扫描设备。 • 扫描转换填充算法:按扫描线的顺序确定某一点是否位于多边形或轮廓线范围之内。算法一般从轮廓线的顶部开始进行到它的底部。 区域填充的边界可以是直线也可以是曲线。
1:多边形区域的扫描转换 2:多边形区域的种子填充 3:光栅图形的反走样算法
1.多边形的扫描转换 1.1 扫描转换 计算机生成的物体常常可以用若干多边形来描述,有些非多边形的物体,也可以用多边形来逼近。在计算机图形学中,多边形有两种重要的表示方法:顶点表示和点阵表示。 顶点表示是用多边形的顶点序列来刻划多边形。这种方式直观,几何意义强,占用内存少,但不能直接用于面着色。 点阵表示是用位于多边形内的象素的集合来刻划多边形。这种方法虽然失去了很多重要的几何信息,但便于运用帧缓冲器表示图形,是面着色所需要的图形表示形式。
顶点表示的多边形 P1 P3 P2 P4 P0
光栅图形的一个基本问题就是把多边形的顶点表示转换成为点阵表示,也就是从多边形的给定边界出发,求出位于其内部的各个象素,并给帧缓冲器内部的各个对应元素设置相应的灰度和颜色,通常这种转换称为“多边形的扫描转换”。光栅图形的一个基本问题就是把多边形的顶点表示转换成为点阵表示,也就是从多边形的给定边界出发,求出位于其内部的各个象素,并给帧缓冲器内部的各个对应元素设置相应的灰度和颜色,通常这种转换称为“多边形的扫描转换”。 • 多边形的扫描转换过程,实际上就是给多边形包围的区域着色的过程,因此是一种面着色的方法。
扫描转换的常用算法 • 逐点判断算法 • 扫描线算法 • 边缘填充算法 • 边界标志算法
1.2 逐点判断算法 • 思想:逐个象素判别,确定它们是否在多边形内部,从而给出位于多边形内的点(象素)的集合。 • 难点:如何确定一个点是否在多边形内部?
点是否在多边形内部的检验 为了对多边形内部的象素点赋值,首先要解决如何判断一个点是否在多边形内部。 判断的方法:先在多边形外部找一个点,然后用线段连接待判决点和待判决点,计算出此线段与多边形边界相交的次数,如果交点的数目为奇数,则待判决点在多边形内部;如果为偶数,待判决点在多边形外部。 1 2 1 P 4 3 3 2
2 P’ 1 2 3 2 1 4 3 1 图例
(1)怎样才能“在多边形外部找一个点”?确定多边形的包围矩形:即xmin,ymin,xmax,ymax,在包围矩形外的点肯定是多边形的外部点; (2)交点计数的特殊情况处理 • 线段和多边形相交在顶点(奇点的处理): • 当线段与多边形P的交点是P的顶点Pi时,则称该交点为奇点; • 为了保证线段和多边形P边界的交点个数为偶数,现将奇点分为极值点和非极值点两类:如果相交的两条边位于线段同侧,则顶点Pi为极值点,否则为非极值点; • 当奇点是P的极值点时,该点按照两个交点计算;否则按照一个交点计算。 • 线段和多边形边共线:线段与多边形相交二次;
算法实现 • 现设P=P0P1…PnP0为一给定的多边形。Framebuffer(x,y)是与点(x,y)相对应的帧缓冲器中的元素。则逐点判断的算法可以表示成为如下的程序: for y:= ymin to ymax do for x:= xmin to xmax do if inside(p, x, y) then setpixel(framebuffer, x, y, polygon-color) else setpixel(framebuffer, x, y, back-color);
逐点判断的算法虽然程序简单,但是不可取。原因是速度太慢,该算法割断了各个象素之间的联系,孤立的考察各个象素和多边形P的内外关系,使得几十万甚至几百万个象素都要一一判别,每次判别都要多次求交点,需要做大量的乘除运算,花费大量的时间。逐点判断的算法虽然程序简单,但是不可取。原因是速度太慢,该算法割断了各个象素之间的联系,孤立的考察各个象素和多边形P的内外关系,使得几十万甚至几百万个象素都要一一判别,每次判别都要多次求交点,需要做大量的乘除运算,花费大量的时间。
1.3 扫描线算法 扫描线算法是多边形扫描转换的常用方法。相比起逐点判断算法,扫描线算法充分利用了象素之间的连贯性,避免的对象素的逐点判断和反复求交的过程。 扫描线算法综合利用了区域的连贯性,扫描线连贯性和边的连贯性等三种形式的连贯性。
区域的连贯性 yk+1 yk 扫描线yk和yk+1将多边形P分割成若干的梯形,它们具有如下的性质:
梯形的两底边分别在y=yk和y=yk+1两条扫描线上,腰在多边形P的边上和显示屏幕的边界上;梯形的两底边分别在y=yk和y=yk+1两条扫描线上,腰在多边形P的边上和显示屏幕的边界上; • 这些梯形可以分为两类,一类在多边形P的内部,一类在多边形P的外部; • 两类梯形在长方形区域{yk , yk+1}内相间的排列,即相邻的两个梯形必有一个在多边形P内,另一个在P外; 根据这些性质,实际上只要知道该长方形区域z中任一梯形内一点关于多边形P的内外关系后,整个梯形内关于多边形P的内外关系就可以确定了。
x1 x2 x3 x4 yk 扫描线的连贯性 扫描线yk和多边形P相交,交点为X1,X2,….XL,它们具有如下的性质:
x1 x2 x3 x4 yk • L是偶数; • 该扫描线上,区段(Xk, Xk+1),k=1,3,5…位于多边形P内部,其余区段都在多边形P外。两类区段沿扫描线相间排列。
xe y = e xd y = d 边的连贯性 设d为一整数,d=e-1。若多边形P的边Pr-1Pr与扫描线y=d和y=e都相交,和两交点的横坐标xe和xd满足下面的关系(mr为斜率): 因此,可利用多边形与当前扫描线交点的位置及递推关系,求得多边形与下一条扫描线的交点的位置。以上性质称为边的连贯性,它是区域的连贯性在两条相邻扫描线上的体现。
扫描线和多边形相交在顶点(奇点的处理): • 当扫描线与多边形P的交点是P的顶点Pi时,则称该交点为奇点; • 为了保证扫描线和多边形P边界的交点个数为偶数,现将奇点分为极值点和非极值点两类:如果相交的两条边位于扫描线同侧,即: (yi+1-yi)*(yi-1-yi)>0 则顶点Pi为极值点,否则为非极值点; • 当奇点是P的极值点时,该点按照两个交点计算;否则按照一个交点计算。 • 扫描线和多边形边共线:线段与多边形相交二次;
算法中不需要检验每一个象素点,而只考虑与多边形边相交的交点分割后的扫描线段。算法中不需要检验每一个象素点,而只考虑与多边形边相交的交点分割后的扫描线段。 扫描线算法的实现 算法的基本思想: 利用多边形的边的连贯性和扫描线的连贯性,对于一个给定的多边形,用一组水平(垂直)的扫描线进行扫描 • 对每一条扫描线均可求出与多边形边的交点 • 交点将扫描线分割成落在多边形内部的线段和落在多边形外部的线段,并且二者相间排列 • 将落在多边形内部的线段上的所有象素点赋以给定的色彩值。
10 8 1 2 3 4 6 4 2 0 2 4 6 8 10 12 扫描线算法的实现原理 • 根据多边形内部点的连续性知:一条扫描线与多边形的交点中,入点和出点之间所有点都是多边形的内部点。所以,对所有的扫描线填充入点到出点之间所有的点就可填充多边形。 • 判断扫描线上的点是否在多边形之内根据多边形区域连续性,分为3个步骤: • 求出扫描线与多边形所有边的交点; • 把这些交点的x坐标值以升序排列; • 从奇数交点出发到偶数交点的扫描 线段位于多边形内部。 如右图,排序x坐标得到的交点序列是 (2,4,9,13),然后对交点2与4之间、9与13之间的所有象素点进行填充。
10 10 8 8 2 3 2 3 6 1 4 6 1 4 4 2 2 0 0 2 4 6 8 10 12 2 4 6 8 10 12 扫描线算法的实现原理—奇点的处理 p5 p5 p3 p3 p4 p4 p6 p6 p2 p2 p1 p1 • p1,p3,p4,p5属于局部极值点,要把他们两次存入交点表中。 如扫描线y=7上的交点中,有交点(2,7,13),按常规方法填充不正确,而要把顶点(7,7)两次存入交点表中(2,7,7,13)。p2,p6为非极值点,则不用如上处理。
扫描线算法的实现步骤 • 对于一条扫描线填充过程可以分为四个步骤: • 求交 • 排序 • 配对 • 填色
扫描线算法的数据结构 • 边的活化链表(AET):把与当前扫描线相交的边称为活性边,并把它们按与扫描线交点x坐标递增的顺序存放在一个链表中,结点内容 • x:当前扫描线与边的交点坐标 • △x:从当前扫描线到下一条扫描线间x的增量(边斜率的倒数) • ymax:该边所交的最高扫描线号ymax
边的分类表(ET):存放在该扫描线第一次出现的边。若某边的较低端点为ymin,则该边就放在扫描线ymin的新边表中。边的分类表(ET):存放在该扫描线第一次出现的边。若某边的较低端点为ymin,则该边就放在扫描线ymin的新边表中。 • x:边的下端点的x坐标; • △x:从当前扫描线到下一条扫描线间x的增量 • ymax:该边所交的最高扫描线号ymax
扫描线算法描述 • 建立ET表,置y为ET表中非空的最小y坐标值 • 置AEL表为空,且按照y值将ET表的边加入AEL表中 • while AEL表非空并且ET表中非空 do begin • 对AEL表中的xmin值按升序排列 • 按照AEL表中交点前后次序,在每对奇偶交点间的x段予以填充 • if 扫描线 y=ymax then 从AEL表中删除这些边 • 对在AEL表中的其他边,计算与下一条扫描线的交点:x=x+1/m • 计算下一条扫描线:y=y+1 • 按照扫描线y值把ET表中相应的边加入AEL表中end • end of algorithm
扫描线算法性能分析 • 扫描线算法的数据结构和程序结构都远比逐点判断算法复杂,对各种表的维护和排序的耗费很大; • 但是由于它充分利用的多边形的区域、扫描线和边的三种形式的连贯性,从而避免了反复求交点的大量运算,因此速度比逐点判断法快的多。
1.4 边缘填充算法 • 基本思想: 在光栅图形中,如果某区域已经着上了为M的某种颜色,则对M做偶数次求补运算后,该区域的颜色不变;而做奇数次求补运算后,该区域则变为 的颜色。因此,边缘填充算法对于每一条扫描线与多边形边的交点(x,y),依次将交点右方的所有象素取补,从而实现对多边形的填充。
算法实现 • 步骤1:将位于扫描线y=e上的所有象素都着上值为M的颜色; for x:= xmin to xmax do setpixel(framebuffer, x, e, M); • 步骤2:对每一个交点xi,依次向右求补; for i:= 1 to m do for x:= xi to xmax do complement(framebuffer, x, e) 当完成上述两个步骤后,扫描线y=e上位于多边形内部的象素进行了奇数次求补运算,并着上了 的颜色。
改进: 对于复杂的图形,一些象素可能被访问多次,一种改进的办法是引入栅栏。通过多边形设一栅栏,每次只对交点与栅栏之间的象素点取补,可使访问象素的次数减少。下图示出了这一算法的原理。
边缘填充算法性能分析 边缘填充算法的数据结构和程序结构都比扫描线算法简单的多。因为边缘填充算法用求补代替了排序,因此在算法执行时需要对帧缓冲器中的大批元素反复的赋值,因此速度不比扫描线算法快。
1.5 边界标志算法 • 算法思想: 先用一种特殊的颜色将多边形的边界勾画出来,然后在采用和扫描线算法相类似的方法将位于多边形内的各个区段着上所需要的颜色。
边界标志算法实现步骤 • 步骤1:以值为boundary-color的特殊颜色勾画多边形P的边界。可采用相应的直线生成算法; • 步骤2:逐条扫描线对多边形着色。先将扫描线上值为boundary-color的特殊象素点依x坐标递增顺序两两配对,再将中间的象素着上相应的颜色;
边界标志算法类语言描述 For y:= ymin to ymax do Begin interior-point := false; for x:= xmin to xmax do begin if getpixel(framebuffer,x,y) = boundary-color then interior-point = not interior-point; if interior-point then setpixel(framebuffer,x,y,polygon-color) else setpixel(framebuffer,x,y,background-color); end end
边界标志算法性能分析 和边界填充算法相比,本算法避免了对帧缓冲器中的大量元素的多次赋值,但是需要逐条扫描线的对帧缓冲器中的元素进行搜索和比较。当用软件实现本算法时,速度与扫描线算法相当。本算法适合于硬件实现,硬件实现时的速度则有很大的提高。
2. 多边形的种子填充 • 种子填充是指先将区域内的一点(称为种子点)赋予给定的颜色,然后将这种颜色扩展到整个区域内的过程。 • 这里所指的区域是已经表示成为点阵形式的象素集合。
2.1 4连通区域和8连通区域 • 取区域内任意两点,若从其中一点出发通过上、下、左、右四中运动,只经过该区域内的点可到达另一点时,则称该区域为4连通区域; • 若将运动方式增加为水平方向、垂直方向和对角线方向的八种运动,仍满足上述的要求,则该区域为8连通区域;
2.2种子填充的递归算法 4连通区域种子填充的递归实现: procedure flood-fill-4(x,y) begin if getpixel(framebuffer,x,y) = old-color then begin setpixel(framebuffer,x,y,new-color); flood-fill-4(x,y+1); flood-fill-4(x,y-1); flood-fill-4(x-1,y); flood-fill-4(x+1,y); end end of algorithm;
递归种子填充算法的性能分析 区域填充的递归算法程序简单,表达清楚。但由于多层递归,系统堆栈反复进出,费时费内存。
29 41 38 40 37 32 44 30 23 39 33 34 31 25 26 24 17 19 10 12 20 18 7 2 3 13 11 6 16 15 8 1 5 9 4 21 14 27 22 28 35 42 36 45 43 上述填充算法是一种深度优先搜索算法,采用堆栈实现。也可以改为广度优先搜索,采用队列实现。
2.3扫描线种子填充算法 • 基本思想: 首先填充当前扫描线上的位于给定区域内的一段区段,然后确定与这一区段相邻的上下两条扫描线上位于区域内的区段,并依次将它们保存起来。反复进行这个过程,直到所保存的各区段都填充完毕为止。
扫描线种子填充算法举例 27 22 26 24 28 23 25 保存上下扫描线区段最右侧象素为种子象素 21 20 18 17 15 19 16 13 12 11 14 7 10 9 8 5 1 4 6 3 2 29 35 34 32 31 30 33 37 36 39 38 40 41 43 42 45 44
区域填充的扫描线算法描述 • 将种子象素点压入堆栈 • while 堆栈非空 do begin • 从堆栈中弹出一个种子象素 • 沿着扫描线对种子象素的左右象素进行填充,直至遇到边界 象素为止 • 标志区间内最左和最右象素为xleft和xright • if在xleft≤x≤xright中检查与当前扫描线相邻的上下两条扫描线 全为边界象素或全为已填充过的象素 then goto 2 • 在xleft≤x≤xright中标记每一个既不包含边界象素又不包含已 填充过的象素的区间 • 将每一区间的最右象素作为种子象素压入堆栈end • end of algorithm