HTML5 Canvas 提高班(二) —— 光柵圖形學(2)Bresenham算法畫直線


    上次的隨筆介紹了如何用中點畫圓的算法提高Canvas繪圖性能,感覺大家還是比較感興趣的。

    本節借助HTML5 canvas 強大的像素處理能力,重點給大家介紹計算機圖形中-光柵學Bresenham算法;並實現兩點畫直線的程序。

光柵圖形學(2)Bresenham算法畫直線

    Bresenham算法是計算機圖形學典型的直線光柵化算法,其歷史可以追溯到上個世界,由 Jack E. Bresenham 1962年在IBM工作時提出。算法的歷史這里就不做更多的介紹了,有興趣的同學可以看這個wikipedia的鏈接里面有很詳細的介紹,我們現在就關注其實現的理論。

    1.Bresenham 算法的實現原理

    用通俗的方法解釋Bresenham 算法:由於計算機屏幕的特殊性,像素不可能有小數的顯示,比如:我們無法顯示0.5像素在屏幕上。這樣我們可以假設一條線段的斜率為k,當這條直線在X方向增加(或減少)1個像素的同時;其Y方向只可能增加(或減少)1或是0,它取決於實際直線與最近光柵網格點的距離,這個距離的最大誤差為0.5。有了感性的認識,下面我們在數學的世界中計算這個Y方向的改變量。

    Bresenham 算法的數學實施:(為了能理解下面的內容可能需要一些基本的數學概念,如果已經忘了也沒有關系我會盡可能簡單的說明

    1。假設空間中有兩點P1和P2,則我們可以求得斜率k,假設0<k<1,這樣我們只需要考慮x方向每次遞增1,y的方向每次是遞增1或是0。

    2。設:  直線方程為:y = kx + b。

               直線當前點為(xi, y) —— 此點是未經過離散后的點。

               直線當前光柵點為(xi, yi) —— 此點是經過離散后的點。

        則: 下一個直線的點應為(xi+1, k(xi + 1) + b),

              由於光柵點只可能位於yi,或yi+1的位置,我們用變量 d_upper 表示真實點的坐標與(yi + 1)離散點坐標的距離:d_upper = (yi + 1) - (k(xi + 1) + b);用d_lower 表示真實點的坐標與(yi)離散度坐標的距離:d_lower = (k(xi + 1) + b) - yi。

     這樣一來我們就可以明確該如何選擇點:如果d_upper > d_lower,則表示真實點距yi的位置更近我就選擇yi為下一離散點的y坐標;反之選擇yi + 1。

        所以我們程序中需要求: d_lower - d_upper = 2k(xi + 1) - 2yi + 2b -1(化簡后的結果,如果大家不相信可以自己算算哈)。(1)

        設:p1,p2之間x方向的距離為 dx,y方向的距離為dy,則斜率k = dy / dx;

        我們將所求的的算式(1)乘以系數dx,命名為變量 Pi(第i步的決策參數) = dx(d_lower - d_upper) = 2*dy*xi - 2*dx*yi + c 。(2)(c=2*dy + dx(2b - 1),c為一常數在程序計算時會省去)。

        通過算式(2)我們可以得出Pi+1(第i + 1時的決策參數) = 2 * dy * Xi+1 - 2*dx * Yi+1 + c (3)

        將算式(3)與算式(2)做差並化簡后得 :

         (4)(呼呼,終於推倒出我們要的算式了,不知道大家看懂推導過程了沒,其實都很簡單就是步驟多了些)

        可以通過算式(2)得出p0 = 2 dy - dx。

    之后我們就遞歸的調用計算決策參數的算法就可以啦,通過互換x、y的位置和坐標的位置可將其算法擴展到任意象限。

    2.Bresenham的程序實現

// 使用 Bresenham 算法畫任意斜率的直線(包括起始點,不包括終止點)
function Line_Bresenham(x1, y1, x2, y2, color)
{
    var  x = x1;
    var y = y1;
    var dx = Math.abs(x2 - x1);
    var dy = Math.abs(y2 - y1);
    var s1 = x2 > x1 ? 1 : -1;
    var s2 = y2 > y1 ? 1 : -1;
    
    var interchange = false;    // 默認不互換 dx、dy
    if (dy > dx)                // 當斜率大於 1 時,dx、dy 互換
    {
        var temp = dx;
        dx = dy;
        dy = temp;
        interchange = true;
    }
    
    var p = 2 * dy - dx;
    for(var i = 0; i < dx; i++)
    {
        putpixel(x, y, color);
        if (p >= 0)
        {
            if (!interchange)        // 當斜率 < 1 時,選取上下象素點
                y += s2;
            else                    // 當斜率 > 1 時,選取左右象素點
                x += s1;
            p = p + 2 * dy - 2 * dx;
        }
        if (!interchange)
            x += s1;                // 當斜率 < 1 時,選取 x 為步長
        else
            y += s2;                // 當斜率 > 1 時,選取 y 為步長
        p += 2 * dy;
    }
}

    這段代碼考慮到任意斜率的情況,大家可以調調看看對不同斜率的直線是如何做處理的。

    下面這段簡單的demo是對這兩次隨筆的一個綜合應用(當然還很不完善,大家領會意思就好):

 

    下次隨筆預告:1.多邊形繪制。

                        2.填充色。


免責聲明!

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



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