第二章 二维图元的生成
-
掌握扫描转换直线段的DDA算法、中点算法
-
及中点算法在哪些方面对DDA算法做了改进
-
-
掌握扫描转换直线段的
Bresenham算法
-
掌握圆弧的八对称性, 扫描转换圆弧的中点算法
-
掌握生成圆弧的多边形迫近法
-
了解正负法,掌握怎样利用正负法生成圆弧
-
掌握扫描转换椭圆弧的中点算法
-
了解线画图元的属性(线型、线宽)控制方法
图形的扫描转换 || 光栅化 || 图元的生成
-
确定最佳逼近图形的象素集合,并用指定属性写象素的过程,即指完成从图元的参数表示形式转换成点阵表示形式的过程称为图形的扫描转换或光栅化。
简单二维图形的显示流程
图元 -> 二维裁剪 -> 扫描转换 -> 显示
裁剪的顺序
-
先裁剪再扫描转换
-
常用,计算量小
-
-
先扫描转换再裁剪
-
算法简单,对有快速测试方法或硬件支持情形有利
-
直线段的扫描转换
基于假设
直线段的宽度为
1
, 大于1
的由线宽控制算法处理直线段的斜率在
[-1, 1]
, 绝对值大于1
的通过修改算法完成
-
直线段由2个点坐标确定 P0 (x0, y0), P1(x1, y1)
直接求交
-
划分区间[x0, x1], 其中
-
计算纵坐标
-
纵坐标取整
复杂度
乘法 + 加法 + 取整
DDA算法
由于
省略了乘法计算
void LineDDA(int x0, int y0, int x1,int y1, int color)
/* 假定x0<x1,-1<=k<=1 */
{
int x;
float dx, dy, y, k;
dx = x1 - x0;
dy = y1 - y0;
k = dy / dx; // 求斜率
y = y0;
for(x = x0; x <= x1; x++) // x从x1到xn
{
Putpixel(x, int(y + 0.5), color);
y += k; //y每次增加一个斜率值
}
}
中点算法
-
消除DDA中的浮点运算
-
使用隐式方程
-
F(x,y)=ax+by+c , 式中
a = y0 - y1,
b = x1 - x0
c = x0y1 - x1y0
由于下一个像素点只有两个选项 p1(x+1,y)
或者 p2(x+1, y+1)
取 M = (x+1, y+0.5)
为 p1
与 p2
中点, Q
为直线与 x = x+1
的交点, 若
-
M
在Q
下方, 则di = F(M) < 0
, 取p2
为下一个像素, 则d(i+1) = F(x+1+1, y+0.5+1)
-
M
在Q
上方, 则di = F(M) > 0
, 取p1
为下一个像素, 则d(i+1) = F(x+1+1, y+0.5)
-
若
M
在Q
上, 取p1
或p2
皆可, 约定取p1
由于 d
的增量是可以根据 d
的正负性预测的, 因此有, 因为(x0, y0)
在直线上, 有 d0=a+0.5*b
-
d >= 0
,d
的增量为d1 = a
-
d < 0
,d
的增量为d2 = a+b
为了消除浮点数计算, 全部改写成 2 倍形式, 即
d0 = a + a + b
d1 = a + a
d2 = a + a + b + b
Midpointline(int x0, int y0, int x1, int y1, int color)
{
int a, b, d, d1, d2, x, y;
a = y0 - y1;
b = x1 - x0;
d = a + a + b; // 计算 a, b, d0
d1 = a + a;
d2 = a + a + b + b; // 计算可能的增量
x = x0;
y = y0;
PutPixel(x, y, color); // 绘制初始像素点
while (x < x1)
{
if (d < 0)
{
x++;
y++;
d += d2; // 如果 d 为负,取右上角点 (y 加 1)
}
else
{
x++;
d += d1; // 如果 d 为正,取右边点
}
PutPixel(x, y, color);
}
}
Bresenham算法
Bresenham算法
是计算机图形学领域使用最广泛的直线扫描转换算法
-
该方法类似于中点法,由误差项符号决定下一个象素取右边点还是右上点
算法原理
过各行各列象素中心构造一组虚拟网格线。按直线从起点到终点的顺序计算直线与各垂直网格线的交点,然后确定该列象素中与此交点最近的象素
该算法的巧妙之处在于采用增量计算,使得对于每一列,只要检查一个误差项的符号,就可以确定该列的所求象素。
算法步骤
令
d
为误差项, 由于直线起点在像素中心,d
的初值为 0
x
下标每增加 1,d
的值相应递增直线的斜率值k
,即d=d+k
一旦
d >= 1
,就把它减去 1,这样保证d
在 0、1 之间当
d >= 0.5
时,取(xi+1,yi+1)
当
d < 0.5
时,取(xi+1,yi)
为方便计算,令
e=d-0.5
,e
的初值为-0.5
,增量为k
当
e >= 0
时,取(xi+1,yi+1)
当
e < 0
时,取(xi+1,yi)
上述算法在计算直线斜率与误差项时用到小数与除法,可以改用整数以避免除法
改进后的算法称为Bresenham算法
由于算法中只用到误差项的符号,而且2dx为正,因此可作如下替换
e = e * 2dx
e0=-0.5 * (2dx) = -dx
增量为:k * (2dx) = 2dy
e > 0
时,减量为:1 * (2dx) = 2dx
于是有如下算法
void Bresenhamline(int x0, int y0, int x1, int y1, int color)
{
int x, y, dx, dy, e;
dx = x1 - x0;
dy = y1 - y0;
e = -dx; // e 的初值为 -dx
x = x0;
y = y0;
for (int i = 0; i <= dx; i++)
{
Putpixel(x, y, color);
x = x + 1;
e = e + 2 * dy; // e 每次的增量为 2 * dy
if (e >= 0)
{
y++;
e = e - 2 * dx; // 若 e >= 0,则 y 增 1,e - 2 * dx
}
}
}