首頁  >  文章  >  web前端  >  用canvas 實現個圖片三角化(LOW POLY)效果_javascript技巧

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

WBOY
WBOY原創
2016-05-16 15:14:502016瀏覽

之前無意中看到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上,就會發現,彩色圖片變成了這樣的灰階圖片:


由於上面說的那個Sobel組件不是很適合自己的用法,同時程式碼也有不恰當的地方,所以自己做了適當修改和優化,優化了循環方法,加快了運算速度,同時加入了回調函數。詳見該專案github中的sobel.js檔  


在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索引的時候再取整,獲取到的顏色值就是錯的。因為這樣取得到的那個像素點就不是我們要的中心像素點。


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

以上內容為大家介紹了用canvas 實現圖片三角化(LOW POLY)效果 ,希望對大家有幫助!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn