1. 程式人生 > >計算機圖形學基礎-直線掃描轉化演算法

計算機圖形學基礎-直線掃描轉化演算法

前言:

在數學上,直線上的點有無窮多個。擔當在計算機光柵顯示器螢幕上表示這條直線時需要做一些處理。

為了在光柵顯示器上用這些離散的畫素點逼近這條直線,需要知道這些畫素點的x,y座標。

求出過p0,p1的直線段方程:y=kx+b; k=(y1-y0)/(x1-x0)(x1 ≠ x0)

假設x已知,即從x的起點x0開始,沿x方向前進一個畫素(步長=1),可以計算出相應的y值。因為畫素的座標時整數,所以y值還要進行取整處理

如:p(1.7,0.8)取整為p(1,0)而1.7和0.8分別離2和1較近,所以可以先將座標+0.5再取整

直線時最基本的圖形,一個動畫或真實感圖形往往需要呼叫成千上萬次畫執行緒序,因此直線演算法的好壞將直接影響圖形的質量和顯示速度。

 

直線的三種掃描轉換演算法:

  • 數值微分演算法(Digital Differential Analyzer,簡稱DDA)
  • 中點畫線法
  • Bresenham演算法

 

數值微分法DDA(Digital DIfferential Analyer):

引進圖形學中一個很重要的思想——增量思想

這個演算法採用的是直線的斜截式方程(y=kx+b)。要求先算出直線的斜率,然後從起點開始,確定最佳逼近於直線的y座標。假設起點的座標為整數,直線方程為y=kx+b,k的取值在0到1之間,x每遞增1,y相應地遞增k

這裡需要處理的是:畫素的座標都是整數,x每次加1,y值必須是整數才行,所以這裡要進行取整處理。如果採用+0.5進行出來,是可以達到效果,但是直線是最基本的圖形,一個動畫或者圖形往往要呼叫上萬次直線處理,所以相率十分低下。

此時,y=kx+b ,是通過一個乘法,再一個加法,再進行取整處理,才能畫出這條直線。在計算機中,加減乘除,最快的是加法運算,如果能把乘法運算取消的話,只剩加法運算,效率會大大提高!所以如果把乘法取消掉呢?那就是增量思想。

推導如下:

 

最終:yi+1 = kxi+1 + b。

式子的含義是:當前步的y值等於前一步的y值加上斜率k。這樣就把原來一個乘法和加法變成了一個加法!

我們用DDA演算法實驗一個例子:

首先計算K值,為0.6。0.6小於1。

下一次,x+1,新的y值為前一個y值+k: 0+0.6,進行四捨五入處理再加0.5取捨,為(0+0.6+0.5=1.1,四捨五入)1。

以此類推。。

 

DDA演算法缺陷:

當|k|大於1的情況下:此時例子如下:

如果在絕對值k大於1的情況下,出現的情況是光柵點太稀疏了!我們表達一個點,要足夠多的點,這樣才看上去會是一條直線,而此時用DDA演算法,光柵點太稀疏了!

-------------------------

DDA演算法是否是最優呢?不是最優,如何改進呢?

第一個思路:計算機中,整數運算速度大於浮點運算速度,DDA演算法是浮點加法,我們能否改進為整數加法呢??!

第二個思路:從直線方程上面型別做文章!下面就是中點畫線法!

 

中點畫線法

 

中點畫線法採用一般是方程。

首先,給一個點F(x,y),用直線方程來說,對於直線的點,如果F(x,y)在直線上,則為0;如果F(x,y)直線上方則大於0;如果F(x,y)在直線下方則小於0.

 

 

所以中點畫線法的基本原理是:直線在x方向增加一個單位,則在y方向上的增量只能是0和1之間。

 

 

 

如上圖:ideal line為理想直線。仍然是x+1,確定y的值。

 M(xp + 1, yp + 0.5) 表示P1和P2的中點,Q是理想直線與垂直線x = xp + 1的交點,如果M點在Q點下方,很明顯P2離直線更新一些,如果M在Q的上方,則P1離直線更新一些,我們取離直線最近的那個畫素點,如果M和Q重合,我們約定取正右方的那個點。那麼如何判斷呢?即我們將M的座標代入到直線的一般式方程。

 

 

假設起點和終點為 (x0, y0)和(x1, y1),則直線方程為:

F(x, y) = ax + by + c, 其中a = y0 - y1, b = x1 - x0,c = x0y1 - x1y0

 

對於直線上的點,F(x, y) = 0;直線上方的點,F(x, y) > 0;直線下方的點,F(x, y) < 0.  因此,判斷M在Q的上方還是下方,只需將M點帶入方程計算判別式:

d = F(M) = F(xp + 1, yp + 0.5) = a(xp + 1) + b(yp + 0.5) + c

 

對於每一個畫素的判別式d,根據它的符號來判斷下一個畫素點。 由於d是xp和yp的線性函式,可採用增量計算來提高運算效率。

當d小於0,中點M在直線的下方,說明直線離上面的點近,取上方的點。

當d大於0,說明中點M在直線的上方,說明直線離下面的點近,取下方的點。

這就是中點直線畫法的判別方式,y的值看d的大小,如圖下:

 

 

好了,此時,我們看看此時中點劃線演算法的效率:

為了求出d,我們需要兩個乘法,四個加法!

 

那麼我們如何提高效率,能否和DDA演算法一樣,增量思想呢?完全可以!關鍵在於如果推匯出d的遞推公式!

如下:

在d小於0的情況下:

 

上面已經推導的很清晰:稍微解釋一下,求解d0是把M0代進去,求第二個點,d1是把M1代進去,求緊接著的下一個點。

最終推匯出di = di-1 + A +B,增量為A+B

 

在d大於等於0的情況下:

這個也一樣,最終推匯出di+1=di+A,增量為A。

還缺少關鍵一部,那就是計算d的初始值d0的值:

P0為起始點,是直線上的點。所以下一個點的中點M必定為(x0+1,y+0.5),將M帶入到直線方程中,得到如上的公式,由於x0,y0是直線上的點,滿足方程等於0.所以Ax0+By0+C等於0.

所以d0 = A+0.5B!

最終的方程也就出來了,

但是由於(x0, y0)在直線上,所以F(x0, y0) = 0,因此,d的初始值為a + 0.5b。

d的初始值包含小數因此可以用2d來代替d,寫出僅包含整數運算的演算法:

 

Bresenham演算法

Bresenham演算法也是一種計算機圖形學中常見的繪製直線的演算法,其本質思想也是步進的思想,但由於避免了浮點運算,相當於DDA演算法的一種改進演算法。

我們看下基本思想:

 

 

其實就是看d的符號(值)來缺點y的值!k是斜率。

 

如上圖來看:

首先求d0=0。d=d+k。一旦d>=1,就減去1,保證d的相對性。就是上圖的第三個d,是通過減1得到的。保證d在0到1之間!

判斷如下:

 

此時y的下一個值,看d的值,如上圖。但是上面也說到了,此時有浮點運算,如何提高到整數加法呢!我們發現其實只要看d的符號就可以,沒必要算出d。所以另e=d-0.5。用e來代替d。

此時:

 

原來d0=0,所以此時e0等於0.5。下一個e=e+k。當e大於0,e也要減去1,保證e也在0到1之間。

改進二:

因為k=dy/dx,該演算法會用到小數和除法,為了利於硬體實現,可以改用整數以避免除法,演算法中只用到誤差項的符號。所以

可以作如下替換:e' = 2 * e * dx;

 

最終結果出來了:

下一盤將附上程式碼