本系列的隨筆在於給大家提供一些難度較深的canvas應用場景,借用數學或物理模型實現效果或性能媲美桌面應用的案例;並且此系將盡可能使用最簡明的js代碼展示效果。
推薦使用:chrome、ie9瀏覽器進行閱讀,同時我還在做一個基於canvas的矢量渲染器的類庫,希望大家關注。
話不多說,我們現在開始第一次隨筆的內容。
光柵圖形學(1)中點畫圓算法
我們平時在使用canvas繪制圖形時,通常會調用context的各種API,如設置樣式的strokeline、fillcolor等;再如繪制圖形的context.arc,context.fillRect等。
如果我們現在有一個場景,需要繪制1W個以上的圖形,並且要求其刷新頻率達到12fps以上,也就是說我們必須要在1秒內完成10W次canvasAPI的調用,想想這有多么可怕,大家可以在機子的機器上嘗試一下。。得卡到強制關閉瀏覽器。
下面我們引入了光柵圖形學中的中點畫圓算法。
1.獲取context元素的像素數組:
var cxt= canvas.getContext("2d"); cxt.clearRect(0, 0, width, height); var data = cxt.getImageData(0, 0,width, height); imageData = data.data;
現在imageData變量便引用了當前canvas元素的所有像素數組。
imageData的數據結構是一維數組,每四個元素表示一個像素的所有屬性,依次表示:r、g、b、alpha 的值,其范圍均是是(0——255)。
有了這樣的理論邏輯我們可以通過canvas上的任意一點(x,y)計算出imageData中表示次像素的第一個元素的索引值。
計算方式如下代碼:
function getStartIndex(x, y) { return y * width * 4 + x * 4; }
有了上面的理論知識,大家應該知道我們下面要做什么了吧?對就是利用中點畫圓算法對每一個像素進行顏色值(rgb)的修改。
2.光柵學——中點畫圓
首先我們通過圓的對稱性將其分為8個部分。
現在我們假設,這個圓的中點位於(0,0)的位置。
設d是點p(x,y)到圓心的距離:則我們得到這樣的圓方程FX(X,Y) = d 。
這里我們按照Bresenham算法,推出:
(此部分可能需要有圖形學學習經驗的同學,再以后的隨筆中可能會介紹Bresenham算法)。
如果dM<0,表示下一點M在圓內,得
如果dM>0,表示下一點M在圓外,得
。
有了上面的理論知識,我們就可以輕松的寫出繪制圓的算法了:
// 中點畫圓法 function circle(x, y, r, color) { var tx = 0, ty = r, d = 1 - r; while(tx <= ty){ // 利用圓的八分對稱性畫點 putpixel(x + tx, y + ty, color); putpixel(x + tx, y - ty, color); putpixel(x - tx, y + ty, color); putpixel(x - tx, y - ty, color); putpixel(x + ty, y + tx, color); putpixel(x + ty, y - tx, color); putpixel(x - ty, y + tx, color); putpixel(x - ty, y - tx, color); if(d < 0){ d += 2 * tx + 3; }else{ d += 2 * (tx - ty) + 5; ty--; } tx++; } }
putpixel函數用於將每一個像素的顏色值填入對應的數組當中,這里我們的顏色只表示灰度值,因為還沒有對彩色做任何處理。
function putpixel(x, y, color){ var index = getStartIndex(x, y); for(var i = 0; i< 4; i++) { if(i == 3) { imageData[index + i] = 255; } else{ // var co = parseInt(Math.random() * 255) imageData[index + i] = color; } } }
至此我們就已經把一個圓的像素填入我們的像素數組當中,最后將像素對象放回原來的canvas當中,就實現了圓的光柵畫法:
cxt.putImageData(data, 0, 0);
3.案例
下面這個案例以1w個圓的邊界碰撞檢測運動為例,說明了我們實現的光柵圖形性能完全可以勝任各種bt場景。(首要推薦chrome18其他瀏覽器可能解析js代碼比較慢)
下次提高班預告:實現光柵算法中的畫線算法。