zoukankan      html  css  js  c++  java
  • 二维图元生成:直线生成算法

    图形是怎么生成的?

    视频控制器通过访问帧缓存来刷新屏幕

    帧缓存中的保存的是点阵数据,而我们将要讨论的是

    如何将图形的几何参数来得到点阵数据,本文主要介绍最简单的直线生成算法

    通过两个点(p_0),(p_1),如何转化成帧缓存中的点阵数据

    图元的生成

    • 概念:图元的参数表示形式到点阵表示形式的转换

    • 参数表示形式不同种类图形的性质决定

    • 点阵表示形式是光栅显示系统刷新时所需的表示形式。

    在光栅显示器的荧光屏上生成一个对象,实质上是往帧暂存寄存器的相应单元中填入数据

    • 像素操作函数:最基本的绘图函数,等同于写读帧缓存

      • 写像素:SetPixel ( int x, int y, int color )
      • 读像素: int GetPixel ( int x, int y)
    • 单缓存与双缓存:

    直线生成的基本思路

    画一条从(p_0)到$p_1 $的直线,实质上是一个发现最佳逼近直线的像素序列、并填入色彩数据的过程,这个过程也称为直线光栅化。

    • 概念:求与直线段充分接近的像素集,并以此像素集替代原连续直线段在屏幕上显示。

    说到直线,肯定想到最基础的直线方程(y=kx+b)。如果用这种方法,会用到乘法,加法,还有取整。最简单的找到充分接近的像素的方法就是取整。

    这在计算机中效率是比较低的。

    我们做一次改进:变乘法为加法

    [y_{i+1}=kx_{i+1}+b ]

    [=k(x_i+1)+b ]

    [=kx_i+b+k ]

    又因为(y_i=kx_i+b),所以

    [y_{i+1}=y_i+k ]

    这样我们就可以通过递推式的方法解决问题。

    基本增量算法(DDA)

    • 基本思想:舍入法求解最佳逼近;利用微分思想,即每一个点坐标都可以由前一个坐标变化一个增量得到。

    • 乘法用加法实现,每一个点坐标都可以由前一个坐标变化一个增量得到。

      [x_{i+1}=x_i+Delta x ]

      [y_{i+1}=y_i+Delta y ]

      [Delta=t_{i+1}-t_i ]

    • 该算法在x或y变化比较大的方向的增量绝对值为1,而另一方向上的增量绝对值小于等于1。

    • 为了方便,我们设置:(Delta x=1,Delta y=0.5)

    • 实现代码,下面代码有多处错误:

      //k在0到1之间 
      void LineDDA(int x0,int y0,int x1,int y1,int color){
      int x, dx, dy, y; float k; 
      dx = x1 - x0; dy = y1 - y0; k = dy / dx; y = y0; 
         for(x = x0; x <= x1; x++) {
                y = (int)(y + 0.5); SetPixel(x, y, color); y += k; 
          } 
      } 
    
    • 改错:
      //k在0到1之间 
      void LineDDA(int x0,int y0,int x1,int y1,int color){
      int x, dx, dy;
      //y应该是浮点型
      float k,y; 
      dx = x1 - x0; dy = y1 - y0; 
          //需要强制转换
          k =(float)dy / dx; 
          y = y0; 
         for(x = x0; x <= x1; x++) {
                // y = (int)(y + 0.5); SetPixel(x, y, color); 
                // 不应该先取整,这样会有很大误差。
                SetPixel(x, (int)(y + 0.5), color);
                y += k; 
          } 
      } 
    

    中点算法

    • 目标:消除DDA算法中的浮点运算,浮点数取整运算,不利于硬件实现; DDA算法效率低。会不会有另外一种递推的方法?

    • 直线隐式方程

      [F(x,y)=ax+by+c=0 ]

      其中: (a = y_0-y_1= -Delta y, b = x_1-x_0= Delta x, c = x_0*y_1- x_1*y_0)

    • 直线的正负划分性

      • 直线上方的点:(F(x, y) >0)
      • 直线下方的点:(F(x, y) <0)
      • 直线上的点: (F(x, y) =0)

    • 设:(y_i)实际坐标(y_{i, r}) 表示取整后的坐标(M)中点

    一、已计算出像素((x_i , y_{i,r}))如何判断距直线最近的下一个像素点

    答案是根据可能所取点间的中点(M)与直线的位置

    如果(M)在直线下方,那就选择像素点(NE),否则选择(E)

    通过构造判别式:(d = F(M) = F(x_{i}+1, y_{i,r}+0.5))(d) 的正和负可判定下一个像素

    二、如何判定下一个像素((x_i+2,??))

    • (d≥0),取正右方像素(E),则判定再下一个像素的(d)(d_1= F(x_i+2, y_{i,r}+0.5) = a(x_i+2) + b(y_{i,r}+0.5) + c = d + a)(d)的增量是(a)(即(-Delta y))
    • (d<0),取右上方像素(E),则判定再下一个像素的(d)(d_2= F(x_i+2, y_{i,r}+1.5) = d + a+b)(d)的增量为(a+b) (即(-(Delta y-Delta x)))

    三、增量(d_0)的初始值:

    [d_{0}=Fleft(x_{0}+1, y_{0}+0.5 ight)=Fleft(x_{0}, y_{0} ight)+a+0.5 b ]

    但是(F(x_0,y_0)=0),所以:

    四、增量(d)的递推公式

    [ d_0 = a + 0.5b ]

    [ d_{i+1}=left{egin{array}{cc} {d_{i}+a} & {d_{i}>0} \ {d_{i}+a+b} & {d_{i} leq 0} end{array} ight. ]

    五、优化:增量都是整数,只有初始值包含小数,可以用(2d)代替 (d)(2a)改写成(a + a)

    [ egin{aligned} &d_{0}=2 a+b\ &d_{t+1}=left{egin{array}{cc} {d_{i}+2 a} & {d_{i}>0} \ {d_{i}+2 a+2 b} & {d_{i} leq 0} end{array} ight. end{aligned} ]

    [egin{aligned} &y_{i+1}=left{egin{array}{cc} {y_{i}} & {d_{i}>0} \ {y_{i}+1} & {d_{i} leq 0} end{array} ight.\ &x_{i+1}=x_{i}+1 end{aligned} ]

    /*x0<x1,y0<y1,0<=k<=1*/
    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 = a + a + b;
        d1 = a + a;
        d2 =  (a + b) + (a + b);
        x = x0;
        y = y0;
        SetPixel(x, y, color);
        while (x<x1){
            if  (d<0){
                y++;
                d  += d2;
            }
            else
                d  += d1;
            x++ ;
            SetPixel(x,y, color);
        }
    }
    

    我们可以直接通过(d=F(M))的增量来构造递推式,从而:

    1、不必计算直线之斜率,因此不做除法;
    2、不用浮点数,只用整数;
    3、只做整数加减法和乘2运算,而乘2运算可以用硬件移位实现。

    其他情况

    上文的递推式子只是当(0 leq m leq 1) 的时候会满足

    这是因为(x)每次的变化量比(y)大,所以(y)每次最多增加(1)

    例如:(p_0(0,0))(p_1(6,12)),如果按照上面的递推式,得到的结果如下图绿线是:

    红线才应该是正确的。

    • (0 leq m leq 1) :

    [egin{aligned} &d_{0}=2 a+b\ &d_{i+1}=left{egin{array}{cc} d_{i}+2 a & d_{i}>0 \ d_{i}+2 a+2 b & d_{i} leq 0 end{array} ight. end{aligned} ]

    (d_i < 0)(y)增加(1)

    • (mgeq 1)

    [egin{aligned} &d_{0}=a+2 b\ &d_{i+1}=left{egin{array}{cc} d_{i}+2 a+2 b & d_{i}>0 \ d_{i}+2 b & d_{i} leq 0 end{array} ight. end{aligned} ]

    (d_i > 0)(x)增加(1)

    • (-1 leq m leq 0)

    [egin{aligned} &d_{0}=2 a-b\ &d_{i+1}=left{egin{array}{cc} d_{i}+2 a-2 b & d_{i}>0 \ d_{i}+2 a & d_{i} leq 0 end{array} ight. end{aligned} ]

    (d_i>0)(y)减少(1)

    • (mleq 0)

    [egin{aligned} &d_{0}=a-2 b\ &d_{i+1}=left{egin{array}{cc} d_{i}-2 b & d_{i}>0 \ d_{i}+2 a-2 b & d_{i} leq 0 end{array} ight. end{aligned} ]

    (d_i<0)(x)增加1

    记住了第一个,其他都是同理

    参考

    [1] https://www.cnblogs.com/wkfvawl/p/11621653.html
    [2] 老师课件

  • 相关阅读:
    一篇与面试官和蔼交流的深入了解JVM(JDK8)
    逆向工程,调试Hello World !程序(更新中)
    SpingBoot + Dubbo + Zookeeper实现简单分布式开发的应用
    Vue Axios 切片上传文件含实时进度
    Vue入门——常见指令及其详细代码示例
    女儿说要看烟花,但是政府规定不能放,程序员爸爸默默的拿起了键盘,程序员就是要为所欲为!
    逆向工程,调试Hello World !程序(更新中)
    python学习初始函数
    Python学习之装饰器
    Python学习之装饰器进阶
  • 原文地址:https://www.cnblogs.com/smallocean/p/12366557.html
Copyright © 2011-2022 走看看