圖形學(3)光柵圖形學的直線繪製(下)

本模塊內容絕大部分是在慕課上看中國農業大學網客時的筆記,因此算作轉載,在此鳴謝趙明、李振波兩位老師,感謝他們錄製該門課程供大家學習!




Bresenham算法

前兩種算法把效率提高到了整數加法級別,只講效率,基本已經可以說是最快了。但是,兩者均嚴重依賴直線方程,那麼,有沒有算法在保證算法效率的同時能夠擴大使用範圍,將畫線算法適用的範圍變得更加廣泛呢?(曲線等)——即是下面要介紹的Bresenham算法。


算法思想

Bresenham早在1962年就發表了該算法,該算法結合DDA與中點畫線法的優勢,它的思路是構造一個由各行各列象素中心構造一組虛擬的網格線,按照直線起點到終點的順序,計算直線與各垂直網格線的交點,然後根據誤差項的符號確定該列像素中與此交點最近的像素。

假設按照如下圖的方向步進、判斷,x每次++,y是否遞增取決於直線與最近像素點中心的距離,也即以0.5爲界,與d=d+k比較,判斷直線具體在哪個像素點內。另外,一旦d>=1,就執行d-=1操作,以保證d的相對性,使它在0,1之間


基於該算法的改進

由於前面的算法已經可以把直線繪製效率提高到整數加法級別了,因此雖然這個思路有優勢,但如果不把它也提高到相同級別,也還是白搭。下面是對其效率提高的探索。

改進1

令e=d-0.5,這樣判斷條件就從d與0.5這個浮點數比較,變爲了e與0比較大小。
此時e的初值是-0.5,每走一步有e+=k,要保持e不要太大可以有:if(e>0)e-=1      *這裏沒有用e>0.5作判斷條件,也是因爲想避免浮點數的出現。理解時可以舉個例子試一下,效果跟e>0.5是一樣的。

改進2

根據斜率的定義,k=(y2-y1)/(x2-x1),由於現在判斷條件不等式的一邊是0,因此可以同乘一下把e化爲整數,即使用2*e*(x2-x1)替換原來的e,而e初值中的浮點數也一併化爲了整數。
*這裏根據前面的假設默認x2>x1

改進後的算法步驟

  1. 讀取兩點座標
  2. 計算初始值△x=x2-x1,△y=y2-y1,e=-△x,x=x1,y=y1......依然假設x2>x1,在實際編程中需要判斷一下
  3. 繪製點(x,y)
  4. e+=2*△y,判斷e的正負決定要描的像素點
  5. 循環3,4直到繪製完成

程序如下

public class Bresenham extends JFrame {
    private int x1 , x2 , y1 , y2 , e;

    public Bresenham( int x1 , int y1  , int x2 , int y2 ){
        super();
        setTitle("Bresham直線繪製算法");
        setSize(800,600);
        setVisible(true);
        this.x1 = x1;
        this.x2 = x2;
        this.y1 = y1;
        this.y2 = y2;
        bresenhamDrawLine(x1 , y1 , x2 , y2 );
    }
    private void bresenhamDrawLine(int x1 , int y1 , int x2 , int y2){
        if (abs(x2-x1)>abs(y2-y1)){         //按x方向遞增
            if (x1 > x2){
                int temp = x1;
                x1 = x2;
                x2 = temp;
                temp = y1;
                y1 = y2;
                y2 = temp;
            }
            e = x1 - x2;
            drawPixel(x1,y1);
            for (int i = x1 , j = y1 ; i < x2 ; i++){
                e += 2*(y2 - y1);
                if (e > 0){
                    j++;
                    drawPixel(i,j);
                }
                else {
                    drawPixel(i,j);
                }
            }
        }else {
            if (y1 > y2){
                int temp = y1;
                y1 = y2;
                y2 = temp;
                temp = x1;
                x1 = x2;
                x2 = temp;
            }
            e = y1 - y2;
            drawPixel(x1,y1);
            for (int j = y1 , i = x1 ; j < y2 ; j++){
                e += 2*(y2 - y1);
                if (e>0){
                    i++;
                    drawPixel(i,j);
                }else {
                    drawPixel(i,j);
                }
            }
        }
    }
通過以上我們可以看出,Bresenham算法並沒有去求k,b亦或是A,B,C,僅僅是通過兩點座標去繪製直線,不限定直線方程類型,集合DDA與中點畫線法優勢,應用更廣泛。

小結

讀過這三種算法,我們經歷了一個算法不斷優化,精益求精的過程,領略了圖形學的魅力所在。這些算法中所蘊含的思想是值得我們用心揣摩、領悟的,這對於以後的學習會有很大幫助,比如從二維到三維的直線繪製等。
另外,我們可以看得出,算法中永遠沒有“最優”,只有不斷的改進。我們要勤于思考,發散思維,發現算法的不足並思考改進方案。

下面是一些可以大致讀得懂的知網論文,筆者只看過一篇,感興趣大家可以去看一看作爲擴展





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章