计算机图形学 第四章 光栅图形学

第四章 光栅图形学

计算机图形学 第四章 光栅图形学的相关内容,包括:直线段的扫描转换算法、圆弧的扫描转换算法、多边形区域填充、字符的生成、裁剪、反走样 等

Def 光栅显示器:一个像素矩阵(因此,要在光栅显示器上显示的图形逼近真实图形,需要用到下面的算法)

4.1 直线段的扫描转换算法

目标:需要在光栅显示器上画出过两点 P 0 ( x 0 ,   y 0 ) P_0(x_0,\,y_0) P0(x0,y0) P 1 ( x 1 ,   y 1 ) P_1(x_1,\,y_1) P1(x1,y1) 的直线 L L L 的最佳逼近

直线的三个算法轮流考,中点画线法和Bresenham算法一定要写出递推的优化算法

数值微分法(DDA)

算法

① 当 k ≤ 1 k\le1 k1 时,以 L L L 横坐标起点 x 0 x_0 x0 向横坐标终点以步长为 1 个像素步进,每次计算当前纵坐标的理论值 y i y_i yi 并做四舍五入:令 y = k x + b y=kx+b y=kx+b ,则令 ( x i ,   r o u n d ( k x i + b ) ) (x_i,\,round(kx_i+b)) (xi,round(kxi+b)) 作为当前像素的坐标

② 但是这样每次都要计算 k x i + b kx_i+b kxi+b ,比较麻烦,可以使用增量计算:
y i + 1 = k x i + 1 + b = k ( x i + 1 ) + b = y i + k y_{i+1}=kx_{i+1}+b=k(x_i+1)+b=y_i+k yi+1=kxi+1+b=k(xi+1)+b=yi+k
因此,只需令纵坐标每次递增 k k k ,再做四舍五入

代码

void DDALine(int x0, int y0, int x1, int y1, int color) {
    int x;
    float dx, dy, y, k;
    dx = x1 - x0;	dy = y1 - y0;
    k = dy / dx;	y = y0;
    for (x = x0; x < x1; ++x) {
        drawPixel(x, int(y + 0.5), color);
        y += k;
    }
}

注意:当 k ≥ 1 k\ge1 k1 时,以 L L L 纵坐标起点 y 0 y_0 y0 向纵坐标终点 y 1 y_1 y1 以步长为 1 个像素步进,每次计算当前横坐标的理论值 x i x_i xi 并做四舍五入

注意:四舍五入后取整不利于硬件实现

:用 DDA 方法扫描转换连接两点 P 0 ( 0 ,   0 ) P_0(0,\,0) P0(0,0) P 1 ( 5 ,   2 ) P_1(5,\,2) P1(5,2) 的直线段

xint(y+0.5)y+0.5
000
100.4+0.5
210.8+0.5
311.2+0.5
421.6+0.5
4_7

中点划线法

算法

① 当 k ≤ 1 k\le1 k1 时,设当前坐标为 ( x i ,   y i ) (x_i,\,y_i) (xi,yi) ,则下一个坐标可以是 ( x i + 1 ,   y i ) (x_i+1,\,y_i) (xi+1,yi) ( x i + 1 , y i + 1 ) (x_i+1,y_i+1) (xi+1,yi+1) ,怎么选择呢?令下一点的实际位置为 Q Q Q 点,下一位置的两个选择的中点为 M M M

  • Q Q Q M M M 上方,则选择 ( x i + 1 , y i + 1 ) (x_i+1,y_i+1) (xi+1,yi+1)
  • Q Q Q M M M 下方,则选择 ( x i + 1 , y i ) (x_i+1,y_i) (xi+1,yi)
4_8

