光栅图形学
光栅图形学主要研究如何使光栅图形最完美地逼近实际图形,本文主要介绍光栅图形学的几个基本算法。
直线段的扫描转换算法
一个像素宽直线绘制的三个常用算法:数值微分法、中点画线法、Bresenham算法
数值微分法(DDA)
程序:
1 |
|
中点画线法
程序(斜率也是小于1):
1 |
|
Bresenham算法
使用最广泛的直线扫描转换算法,仍然假定直线斜率在0~1之间,类似于中点法,由误差项符号决定下一像素
1 |
|
圆弧的扫描转换算法
圆的八对称性:若已知圆弧上一点(x, y),可以得到其关于四条对称轴的其它7个点的性质,因此只要扫描转换八分之一圆弧,就可以求出整个圆弧的像素集。
简单方程产生圆弧
利用其函数方程,直接离散计算
中点画圆法
程序:
1 |
|
圆的Bresenham算法
程序:
1 |
|
椭圆的扫描转换
椭圆的中点算法:和圆类似,确定一个像素后,接着在两个候选像素的中点计算一个判别式的值,根据判别式的符号确定哪个像素离椭圆更近。
多边形区域填充
表示方法
两种表示方法:
- 顶点表示:直观,不能直接用于面填充
- 点阵表示:可以用于面填充
多边形填充-扫描算法
步骤
- 求交:计算扫描线与多边形各边的交点
- 排序:把所有交点按x值递增顺序排序
- 配对:第一个与第二个,第三个与第四个等;每对交点代表扫描线与多边形一个相交区间
- 着色把相交区间内的象素置成多边形颜色,把相交区间外的象素置成背景色
活性边:与当前扫描线相交的边
活性边表AET:按与扫描线交点按x坐标递增顺序存放的活性边链表
活性边表节点保存的信息:
- 当前扫描线与边的交点
- 从当前扫描线到下一条扫描线之间的x增量
- 边所交的最高扫描线号
算法过程:
多边形填充-边界标志算法
在帧缓冲器中对多边形边界所经过的像素打上标志,按扫描线将位于多边形内的各个区段着上所需颜色。
多边形填充-种子填充算法
先将区域的一点赋予指定的颜色,然后将该颜色扩展到整个区域的过程,要求区域是连通的。
种子填充的递归算法原理简单,但效率不高,下面介绍种子填充的扫描线算法:
步骤:
初始化
堆栈置空。将种子点(x, y)入栈
出栈
若栈空则结束。否则取栈顶元素(x, y),以y作为当前扫描线
填充并确定种子点所在区段
从种子点(x, y)出发,沿当前扫描线向左、右两个方向填充,直到边界。分别标记区段的左、右端点坐标为xl和xr
确定新的种子点
在区间[xl,xr]中检查与当前扫描线y上、下相邻的两条扫描线上的象素。若存在非边界、未填充的象素,则把每一区间的最右象素作为种子点压入堆栈,返回第(2)步
字符的生成
点阵式字符
定义成一个成为掩膜的矩阵,由点阵中的不同值表达字符的形状。
变化:加租、旋转等,如下图
矢量型字符
用点坐标的序列表示各个笔划
方向编码式字符
用若干种方向编码(如8方向编码)来表达一个字符
轮廓字型技术
一种点阵字符压缩技术,对字型数据压缩后再存储,使用时,将压缩的数据还原为字符位图点阵
原理:
- 采用直线、或者二/三次Bezier(贝塞尔)曲线的集合来描述一个字符的轮廓线。轮廓线构成一个或若干个封闭的平面区域
- 轮廓线定义加上一些指示横宽、竖宽、基点、基线等的控制信息,就构成了字符的压缩数据。这种控制信息用于保证字符变倍时引起的字符笔划原来的横宽/竖宽变大变小时,其宽度在任何点阵情况下永远一致
- 采用适当的区域填充算法,可以从字符的轮廓线定义产生的字符位图点阵
裁剪
确定图形中哪些部分落在显示区内,以便只显示该部分图形的过程。
一般先裁剪再扫描转换
直线段裁剪-Cohen-Sutherland裁剪算法
延长窗口的边,将二维平面分成九个区域。
裁剪一条线段时,先求出P1P2所在的区号code1,code2
- 若code1=0 且 code2=0,则线段P1P2完全在窗口内,应取之
- 若按位与运算code1 & code2≠0,则说明两个端点同在窗口的上方、下方、左方或右方。可判断线段完全在窗口外,可弃之
- 否则,按第三种情况处理。求出线段与窗口某边的交点,在交点处把线段一分为二,其中必有一段在窗口外,可弃之。在对另一段重复上述处理
直线段裁剪-中点分割裁剪算法
首先采用前述方法对线段端点进行编码,把线段与窗口的关系分为三种情况: 全在、完全不在、线段和窗口有交
对前两种情况,进行一样的处理(显示或抛弃)
对第三种情况,用中点分割方法求出线段与窗口的交点
- 从P0点出发找出距P0最近的可见点A(从P0往P1方向运动第一个进入窗口的点)
- 从P1点出发找出距P1最近的可见点B (从P1往P0方向运动第一个进入窗口的点)
- 两个可见点之间的连线即为线段P0P1的可见部分
中点分割方法
先求出P0P1的中点Pm
若P0Pm不是显然不可见的,并且P0P1在窗口中有可见部分,则距P0最近的可见点一定落在P0Pm上,所以用P0Pm代替P0P1
否则若P0Pm是显然不可见的,则取PmP1代替P0P1
再对新的P0P1求中点Pm。重复上述过程,直到PmP1长度小于给定的控制常数为止,此时Pm收敛于交点
由于该算法的主要计算过程只用到加法和除2运算,所以特别适合硬件实现,同时也适合于并行计算
直线段裁剪-梁友栋-Barskey算法
一种参数化裁剪算法,更快。
多边形裁剪
Sutherland-Hodgeman算法
基本思想是一次用窗口的一条边裁剪多边形。
算法的每一步,考虑窗口的一条边以及延长线构成的裁剪线。该线把平面分成两个部分:一部分包含窗口,称为可见一侧;另一部分称为不可见一侧。依序考虑多边形的各条边的两端点S、P。
它们与裁剪线的位置关系只有四种
- S、P均在可见一侧=>仅输出顶点P
- S、P均在不可见一侧=> 输出0个顶点
- S可见,P不可见=>输出线段SP与裁剪线的交点I;
- S不可见,P可见=>输出线段SP与裁剪线的交点I和终点P
记忆规则:交点(如果存在)+终点(如果可见)
字符串裁剪
主要分为字符串精度剪裁、字符精度剪裁、像素精度剪裁
反走样
提高分辨率
原理
显示器分辨率提高一倍,直线经过两倍的象素,锯齿也增加一倍,但同时每个阶梯的宽度也减小了一倍,显示出的直线段看起来就平直光滑了一些
缺点
这种反走样方法是以4倍的存储器代价和扫描转换时间获得的。因此,增加分辨率虽然简单,但是不经济的方法,而且它也只能减轻而不能消除锯齿问题
区域采样
假定每个象素是一个具有一定面积的小区域,将直线段看作具有一定宽度的狭长矩形。当直线段与象素有交时,求出两者相交区域的面积,然后根据相交区域面积的大小确定该象素的亮度值。
象素的显示灰度值=round(阴影面积*象素的最大灰度值) 。反走样效果较好。
加权区域采样
使相交区域对象素亮度的贡献依赖于该区域与象素中心的距离。当直线经过该象素时,该象素的亮度F是在两者相交区域A´上对滤波器(函数w)进行积分的积分值。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!