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

來源:互聯網
上載者:User

 之前無意中看到Ovilia 用threejs做了個LOW POLY,也就是圖片平面三角化的效果,覺得很驚豔,然後就自己花了點時間嘗試了一下。

我是沒怎麼用過threejs,所以就直接用canvas的2d繪圖API來做,因為感覺似乎這效果也用不上threejs。

直接上demo先:http://whxaxes.github.io/canvas-test/src/Funny-demo/lowpoly/index.html   (也可以在移動端看,不過因為計算量比較大,行動裝置計算起來會比PC要多花些時間。)

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

這兩個演算法sobel還好一點,delaunay就有點複雜了,待日後可以研究一下。不過目前只為做出個效果的話,還是可以用這些組件的。

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

先將圖片繪製到canvas上:

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上,就會發現,彩色圖片變成了這樣的灰階圖片:

  圖片處理好後,再對imgData進行遍曆,把顏色值大於40(也就是灰階為 rgb(40,40,40)以上的)的座標點記錄下來。因為我們要隨機選取一些處於邊緣的座標點,所以對收集的座標進行打亂,再加入一些隨機出來的座標 以及 四個邊角的座標值。這樣,我們就可以擷取到我們需要的座標點了

var collectors = [];
var index;
//        收集色值大於40的邊緣點
for(var x=0;x<imgData.width;x++){
   for(var y=0;y<imgData.height;y++){
       index = ~~(y*imgData.width + x)*4;

       if(newImgData.data[index] > 40){
            collectors.push([x , y]);
       }
   }
}

//        對收集到的點隨機排列
collectors.sort(function(){return Math.random()-Math.random()});

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

//        添加邊緣點
particles = particles.concat(collectors.slice(0,~~(collectors.length/50)));

//        添加四頂點座標
particles = particles.concat([[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索引的時候再取整,擷取到的顏色值就是錯的。因為這樣擷取到的那個像素點就不是我們要的中心像素點。

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

  

  

  雖然沒有設計師手動描出來的好看,不過也方便很多,做來玩玩還是挺有意思的。



相關文章

Cloud Intelligence Leading the Digital Future

Alibaba Cloud ACtivate Online Conference, Nov. 20th & 21st, 2019 (UTC+08)

Register Now >

11.11 Big Sale for Cloud

Get Unbeatable Offers with up to 90% Off,Oct.24-Nov.13 (UTC+8)

Get It Now >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。