② 如何判别 Q Q Q M M M 的位置关系?设过点 P 0 ( x 0 ,   y 0 ) P_0(x_0,\,y_0) P0(x0,y0) P 1 ( x 1 ,   y 1 ) P_1(x_1,\,y_1) P1(x1,y1) 的直线 L L L 方程为:
F ( x ,   y ) = a x + b y + c = 0 ; ( a = y 0 − y 1 ;   b = x 1 − x 0 ;   c = x 0 y 1 − x 1 y 0 ) F(x,\,y)=ax+by+c=0;\quad (a=y_0-y_1;\,b=x_1-x_0;\,c=x_0y_1-x_1y_0) F(x,y)=ax+by+c=0;(a=y0y1;b=x1x0;c=x0y1x1y0)
只需要将 M M M 带入 F ( x ,   y ) F(x,\,y) F(x,y) 即可得到 M M M 与直线的位置关系:
d = F ( M ) = F ( x i + 1 , y i + 0.5 ) = a ( x i + 1 ) + b ( y i + 0.5 ) + c d=F(M)=F(x_i+1,y_i+0.5)=a(x_i+1)+b(y_i+0.5)+c d=F(M)=F(xi+1,yi+0.5)=a(xi+1)+b(yi+0.5)+c

  • d < 0 d\lt 0 d<0 M M M Q Q Q 点(直线 L L L )下方,取 ( x i + 1 ,   y i + 1 ) (x_i+1,\,y_i+1) (xi+1,yi+1) 为下一个像素点
  • d > 0 d\gt 0 d>0 M M M Q Q Q 点(直线 L L L )上方,取 ( x i + 1 ,   y i ) (x_i+1,\,y_i) (xi+1,yi) 为下一个像素点
  • d = 0 d=0 d=0 M M M Q Q Q 点重合,约定取 ( x i + 1 ,   y i ) (x_i+1,\,y_i) (xi+1,yi) 为下一个像素点

③ 每次需要计算下一个 d d d,太麻烦了,同样也可以使用增量计算:

  • d d d 的初始值为:
    F ( x 0 + 1 ,   y 0 + 0.5 ) = ( a x 0 + b y 0 + c ) + a + 0.5 b = F ( x 0 ,   y 0 ) + a + 0.5 b = a + 0.5 b F(x_0+1,\,y_0+0.5)=(ax_0+by_0+c)+a+0.5b=F(x_0,\,y_0)+a+0.5b=a+0.5b F(x0+1,y0+0.5)=(ax0+by0+c)+a+0.5b=F(x0,y0)+a+0.5b=a+0.5b

  • d i > 0 d_i\gt 0 di>0 ,则 d i + 1 = F ( x i + 2 ,   y i + 0.5 ) = d i + a d_{i+1}=F(x_i+2,\,y_i+0.5)=d_i+a di+1=F(xi+2,yi+0.5)=di+a ,增量为 a a a

  • d i < 0 d_i\lt 0 di<0 ,则 d i + 1 = F ( x i + 2 ,   y i + 1.5 ) = d i + a + b d_{i+1}=F(x_i+2,\,y_i+1.5)=d_i+a+b di+1=F(xi+2,yi+1.5)=di+a+b ,增量为 a + b a+b a+b

④ 由于 a a a b b b c c c 都是整数,而加 0.5 b 0.5b 0.5b 又涉及浮点数运算了,所以可以使用 2 d 2d 2d 来代替 d d d 加速

代码

void MidPointLine(int x0, int y0, int x1, int y1, int color) {
    int a, b, d1, d2, d, x, y;
    a = y0 - y1;	b = x1 - x0;
    d = 2 * a + b;	d1 = 2 * a;		d2 = 2 * (a + b);
    x = x0;			y = y0;
    while (x < x1) {
        if (d < 0) {
            ++x; ++y; d += d2;
        } else {
            ++x; d += d1;
        }
        drawPixel(x, y, color);
    }
}

注意:当 k ≥ 1 k\ge1 k1 时,以 L L L 纵坐标起点 y 0 y_0 y0 向纵坐标终点 y 1 y_1 y1 以步长为 1 个像素步进, d d d 的初始值为 0.5 a + b 0.5a+b 0.5a+b d 1 = b d_1=b d1=b d 2 = a + b d_2=a+b d2=a+b

:用中点画线法扫描转换连接两点 P 0 ( 0 ,   0 ) P_0(0,\,0) P0(0,0) P 1 ( 5 ,   2 ) P_1(5,\,2) P1(5,2) 的直线段

// initialization
a = y0 - y1 = -2;	b = x1 - x0 = 5;
d0 = 2 * a + b = 1;
d1 = 2 * a = -4;	d2 = 2 * (a + b) = 6;
xyd
001
10-3
213
31-1
425
4_12

Bresenham 算法

算法

①(假设斜率 k ≤ 0 k\le0 k0 )过各行各列像素中心构造网格线;采用增量计算,计算直线与垂直网格线交点到下一水平网格线的距离 d d d (误差项),依据 d d d 的大小判断下一像素点的选取:

  • d > 0.5 d\gt 0.5 d>0.5 时,选取 ( x i + 1 ,   y i + 1 ) (x_i+1,\,y_i+1) (xi+1,yi+1)
  • d ≤ 0.5 d\le 0.5 d0.5 时,选取 ( x i + 1 ,   y i ) (x_i+1,\,y_i) (xi+1,yi)

