OpenGL.光栅化就是扫描转换

  • 进一步说,对于一些几何,我们还只是有数学的抽象表达,但实际的图像确实离散的像素点
  • 所以光栅化其实就是把数学表达转化为像素表达,得到的数据我们称为“片段”,用于进一步的片段处理

线段的扫描转换

  • 对于线段来说,其数学描述只是两个顶点(x1,y1)和(x2,y2)
  • 这两个顶点自然可以很容易的对应两个像素点,但这两个顶点的连线也应该对应着若干个像素点才行
  • 这里默认像素位置均为整数,尽管也有不是整数的情况

DDA算法

  • 一个很显然的思路,就是求出线段的斜率,然后递增x,y每次加上斜率并取整
  • 设x1 < x2
DDA(int x1,int y1,int x2,int y2){
    int x = 0;
    double y = y1, m = (y2-y1)/(double)(x2-x1);
    for(x=x1;x<=x2;++x){
        y += m;
        output(x,(int)y);
    }
}
  • 但是这样写仅对于 0<=|m|<=1 的情况才能得到连续的像素位置
  • 对于 |m|>1 的情况就 递增y 就行了呗

Bresenham算法

  • DDA涉及了大量的浮点运算,这样很耗时间的
  • 如果能有完全是整数运算的就好了,那肯定是快很多啊
  • 分析一下线段的转换,只考虑 0 < m < 1的情况,其余的对称
    OpenGL.光栅化就是扫描转换_第1张图片

  • 对于已经确定的点p,其下一个要选择的点

    • 如果是绿色线,则更靠近m,则选择m
    • 如果是红色线,则更靠近n,则选择n
  • 就是说,下一个选择只有两个选择,如何选择就看线段更靠近那个点
  • 怎样来衡量“靠近”呢?
    • 取 Δx = x2 - x1 , Δy = y2 - y1
    • 设置决策变量d
    • OpenGL.光栅化就是扫描转换_第2张图片
      d=Δx(ab)
    • a为交点与候选点n(更高的点)的距离
    • b为交点与候选点m(同样高度的点)的距离
    • Δx的存在是为了使结果为整数,之后会看到作用
    • 若d>0,则更靠近m,选m,否则选n
  • 对于已有的点p,其决策变量为d1,考虑下面两种情况
    • 情况1
    • OpenGL.光栅化就是扫描转换_第3张图片
    • 对于点p的决策变量d1是大于0的,那么下一次的决策变量
      • a2 = a1 - m
      • b2 = b1 + m
      • d2 = Δx * (a2 - b2)
      • d2 = Δx * (a1 - b1 - 2 * m)
      • d2 = d1 - 2 * Δy
    • 情况2
    • OpenGL.光栅化就是扫描转换_第4张图片
    • 否则,那么下一次的决策变量
      • a2 = a1 + 1 - m
      • b2 = b1 + m - 1
      • d2 = Δx * (a2 - b2) = Δx * (a1 - b1 + 2 - 2 * m)
      • d2 = Δx * (a1 - b1 + 2 * (1 - m))
      • d2 = Δx * (a1 - b1)+2 * Δx * (1-m)
      • d2 = d1 + 2 * (Δx - Δy)
    • PS:如果d1是整数,那么d2就是整数
  • 尽管还有如下两种情况,但实际上并无影响(应该是没影响的吧)

    • OpenGL.光栅化就是扫描转换_第5张图片
    • -
    • OpenGL.光栅化就是扫描转换_第6张图片
  • 那只要我们确定了决策变量的初值,之后的决策变量就都可以由整数运算的递推公式得出

  • 那么对于初始点(x1,y1)
    • a = 1
    • b = 0
    • d = Δx > 0 为整数
  • 接下来的运算就都是整数了 =、=

多边形的扫描转换

  • 牵扯到多边形其实会麻烦很多

内外点检测

  • 针对多边形,需要判断某个点是不是在其内部
  • 对于凸多边形很容易,如果一个点都在其边的同一侧,那么就在其内部
    OpenGL.光栅化就是扫描转换_第7张图片

  • 那么对于任意多边形呢?

  • 奇偶检测法
    • 从该点向任意一个方向引射线
    • 如果与多边形的交点个数为偶数个(包括0),则该点为外部点
    • 如果为奇数个,则为内部点
    • 对于计数
      • 与边相交,计数增加一
      • 如果与顶点相交
        • 形成顶点的线如果在射线两侧,则增加一
        • 如果在射线同侧,则增加2或不增加
  • 环绕数法
    • 同样是从该点向任意一个方向引射线
    • 选多变形的一个顶点,沿着边来一次走完整个多边形的外框
      • 如果是从左到右经过射线,则环绕数减一
      • 如果是从右到左经过射线,则环绕数加一
    • 最后环绕数不为零,则该点为内部点
    • 一个很神奇的算法

填充算法####

  • 种子填充法
    • 先根据扫描线算法把多边形的边界转换成像素位置信息
    • 再选取多边形内部的一个点,进行递归遍历
flood_fill(int x,int y){
    if(read_pixel(x,y)==NULL){
        get(x,y);
        flood_fill(x-1,y);
        flood_fill(x+1,y);
        flood_fill(x,y-1);
        flood_fill(x,y+1);
    }
}
  • 扫描线法
    • 从下往上建立新边表 NET
    • 新边表在对应y值出现新边的一个顶点的时候
      • 记录这个顶点的 x 值
      • 记录在这条边上,如果y增加1,则x的变化Δx
      • 记录这条边终点的y值
      • 每条记录之间用链表连接
    • 对于活动边表 AET
      • 从最小的y开始,即从下往上的扫描线,y递增
      • 看对应的y值是否有新边(根据NET),有则加入到AET中
      • 看当前AET中是否有终点y值等于当前y值的记录,有则删除
      • 对AET中所有的记录进行排序,从小到大,先按照x值排序,再按照Δx值排序
      • 从前往后两两组合,填充当前两条记录的x值之间的所有点

你可能感兴趣的:(OpenGL,OpenGL,扫描转换,光栅化,新边表,Bresenham)