标签:图形学 光栅化 bresenham 有序边表算法 wu直线反走样算法
前几章介绍了几何处理和裁剪变换,接下来的步骤就是光栅化
光栅化是将形式表示的几何图元转换为阵列表示的数据片元的过程,片元中每一个像素对应帧缓冲区中的每一个像素
设直线表达式为
DAA是基于微分运算的线段生成算法,其主要计算式便是
为了有效的避免了斜率为正无穷时
下面是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算法避免的迭代时的乘积运算因此比直接用直线表达式求点坐标效率更高,但是,每一步骤中的浮点操作和取整运算开销仍然较大(体系结构告诉我们整数运算和浮点运算效率可以相差几十倍)
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对称变换得到正确位置即可。
中点画圆算法和Bresenham画线算法一样,通过引入决策参数来消除浮点和乘法运算
把圆划分成8个
从
中点画圆的思想就是:判断如果
取决策参数
同样有迭代思想求
注意上述描述的是以原点为中心点的画圆算法,若中心点需要在
中点画椭圆算法和中点画圆算法基本一样,只是需要在一象限分两个区域1、2讨论,令
区域1中:
区域2中:
注意区域2中小于0选
多边形扫描线填充的方法是沿多条平行于x轴的直线
扫面线算法利用以下相邻像素连贯性,提高扫描效率:
扫描线算法的一般步骤为:
有序边表算法就是一种经典的扫描线算法
定义活性边是与当前扫描线相交的边,边结构如下:
typedef struct {
int ymax; //边最大y值,即与相交的最大扫面线号
float x; //当前扫描线与边相交的x坐标
float dx; //边斜率的倒数
Edge *next; //指向下一条边的指针
} Edge;
那么活性边表AEL就是活性边按x递增的顺序构成的链表,和活动边表相关的是当前的扫描线,随着扫描线的移动按照如下的规则维护AEL:
x=x+dx
上述新边的定义是:若某条(非水平)边的下端点是y,那么称之为扫面线y的新边;很容易发现要构造AEL就先要构造定义新边表NET,NEL无序排列因此构造NET枚举一遍边即可
下面给出有序边表算法的算法描述:
y=y+1
ymax=y
的边x=x+dx
更详细的有序边表算法可以查看:http://blog.csdn.net/orbit/article/details/7368996
走样是指:用离散的像素来连续的图形时引起的失真,常见的走样有:
反走样有一下几种策略:
提高分辨率可取但成本高,显示器首先要重新设计(提高1倍的分辨率需要4倍的像素点阵和帧缓存容量)其次图元生成、片元生成等算法开销均要增大
区域采样是把直线段看作具有一定宽度的狭长矩形,当直线段与某象素有交时,求出两者相交区域的面积,根据相交区域的面积,加权或不加权地确定该象素的亮度值,如下图展示:
非加权区域采样需要直接计算相交的三角形或梯形或其他多边形的区域的面积,运算量大(设计乘法运算),一种近似策略是分割像素点为更小的子像素,计算子像素落在直线内部的比例从而得到近似面积
非加权区域采样有一个问题就是像素中靠外面的子像素对近似面积的贡献和靠里面的子像素对近似面积的贡献相同,这样的得到的反走样图像不是很平顺,一般采用加权区域采样
我们可以仿照图像处理中滤波器的思想来设计反走样立方体滤波器以及圆锥体滤波器等等来确定权重
直线反走样算法中应用最广泛的是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直线反走样算法,略
多边形填充区域边界反走样算法和直线反走样算法类似,只是它只考虑多边形外部边界的反走样,内部均填充为多边形指定颜色
或者直接按直线反走样算法,然后做扫描填充时不考虑边界像素点,只按最大亮度/不透明度填充内部像素点即可
标签:图形学 光栅化 bresenham 有序边表算法 wu直线反走样算法
原文地址:http://blog.csdn.net/u014030117/article/details/46523871