误差项 d d d 的初值 d 0 = 0 d_0=0 d0=0 ;当横坐标增加 1 时,误差项 d d d 的增量为 k k k ;当 d d d 大于 1 时,将其减一,保证 d ∈ [ 0 ,   1 ] d\in[0,\,1] d[0,1]

4_14

② 为了方便计算,可令 e = d − 0.5 e=d-0.5 e=d0.5 e 0 = − 0.5 e_0=-0.5 e0=0.5 ,增量为 k k k

  • e > 0 e\gt 0 e>0 时,选取 ( x i + 1 ,   y i + 1 ) (x_i+1,\,y_i+1) (xi+1,yi+1)
  • e ≤ 0 e\le 0 e0 时,选取 ( x i + 1 ,   y i ) (x_i+1,\,y_i) (xi+1,yi)

(因为只用到误差项 e e e 的符号,所以可以改用整数 2 e 2e 2e 来避免除法)

代码

void BresenhamLine(int x0, int y0, int x1, int y1, int color) {
    int x, y, dx, dy;
    float k, e;
    dx = x1 - x0;	dy = y1 - y0;
    k = dy - dx;	e = -0.5;
    x = x0;			y = y0;
    for (int i = 0; i < dx; ++i) {
        drawPixel(x, y, color);
        ++x;	e += k;
        if (e > 0.5)	e -= 1;
        if (e >= 0)		++y;
    }
}

:用Bresenham画线法扫描转换连接两点 P 0 ( 0 ,   0 ) P_0(0,\,0) P0(0,0) P 1 ( 5 ,   2 ) P_1(5,\,2) P1(5,2) 的直线段

xyed
00-0.50
10-0.10.4
210.30.8
31-0.30.2
420.10.6
4_12

4.2 圆弧的扫描转换算法

目标

  • 需要在光栅显示器上画出以原点 O ( 0 ,   0 ) O(0,\,0) O(0,0) 为圆心、以 R R R R R R 为整数)为半径的圆
  • 需要在光栅显示器上画出以原点 O ( 0 ,   0 ) O(0,\,0) O(0,0) 为对称中心、以 a a a b b b 为椭圆参数的椭圆

特征:圆具有八对称性,因此只要扫描转换八分之一的圆弧4_18
#### 简单方程产生圆弧(DDA)

算法

利用函数方程直接离散计算:
x 2 + y 2 = R 2 x i + 1 = x i + 1 , x ∈ [ 0 ,   R 2 ] y i + 1 = r o u n d ( R 2 − x i + 1 2 ) x^2+y^2=R^2 \\ x_{i+1}=x_i+1,\quad x\in[0,\,\frac{R}{\sqrt{2}}] \\ y_{i+1}=round(\sqrt{R^2-x_{i+1}^2}) x2+y2=R2xi+1=xi+1,x[0,2 R]yi+1=round(R2xi+12 )

中点画圆法

算法

原方程: F ( x ,   y ) = x 2 + y 2 − R 2 F(x,\,y)=x^2+y^2-R^2 F(x,y)=x2+y2R2

构造判别式: d = F ( M ) = F ( x p + 1 ,   y p − 0.5 ) = ( x p + 1 ) 2 + ( y p + 1 ) 2 − R 2 d=F(M)=F(x_p+1,\,y_p-0.5)=(x_p+1)^2+(y_p+1)^2-R^2 d=F(M)=F(xp+1,yp0.5)=(xp+1)2+(yp+1)2R2

  • 初始值为 d 0 = F ( 1 ,   R − 0.5 ) = 1.25 − R d_0=F(1,\,R-0.5)=1.25-R d0=F(1,R0.5)=1.25R

  • d < 0 d\lt 0 d<0 ,取 P 1 P_1 P1 为下一像素点,再下一像素的判别式为:
    d = F ( x p + 2 ,   y p − 0.5 ) = d + 2 x p + 3 d=F(x_p+2,\,y_p-0.5)=d+2x_p+3 d=F(xp+2,yp0.5)=d+2xp+3

  • d ≥ 0 d\ge 0 d0 ,取 P 2 P_2 P2 为下一像素点,再下一像素的判别式为:
    d = F ( x p + 2 ,   y p − 1.5 ) = d + 2 ( x p − y p ) + 5 d=F(x_p+2,\,y_p-1.5)=d+2(x_p-y_p)+5 d=F(xp+2,yp1.5)=d+2(xpyp)+5

4_22

代码

void MidPointCircle(int r, int color) {
    int x = 0, y = r;
    float d = 1.25 - r;
    circlePoint(x, y, color);
    while (x <= y) {
        if (d < 0)	d += 2 * x + 3;
        else {
        	d += 2 * (x - y) + 5;
            --y;
        }
        ++x;
        circlePoints(x, y, color);
    }
}
Bresenham算法

