图形是怎么生成的?

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

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

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

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

图元的生成

  • 概念:图元的参数表示形式到<stron>表示形式的转换</stron>

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

  • <stron>是光栅显示系统刷新时所需的表示形式。</stron>

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

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

    • 写像素: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}=F\left(x_{0}+1, y_{0}+0.5\right)=F\left(x_{0}, y_{0}\right)+a+0.5 b \]

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

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

\[ d_0 = a + 0.5b \]
\[ d_{i+1}=\left\{\begin{array}{cc} {d_{i}+a} & {d_{i}>0} \\ {d_{i}+a+b} & {d_{i} \leq 0} \end{array}\right. \]

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

\[ \begin{aligned} &d_{0}=2 a+b\\ &d_{t+1}=\left\{\begin{array}{cc} {d_{i}+2 a} & {d_{i}>0} \\ {d_{i}+2 a+2 b} & {d_{i} \leq 0} \end{array}\right. \end{aligned} \]
\[\begin{aligned} &y_{i+1}=\left\{\begin{array}{cc} {y_{i}} & {d_{i}>0} \\ {y_{i}+1} & {d_{i} \leq 0} \end{array}\right.\\ &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\) :
\[\begin{aligned} &d_{0}=2 a+b\\ &d_{i+1}=\left\{\begin{array}{cc} d_{i}+2 a & d_{i}>0 \\ d_{i}+2 a+2 b & d_{i} \leq 0 \end{array}\right. \end{aligned} \]

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

  • \(m\geq 1\)
\[\begin{aligned} &d_{0}=a+2 b\\ &d_{i+1}=\left\{\begin{array}{cc} d_{i}+2 a+2 b & d_{i}>0 \\ d_{i}+2 b & d_{i} \leq 0 \end{array}\right. \end{aligned} \]

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

  • \(-1 \leq m \leq 0\)
\[\begin{aligned} &d_{0}=2 a-b\\ &d_{i+1}=\left\{\begin{array}{cc} d_{i}+2 a-2 b & d_{i}>0 \\ d_{i}+2 a & d_{i} \leq 0 \end{array}\right. \end{aligned} \]

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

  • \(m\leq 0\)
\[\begin{aligned} &d_{0}=a-2 b\\ &d_{i+1}=\left\{\begin{array}{cc} d_{i}-2 b & d_{i}>0 \\ d_{i}+2 a-2 b & d_{i} \leq 0 \end{array}\right. \end{aligned} \]

\(d_i<0\)\(x\)增加1

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

参考

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