用canvas 實現個圖片三角化(LOW POLY)效果

  以前無心中看到Ovilia 用threejs作了個LOW POLY,也就是圖片平面三角化的效果,以爲很驚豔,而後就本身花了點時間嘗試了一下。html

  我是沒怎麼用過threejs,因此就直接用canvas的2d繪圖API來作,由於感受彷佛這效果也用不上threejs。git

  直接上demo先:http://whxaxes.github.io/canvas-test/src/Funny-demo/lowpoly/index.html   (也能夠在移動端看,不過由於計算量比較大,移動設備計算起來會比PC要多花些時間。)github

  作這種效果主要須要把圖片三角化,以及對圖片進行邊緣化檢測。這兩個,第一個用到的delaunay三角化算法,第二個用到的sobel邊緣檢測算法。聽起來偷挺高大上的,索性兩個算法都有相應的開源組件能夠直接拿來用:ironwallaby的delaunay組件  以及 Miguel Mota的sobel組件。算法

  這兩個算法sobel還好一點,delaunay就有點複雜了,待往後能夠研究一下。不過目前只爲作出個效果的話,仍是能夠用這些組件的。canvas

  兩個最重要的組件都有了,剩下的事就很簡單了:數組

  先將圖片繪製到canvas上:dom

canvas.width = (img.width > 800) ? 800 : img.width;
canvas.height = img.height * canvas.width/img.width;

ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

  而後獲取到canvas的imgData,再經過sobel計算返回新的imgData函數

 var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);

var newImgData = Sobel(imgData);

   若是咱們把newImgData放到canvas上,就會發現,彩色圖片變成了這樣的灰度圖片:優化

  

  因爲上面說的那個Sobel組件不是很適合本身的用法,同時代碼也有不恰當的地方,因此本身作了適當修改和優化,優化了循環方法,加快了運算速度,同時加入了回調函數。詳見該項目github中的sobel.js文件url

  在Sobel方法中對imgData.data進行遍歷的時候,會調用回調函數,在回調中把顏色值大於40(也就是灰度爲rgb(40,40,40)以上的)的座標點記錄下來。而後隨機獲取一部分邊緣點,再加入一些隨機出來的座標 以及 四個邊角的座標值。這樣,咱們就能夠獲取到咱們須要的座標點了

        var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);

//          收集色值大於40的邊緣像素點
            var collectors = [];
            Sobel(imgData , function(value , x , y){
                if(value > 40){collectors.push([x , y]);}
            });

//          添加一些隨機點
            for(var i=0;i<300;i++){particles.push([Math.random()*canvas.width , Math.random()*canvas.height]);}

//          添加隨機邊緣點,數量爲邊緣點數量除於50
            var length = ~~(collectors.length/50), random;
            for(var l=0;l<length;l++){
                random = (Math.random()*collectors.length)<<0;
                particles.push(collectors[random]);
                collectors.splice(random , 1);
            }

//          添加四頂點座標
            particles.push([0,0] , [0,canvas.height] , [canvas.width,0] , [canvas.width,canvas.height]);

  獲取到座標點後,就能夠經過delaunay組件計算,獲取到拍好次序的三角座標數組,對這些數組裏的點進行連線,就能夠出現這樣的效果:

  

   固然,咱們要的效果不是連線,而是對全部三角形進行顏色填充,也就是獲取三角形的三個座標,而後計算出中心點的座標,再根據中心點座標在imgData裏獲取到相應的rgb的顏色值,而後填充到三角區域就能夠了:

//        使用delaunay三角化獲取三角座標
var triangles = Delaunay.triangulate(particles);

var x1,x2,x3,y1,y2,y3,cx,cy;
for(var i=0;i < triangles.length; i+=3) {
    x1 = particles[triangles[i]][0];
    x2 = particles[triangles[i+1]][0];
    x3 = particles[triangles[i+2]][0];
    y1 = particles[triangles[i]][1];
    y2 = particles[triangles[i+1]][1];
    y3 = particles[triangles[i+2]][1];

//            獲取三角形中心點座標
    cx = ~~((x1 + x2 + x3) / 3);
    cy = ~~((y1 + y2 + y3) / 3);

//            獲取中心點座標的顏色值
    index = (cy*imgData.width + cx)*4;
    var color_r = imgData.data[index];
    var color_g = imgData.data[index+1];
    var color_b = imgData.data[index+2];

//            繪製三角形
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.lineTo(x3, y3);
    ctx.closePath();
    ctx.fillStyle = "rgba("+color_r+","+color_g+","+color_b+",1)";
    ctx.fill();
    ctx.restore();
}

  上面有一點要注意,獲取到的中心點座標必定要取整,纔可以獲取到正確的顏色參數,若是想着不取整,而是在獲取rgb索引的時候再取整,獲取到的顏色值就是錯的。由於這樣獲取到的那個像素點就不是咱們要的中心像素點。

  顏色也獲取到後,就是簡單的連線,而後填充操做了,最後出來的效果就是:

  

  

  雖然沒有設計師手動描出來的好看,不過也方便不少,作來玩玩仍是挺有意思的。

  源碼地址,有興趣的能夠一看哈:

  https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Funny-demo/lowpoly

相關文章
相關標籤/搜索