考试不会考

原理和前面 Bresenham 算法画直线的原理差不多

椭圆

计算量比较大,考的概率较低

特征:椭圆只具有四对称性,并且四分之一椭圆弧上也要分成两段做,因为切线的斜率同时有大于1和小于1发部分,需要分开处理4_33
中点画椭圆法

算法

a > b a\gt b a>b 的椭圆为例:

方程式: F ( x ,   y ) = b 2 x 2 + a 2 y 2 − a 2 b 2 = 0 F(x,\,y)=b^2x^2+a^2y^2-a^2b^2=0 F(x,y)=b2x2+a2y2a2b2=0 (椭圆外的点, F ( x ,   y ) > 0 F(x,\,y)\gt0 F(x,y)>0

法向量: N ( x ,   y ) = 2 b 2 x i + 2 a 2 y j N(x,\,y)=2b^2xi+2a^2yj N(x,y)=2b2xi+2a2yj ,因此分界点为 ( 2 2 a ,   2 2 b ) (\frac{\sqrt2}{2}a,\,\frac{\sqrt2}{2}b) (22 a,22 b)

上半部分

判别式 d 1 = F ( x i + 1 ,   y i − 0.5 ) = b 2 ( x i + 1 ) 2 + a 2 ( y i − 0.5 ) 2 − a 2 b 2 d_1=F(x_i+1,\,y_i-0.5)=b^2(x_i+1)^2+a^2(y_i-0.5)^2-a^2b^2 d1=F(xi+1,yi0.5)=b2(xi+1)2+a2(yi0.5)2a2b2

  • 判别式初始值为 d 10 = F ( 1 ,   b − 0.5 ) = b 2 + a 2 ( − b + 0.25 ) d_{10}=F(1,\,b-0.5)=b^2+a^2(-b+0.25) d10=F(1,b0.5)=b2+a2(b+0.25)
  • d 1 ≤ 0 d_1\le0 d10 ,取 P u P_u Pu 为下一像素点,下一判别式的值为:

d 1 = F ( x i + 2 ,   y i − 0.5 ) = d 1 + b 2 ( 2 x i + 3 ) d_1=F(x_i+2,\,y_i-0.5)=d_1+b^2(2x_i+3) d1=F(xi+2,yi0.5)=d1+b2(2xi+3)

  • d 1 > 0 d_1\gt0 d1>0 ,取 P d P_d Pd 为下一像素点,下一判别式的值为:

d 1 = F ( x i + 2 ,   y 1 − 1.5 ) = d 1 + b 2 ( 2 x i + 3 ) + a 2 ( − 2 y i + 2 ) d_1=F(x_i+2,\,y_1-1.5)=d_1+b^2(2x_i+3)+a^2(-2y_i+2) d1=F(xi+2,y11.5)=d1+b2(2xi+3)+a2(2yi+2)

4_34

下半部分

判别式 d 2 = F ( x i + 0.5 ,   y i − 1 ) = b 2 ( x i + 0.5 ) 2 + a 2 ( y i − 1 ) 2 − a 2 b 2 d_2=F(x_i+0.5,\,y_i-1)=b^2(x_i+0.5)^2+a^2(y_i-1)^2-a^2b^2 d2=F(xi+0.5,yi1)=b2(xi+0.5)2+a2(yi1)2a2b2

  • 判别式初始值按照上半部分的最后一个点确定
  • d 2 > 0 d_2\gt0 d2>0 ,取 P l P_l Pl 为下一像素点,下一判别式的值为:

d 2 = F ( x i + 0.5 ,   y i − 2 ) = d 2 + a 2 ( − 2 y i + 3 ) d_2=F(x_i+0.5,\,y_i-2)=d_2+a^2(-2y_i+3) d2=F(xi+0.5,yi2)=d2+a2(2yi+3)

  • d 2 < 0 d_2\lt0 d2<0 ,取 P r P_r Pr 为下一像素点,下一判别式的值为:

d 2 = F ( x i + 1.5 ,   y i − 2 ) = d 2 + b 2 ( 2 x i + 2 ) + a 2 ( − 2 y i + 3 ) d_2=F(x_i+1.5,\,y_i-2)=d_2+b^2(2x_i+2)+a^2(-2y_i+3) d2=F(xi+1.5,yi2)=d2+b2(2xi+2)+a2(2yi+3)

4_37

(仔细观察一下,上半部分和下半部分的判别式增量是对称的)

4.3 多边形区域填充

两种表示方法:顶点表示和点阵表示

扫描线算法

考过好几次。给图形,写出活性边表的迭代过程,还要写出扫描线的四个转换步骤。可能也要写伪代码,或解释活性边表项的内容

步骤

  • 求交:计算扫描线与多边形各边的交点
  • 排序:把所有交点按照横坐标递增顺序排序
  • 配对:排序后的交点序列中相邻的两个进行配对
  • 着色:把相交区间内的像素置成多边形颜色,区间外的像素填充为背景色

算法:

4_46

活性边: 多边形中与当前扫描线相交的边

(图中每个点代表每个像素)

活性边表(AET): 链表,结点代表多边形的某条边与当前扫描线的脚店情况,按照横坐标递增存放,保存的信息有:

  • x x x :当前扫描线与边的交点
  • Δ x \Delta x Δx :从当前扫描线到下一条扫描线之间的交点的 x x x 增量
  • y m a x y_{max} ymax :该边所交的最高扫描线号(扫描线号就是纵坐标)

(活性边表只有一个,但是会随着遍历扫描线的过程而动态变化,表里每一项代表多边形的一条边)

(活性边表代表当前被遍历到的扫描线与多边形各边的交点情况)

如扫描线 6 6 6 7 7 7 的活性边表(AET):

4_48_1

新边表(NET): 每一条扫描线都建立一个新边表,用于存放 第一次出现交点是与当前扫描线相交的多边形的边(若某边的较低端点为 y m i n y_{min} ymin ,则该边存放在扫描线 y m i n y_{min} ymin 的新边表中)

4_52

交点取舍: 扫描线与多边形的顶点相交时,需要做取舍

方法:检查顶点的两条边的另外两个端点 y y y 值,按这两个 y y y 值中大于交点 y y y 值的个数是 0、1、2 来决定取零个、一个还是两个交点

  • 共享顶点的两条边落在扫描线两侧,则只取一个交点,如 P 1 P_1 P1
  • 共享顶点的两条边落在扫描线同一侧:
    • 若交点是局部最高点,即 y i > y i − 1 y_i\gt y_{i-1} yi>yi1 y i > y i + 1 y_i\gt y_{i+1} yi>yi+1 ,则取零个交点,如 P 6 P_6 P6
    • 若交点是局部最高点,即 y i < y i − 1 y_i\lt y_{i-1} yi<yi1 y i < y i + 1 y_i\lt y_{i+1} yi<yi+1 ,则取两个交点,如 P 2 P_2 P2

增量法: 下一扫描线与多边形某条边的交点横坐标不需要重新计算,只需要 x i + Δ x x_i+\Delta x xi+Δx

  • 若该边的方程为 a x + b y + c = 0 ax+by+c=0 ax+by+c=0 ,则 Δ x = − b a \Delta x=-\frac{b}{a} Δx=ab

算法步骤:

void polyfill(多边形 polygon, int color) {
    for (i : 扫描线) {
        初始化新边表头指针NET[i];
        将 ymin = i 的边放入新边表 NET[i];
    }
    创建空的AET表;
    for (i : 扫描线) {
        插入排序法: 将NET[i]中的所有边结点插入AET;	//保证按横坐标递增顺序排列
        // 此时AET表中除了新边,还有ymax > i 的边结点
        遍历AET表,删除 ymax = i的结点;
       	for((x, y) : 左闭右开的配对交点区间) {
            drawpixel(x, y, color);
            将ymax > i的结点的x值递增dx;
        }
    }
}

举例: 沿用上面的多边形,活性边表 AET 的迭代过程:(加粗代表新增加的边)

i = 0AET = null
i = 1AET = P1P2 -> P2P3
i = 2AET = P6P1 -> P1P2 -> P2P3;
del: P1P2;
left: P6P1 -> P2P3
i = 3AET = P6P1 -> P2P3 -> P3P4;
del: P2P3;
left: P6P1 -> P3P4
i = 4AET = P6P1 -> P3P4
i = 5AET = P6P1 -> P5P6 -> P4P5 -> P3P4
i = 6AET = P6P1 -> P5P6 -> P4P5 -> P3P4
i = 7AET = P6P1 -> P5P6 -> P4P5 -> P3P4
del: P6P1, P5P6
left: P4P5 -> P3P4
i = 8AET = P4P5 -> P3P4
del: P4P5, P3P4
left: null

边界标志算法

在帧缓冲器中对边界经过的像素打上标志,然后对每行循环每个像素填充颜色

简单暴力,计算量大,适合硬件实现、对多边形形状没有要求

(记录一个 flag 用于指示是否在多边形内部,每次遇到多边形的边界就反转 flag)

种子填充算法

有点像是 BFS,从区域内某一点(种子点)出发,通过四个(4向连通区域)或八个(8向连通区域)的移动组合来到达区域内的任意像素

因此要求填充区域是联通的,且初始状态下填充区域内部颜色统一,填充区域边界颜色统一

原理简单,但递归费时费内存,效率不高

种子填充的扫描线算法

从种子点向左右填充直到边界,再从两端向上下找未填充像素作为种子压栈,重复上述过程

每个区间只需入栈一次

4.4 字符的生成

字符编码

  • 美国信息交换标准代码集ASCII码

  • 汉字编码国家标准字符集GB2312-80

    • 字符最高位 0表示ASCII码,1表示汉字编码

字符表达和生成

点阵式字符

硬件操作快,可以加粗、旋转90度、斜体、比例缩放等,任意角度旋转比较困难

  • 加粗:每个像素 ( x i ,   y ) (x_i,\,y) (xi,y) 右边相邻的像素 ( x i + 1 ,   y ) (x_{i+1},\,y) (xi+1,y) 也被写入
  • 旋转90°:每个像素先交换 x x x y y y 坐标,再改变 y y y 的符号(先沿对角线翻转,在上下翻转)
  • 斜体:逐行拷贝像素,每隔 n n n 行左移一单元
4_68
压缩技术
  • 黑白段压缩法:简单,还原快,不失真,压缩较差,使用不方便,用于低级文字处理系统

  • 部件压缩法:压缩比大,字型质量不能保证

  • 轮廓字型法:压缩比大,能保证质量,符合工业标准化方法

    • 采用直线或二/三次贝塞尔曲线的集合来描述一个字符的轮廓线,加上指示横宽、竖宽、基点、基线等的控制信息(即构成压缩数据),采用适当的区域填充算法;
    • TrueType轮廓字型技术由Apple和Microsoft联合开发;
    • 占领主要电子印刷市场的是北大方正激光照排系统;
矢量式字符
  • 用点坐标的序列表示各个笔划,调用矢量式字符的过程相当于输出一个 polyline
  • 存储空间小、美观、变换方便
4_70
方向编码式字符
  • 用若干种方向编码(如8方向编码)来表达一个字符,偶数方向固定长度为 1 ,奇数方向固定长度为 2 \sqrt2 2

    • 字母“B”的方向矢量构成:{0000 123 444 000 123 4444 0 666666}
  • 容易填入帧缓存寄存器显示,所占空间小,易放大缩小或45度旋转,但难以进行任意角度的旋转

![请添加图片描述]()
_14_72_2

4.5 裁剪

三个算法不会同时考

一般是先裁剪再扫描转换

直线段裁剪

Cohen-Sutherland裁剪算法

考了无数次 [\doge]

延长窗口四条边,将平面分为九宫格;每个格用四位编码 C t C b C r C l C_tC_bC_rC_l CtCbCrCl

C_t = y > y_max ? 1 : 0;	// top
C_b = y > y_max ? 0 : 1;	// bottom
C_r = x > x_max ? 1 : 0;	// right
C_l = x > x_max ? 0 : 1;	// left
4_80

裁剪一条线段 P 1 P 2 P_1P_2 P1P2 时,先求出所在区号 c o d e 1 code_1 code1 c o d e 2 code_2 code2

  • c o d e 1 = 0 code_1=0 code1=0 c o d e 2 = 0 code_2=0 code2=0 ,则说明线段 P 1 P 2 P_1P_2 P1P2 完全在窗口内,不需要裁剪,直接保留
  • c o d e 1   &   c o d e 2 ≠ 0 code_1\,\&\,code_2\not=0 code1&code2=0 ,则说明线段 P 1 P 2 P_1P_2 P1P2 完全在窗口外,不需要裁剪,直接舍弃
  • 否则,求出线段与窗口某边的交点,在交点处把线段一分为二,其中必有一段完全在窗口外,舍弃;再对另一段重复上述处理

注意:第三种情况求交点时,可以取任意一个编码不为 0 的端点,然后按顺序遍历编码的每一位,取编码不为 0 的那一位对应的窗口边界,求边与此窗口边界的交点

中点分割方法(求交点的算法)

Cohen-Sutherland 裁剪算法在的第三种情况需要求出 P 0 P 1 P_0P_1 P0P1 与窗口的一个交点,可以用中点分割算法进行求交点的优化:

  • 首先求出 P 0 P 1 P_0P_1 P0P1 的中点 P m P_m Pm 及其编码
    • P 0 P m P_0P_m P0Pm 不是显然不可见的:(此时已知 P 0 P 1 P_0P_1 P0P1 在窗口中有可见部分)则 P 0 P m P_0P_m P0Pm 中必与窗口有一个交点,所以用 P 0 P m P_0P_m P0Pm 代替 P 0 P 1 P_0P_1 P0P1
    • P 0 P m P_0P_m P0Pm 是显然不可见的:则用 P m P 1 P_mP_1 PmP1 代替 P 0 P 1 P_0P_1 P0P1
  • 重复上述过程,直到 P 0 P 1 P_0P_1 P0P1 的长度小于给定的常数为止,此时 P m P_m Pm 收敛于交点
4_85

(Cohen-Sutherland 裁剪算法在的第三种情况需要求出 P 0 P 1 P_0P_1 P0P1 与窗口的一个交点,而且只需要求出一个交点就够了)

注意:由于该算法的主要计算过程只用到加法和除 2 运算,所以特别适合硬件实现和并行计算

梁友栋-Barskey算法

设线段 P 0 P 1 P_0P_1 P0P1 端点坐标分别为 ( X 1 ,   Y 1 ) (X_1,\,Y_1) (X1,Y1) ( X 2 ,   Y 2 ) (X_2,\,Y_2) (X2,Y2) ;以 ( X 1 ,   Y 1 ) (X_1,\,Y_1) (X1,Y1) 为起点,参数 0 ≤ μ ≤ 1 0\le\mu\le1 0μ1 Δ X = X 2 − X 1 \Delta X=X_2-X_1 ΔX=X2X1 Δ Y = Y 1 − Y 2 \Delta Y=Y_1-Y_2 ΔY=Y1Y2 ,则裁剪的保留条件为:
X L ≤ X 1 + μ Δ X ≤ X R Y B ≤ Y 1 + μ Δ Y ≤ Y T X_L\le X_1+\mu\Delta X\le X_R \\ Y_B\le Y_1+\mu\Delta Y\le Y_T \\ XLX1+μΔXXRYBY1+μΔYYT
可以表示为: μ p k ≤ q k \mu p_k\le q_k μpkqk p k p_k pk q k q_k qk 定义为:
p 1 = − Δ X q 1 = X 1 − X L p 2 = Δ X q 2 = X R − X 1 p 3 = − Δ Y q 3 = Y 1 − Y B p 4 = Δ Y q 4 = Y T − Y 1 p_1=-\Delta X\quad q_1=X_1-X_L\quad\quad p_2=\Delta X\quad q_2=X_R-X_1\\ p_3=-\Delta Y\quad q_3=Y_1-Y_B\quad\quad p_4=\Delta Y\quad q_4=Y_T-Y_1\\ p1=ΔXq1=X1XLp2=ΔXq2=XRX1p3=ΔYq3=Y1YBp4=ΔYq4=YTY1
位置关系:

  • p k = 0 p_k=0 pk=0 ,则平行于裁剪边界之一:
    • 若同时有 q k < 0 q_k\lt 0 qk<0 ,则线段完全在边界外,舍弃该线段
    • 若此时 q k ≥ 0 q_k \ge 0 qk0 ,则线段平行于裁剪边界且在窗口内
  • p k ≠ 0 p_k\not=0 pk=0 ,则可以计算出线段与边界 k k k 的延长线的交点的 u u u 值: u = q k p k u=\frac{q_k}{p_k} u=pkqk
    • p k < 0 p_k\lt 0 pk<0 ,线段从裁剪边界及延长线的外部延伸到内部,对于不同的边界,有正负不同的 Δ X \Delta X ΔX Δ Y \Delta Y ΔY
    • p k > 0 p_k\gt 0 pk>0 ,线段从裁剪边界及延长线的内部延伸到外部,对于不同的边界,有正负不同的 Δ X \Delta X ΔX Δ Y \Delta Y ΔY
4_88

**参数计算:**对于每条直线,计算参数 u 1 u_1 u1 u 2 u_2 u2 ,它们定义了裁剪矩形的线段部分:

  • u 1 u_1 u1 代表线段相对于矩形边界从外到内( p k < 0 p_k\lt 0 pk<0 ), u 1 = m a x ( 0 , u_1=max(0, u1=max(0, 其他从外到内的 u ) u) u)
  • u 2 u_2 u2 代表线段相对于矩形边界从内到外( p k > 0 p_k\gt 0 pk>0 ), u 2 = m i n ( 1 , u_2=min(1, u2=min(1, 其他从内到外的 u ) u) u)

交点判断:

  • u 1 > u 2 u_1 \gt u_2 u1>u2 ,则线段完全落在裁剪窗口之外,舍弃;

  • 否则,裁剪线段可以由参数 u 1 u_1 u1 u 2 u_2 u2 计算出来:

( X 1 + u 1 Δ X ,   Y 1 + u 1 Δ Y ) → ( X 1 + u 2 Δ X ,   Y 1 + u 2 Δ Y ) (X_1+u_1\Delta X,\,Y_1+u_1\Delta Y)\to(X_1+u_2\Delta X,\,Y_1+u_2\Delta Y) (X1+u1ΔX,Y1+u1ΔY)(X1+u2ΔX,Y1+u2ΔY)

(实际算法就是对裁剪矩形上下左右边界都进行判断,只要有一个出现舍弃就舍弃;否则算出参数 u 1 u_1 u1 u 2 u_2 u2 ,进而算出裁剪线段)

注意: 使用参数化的算法,计算更快

多边形裁剪

Sutherland-Hodgeman算法

考虑窗口的每一条边与线段的关系,设线段 S P SP SP 为有向线段, S → P S\to P SP

  • S S S P P P 均可见,则输出终点 P P P
  • S S S P P P 均不可见,则不输出
  • S S S 可见, P P P 不可见,则输出 S P SP SP 与裁剪线的交点 I I I
  • S S S 不可见, P P P 可见,则输出 S P SP SP 与裁剪线的交点 I I I 与终点 P P P

记忆:输出 = 交点(如果存在)+ 终点(如果可见)

(即若存在交点,则需输出交点;若终点可见,则需输出终点)

4_92

遍历窗口四条裁剪线;对于某一条裁剪线,遍历多边形每一条边,将输出的顶点按顺序连接:

4_93

字符串裁剪

经常考简答题,画出四种裁剪精度下裁剪结果的示意图

待裁剪字符串串精度裁剪字符精度裁剪像素精度裁剪
字符串精度裁剪
  • 求出字符串外包盒(box),与窗口裁剪边进行比较:当字符串外包盒完全再窗口内时显示,否则不显示;
字符精度裁剪
  • 先以字符串 box 判断字符串是全删、全留或部分留
  • 对于部分留的字符串,逐个测量字符的 box 与窗口裁剪边关系而决定字符保留或删除
像素精度裁剪
  • 先以字符串 box 判断字符串是全删、全留或部分留
  • 对于部分留的字符串,逐个测量字符的 box 与窗口裁剪边关系而决定字符全删、全留或部分留
  • 对部分留的字符的每一笔划,用直线裁剪法对窗边裁剪

4.6 反走样

Def 走样:使用离散的光栅显示器显示连续的图形信号,引起失真现象(包括:阶梯状的边界、图形细节失真、狭小图形遗失)

区域采样可能考简答题

提高分辨率

依靠硬件设施,存储器代价和扫描转换时间增加,只能减轻而不能消除锯齿问题

区域采样

根据直线段与像素相交区域面积大小确定该像素的亮度值

将一个像素的面积看作1,则亮度 = = = 最大亮度 × \times × 相交面积

面积可以采用离散估计的方法,即将像素均分为 n n n 个子像素,计算中心点落在直线段内的子像素个数 k k k ,则相交区域面积近似值为 k n \frac{k}{n} nk

加权区域采样

相交区域对像素亮度的贡献依赖于该区域与像素中心的距离

同样可以使用离散的计算方法,即将像素均分为 n n n 个子像素,选定一个加权表,则该像素亮度 = = = 最大亮度 ×   ∑ i = 1 n p i w i \times\,\sum\limits_{i=1}^n p_iw_i ×i=1npiwi ,其中 w i w_i wi 为子像素权重,当子像素中心落在直线段内时, p i = 1 p_i=1 pi=1 ,否则为 0 0 0

:将屏幕划分为 n = 3 × 3 n=3\times3 n=3×3 个子象素,加权表可以取作:
[ w 1 w 2 w 3 w 4 w 5 w 6 w 7 w 8 w 9 ] = 1 16 [ 1 2 1 2 4 2 1 2 1 ] \left[ \begin{matrix} w_1 & w_2 & w_3 \\ w_4 & w_5 & w_6 \\ w_7 & w_8 & w_9 \\ \end{matrix} \right] =\frac{1}{16} \left[ \begin{matrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \\ \end{matrix} \right] w1w4w7w2w5w8w3w6w9 =161 121242121
8

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Air浩瀚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值