图形学复习4——光栅化(画线画圆扫描线反走样算法)
来源:互联网 发布:软件著作权 无形资产 编辑:程序博客网 时间:2024/06/04 22:35
图形学复习
CH7 光栅化
前几章介绍了几何处理和裁剪变换,接下来的步骤就是光栅化
光栅化是将形式表示的几何图元转换为阵列表示的数据片元的过程,片元中每一个像素对应帧缓冲区中的每一个像素
7.1 线段生成算法
(1)DDA画线算法
设直线表达式为
DAA是基于微分运算的线段生成算法,其主要计算式便是
- 若
|m|≤1 则x方向的变化大于y方向的变化,以x方向为主方向,取δx=1 根据m计算δy=mδx - 若
|m|>1 则y方向的变化大于x方向的变化,以y方向为主方向,取δy=1 根据m计算δx=1mδy
为了有效的避免了斜率为正无穷时
下面是DDA算法C语言版本代码:
void lineDDA(int x0, int y0, int xend, int yend){ int steps, k; float xstep, ystep; float x = x0, y = y0; int dx = xend - x0; int dy = yend - y0; if (fabs(dx) >= fabs(dy)) steps = dx; else steps = dy; xstep = (float)dx / (float)steps; ystep = (float)dy / (float)steps; setPixel(round(x), round(y)); for (k = 0; k < steps; k++) { x += xstep; y += ystep; setPixel(round(x), round(y)); }}
DDA算法避免的迭代时的乘积运算因此比直接用直线表达式求点坐标效率更高,但是,每一步骤中的浮点操作和取整运算开销仍然较大(体系结构告诉我们整数运算和浮点运算效率可以相差几十倍)
(2)Bresenham画线算法
Bresenham画线算法是一种精确而有效的线段生成算法,它运用DDA的思想并通过邻近点的比较避免了浮点操作和取整运算,下面讨论斜率小于1的线段的Bresenham画线算法(斜率大于1只需要类似DDA中的比较、交换即可)
再进一步方便讨论,限定
令
令
则要比较的就是
那么每次我们可以带入上式计算大于0(
这样仍然存在乘法计算,考虑
上述即是Bresenham画线算法的思想,下面给出二维全空间的**Bresenham画线算法**C语言源代码:
void line_Bresenham(int x1, int y1, int x2, int y2){ int dx = abs(x2 - x1); int dy = abs(y2 - y1); bool morethan_45 = dy > dx; if (morethan_45) { swap(x1, y1); swap(x2, y2); dx = abs(x2 - x1); dy = abs(y2 - y1); } if (x1 > x2) { swap(x1, x2); swap(y1, y2); } int ystep = y2 > y1 ? 1 : -1; int y = y1; int x = x1; int twody = 2 * dy; int twody_minus_twodx = 2 * dy - 2 * dx; int p = twody - dx; while (x <= x2) { if (morethan_45) { plot_point(y + size, x); } else { plot_point(x, y + size); } if (p < 0) { p += twody; } else { p += twody_minus_twodx; y += ystep; } x++; }}
对称说明:
我们在1.Bresenham画线算法中给出的算法是在0 <= m <= 1的情况下的,所以对于全二维空间的直线我们需要做两次对称。(1)x轴对称 做x轴对称将m取值范围扩展到|m| <= 1: 其他不变,引入变量ystep表示y的运动方向和步长。 0 <= m <= 1时,ystep = 1;-1 <= m < 0时,ystep = -1。 那么算法的第四步迭代y坐标时将1换成ystep即可。(2)y=x对称 处理完|m| <= 1情况后,再处理|m| > 1的情况: 任意一条|m| > 1的直线都和一条|m| <= 1的直线关于y=x对称,那么则没必要写一次y方向为主方向的画线算法。直接在输入坐标后将端点坐标做一次y=x对称变换,当作一条|m| <= 1的直线来完成Bresenham画线算法的相应计算。最后在画点时再做一次y=x对称变换得到正确位置即可。
7.2 圆和椭圆生成算法
(1)中点画圆算法
中点画圆算法和Bresenham画线算法一样,通过引入决策参数来消除浮点和乘法运算
把圆划分成8个
从
中点画圆的思想就是:判断如果
取决策参数
pk<0 ,中点位于圆内,yk 更靠近圆周边界- 否则,
yk−1 更靠近圆周边界
同样有迭代思想求
p0=54−r - 若
pk<0 ,则迭代yk+1=yk,2yk+1=2yk,pk+1=pk+2xk+1+1 - 否则,迭代
yk+1=yk−1,2xk+1=2xk,2yk+1=2yk−2,pk+1=pk+2xk+1+1−2yk+1 - 两种情况均有
xk+1=xk+1,2xk+1=2xk+2
注意上述描述的是以原点为中心点的画圆算法,若中心点需要在
(2)中点画椭圆算法
中点画椭圆算法和中点画圆算法基本一样,只是需要在一象限分两个区域1、2讨论,令
区域1中:
区域2中:
注意区域2中小于0选
7.3 通用扫描线填充算法
多边形扫描线填充的方法是沿多条平行于x轴的直线
(1)扫描线算法
扫面线算法利用以下相邻像素连贯性,提高扫描效率:
- 边的连贯性:某条边与扫描线相交,它可能和下一条扫描线也相交
- 扫描线的连贯性:当前扫描线的交点顺序可能和下一条扫描线的交点顺序相同或类似
- 区间的连贯性:同一区间上像素取相同颜色填充
扫描线算法的一般步骤为:
- 求交点
- 交点排序
- 交点配对
(2)有序边表算法
有序边表算法就是一种经典的扫描线算法
定义活性边是与当前扫描线相交的边,边结构如下:
typedef struct { int ymax; //边最大y值,即与相交的最大扫面线号 float x; //当前扫描线与边相交的x坐标 float dx; //边斜率的倒数 Edge *next; //指向下一条边的指针} Edge;
那么活性边表AEL就是活性边按x递增的顺序构成的链表,和活动边表相关的是当前的扫描线,随着扫描线的移动按照如下的规则维护AEL:
- 如果一条边和下一条扫描线有交点,则根据该边的边斜率倒数dx来更新和下一条扫面线相交的x坐标点,即
x=x+dx
- 如果一条边和下一条扫描线没有交点了,那么这条边不再是活动边,则删除AEL中的这条边
- 如果有其它新边和下一条扫描线相交,那么需要把新边加入AEL
上述新边的定义是:若某条(非水平)边的下端点是y,那么称之为扫面线y的新边;很容易发现要构造AEL就先要构造定义新边表NET,NEL无序排列因此构造NET枚举一遍边即可
下面给出有序边表算法的算法描述:
- 枚举所有边,构造NET
- 取y的初始值为NET中最小的非空元素,即最低扫描线的y坐标
- 置AEL为空
- (循环开始)若NET中扫描边y的新边不为空则将所有边“取出”并插入AEL,AEL排序
- AEL边中的x两两配对(x按取整规则取整),获取了有效区间后在第y行填充各区段
y=y+1
- AEL中删除
ymax=y
的边 - AEL中剩下的每条边做x更新,
x=x+dx
- (跳转)若AEL和NET有一个非空则转第4步循环
更详细的有序边表算法可以查看:http://blog.csdn.net/orbit/article/details/7368996
7.4 反走样
(1)走样与反走样策略
走样是指:用离散的像素来连续的图形时引起的失真,常见的走样有:
- 阶梯状边界(阶梯状斜线、阶梯状圆弧)
- 图形细节失真(图形比一个像素点小但被生成算法扩充成一个像素,细节展现削弱)
- 狭小图形直接遗失(图形比一个像素点小并且直接被生成算法“抛弃”,细节遗失)
反走样有一下几种策略:
- 提高分辨率
- 非加权区域采样
- 加权区域采样
提高分辨率可取但成本高,显示器首先要重新设计(提高1倍的分辨率需要4倍的像素点阵和帧缓存容量)其次图元生成、片元生成等算法开销均要增大
区域采样是把直线段看作具有一定宽度的狭长矩形,当直线段与某象素有交时,求出两者相交区域的面积,根据相交区域的面积,加权或不加权地确定该象素的亮度值,如下图展示:
非加权区域采样需要直接计算相交的三角形或梯形或其他多边形的区域的面积,运算量大(设计乘法运算),一种近似策略是分割像素点为更小的子像素,计算子像素落在直线内部的比例从而得到近似面积
非加权区域采样有一个问题就是像素中靠外面的子像素对近似面积的贡献和靠里面的子像素对近似面积的贡献相同,这样的得到的反走样图像不是很平顺,一般采用加权区域采样
我们可以仿照图像处理中滤波器的思想来设计反走样立方体滤波器以及圆锥体滤波器等等来确定权重
(2)直线反走样算法
直线反走样算法中应用最广泛的是Wu直线反走样算法,Wu反走样的算法和Bresenham画线算法思想很类似,同样是在比较
设背景色是
对于黑线白背景我们用RGBA颜色的不透明度alpha表示灰度颜色,且栅格距离为1,则有H、L点不透明度分别为
因此Wu反走样计算十分简单,直观上也能理解:线条离H点更近因此H点有更大的不透明度(
当0 < error < 0.5时,y(xk+1) > yk 且主点是(xk+1, yk)在下,dupper=1 - error,dlower=error,即主点不透明度1 - error,副点不透明度error。当-0.5 <= error <= 0时,y(xk+1) < yk 且主点是(xk+1, yk)在上,dupper=1 + error,dlower=-error,即主点不透明度1 + error,副点不透明度-error。
Bresenham画线算法稍加修改即可得到Wu直线反走样算法,略
(3)多边形填充区域边界反走样算法
多边形填充区域边界反走样算法和直线反走样算法类似,只是它只考虑多边形外部边界的反走样,内部均填充为多边形指定颜色
或者直接按直线反走样算法,然后做扫描填充时不考虑边界像素点,只按最大亮度/不透明度填充内部像素点即可
- 图形学复习4——光栅化(画线画圆扫描线反走样算法)
- 光栅图形学算法——裁剪算法
- 图形学笔记2——反走样 (antialiasing)
- 图形学(7)反走样技术
- 计算机图形学 学习笔记(一):光栅图形学算法:直线扫描算法(DDA,中点画线算法,Bresenham算法)
- 光栅图形学之直线段扫描算法(中点画线法)
- 光栅图形学算法(一)
- 计算机图形学 学习笔记(三):多边形的区域填充算法,反走样算法
- 计算机图形学基础(1)——画线算法
- 8位模式下画线(Bresenham算法,光栅化)
- 计算机图形学(三)_图元的属性_16_ 反走样_8_OpenGL反走样函数
- 光栅图形学——直线段的扫描转换算法
- [图形学]光栅直线算法
- 光栅图形学之直线段扫描算法(中点画线法之代码)
- 光栅图形学之直线段扫描算法(中点画线法之matlab代码)
- 光栅图形学之直线段扫描算法(中点画线法之java代码)
- 图形学学习笔记2——点阵图形光栅化
- 计算机图形学(三)_图元的属性_16_ 反走样_7_区域边界的反走样
- 创建简单的jquery插件
- STL之deque容器详解
- 进制转换
- 程序媛的必备素质
- 对元组Tuple特定项进行比较
- 图形学复习4——光栅化(画线画圆扫描线反走样算法)
- 括号匹配
- LeetCode——Min Stack
- 品读程序员晋级之路
- Qt中使用GridLayout如何设置一个按钮占两个位置
- 泛型的详细使用
- java之包含min函数的栈
- Java优先级队列PriotyQueue
- Execution failed for task ':dexDebug' Android Studio 编译失败