二維圖元生成:直線生成算法


圖形是怎么生成的?

視頻控制器通過訪問幀緩存來刷新屏幕

幀緩存中的保存的是點陣數據,而我們將要討論的是

如何將圖形的幾何參數來得到點陣數據,本文主要介紹最簡單的直線生成算法

通過兩個點\(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}=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] 老師課件


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM