우연히 Ovilia가 threejs를 사용하여 사진의 평면 삼각법 효과인 LOW POLY를 만드는 것을 보고 정말 대단하다고 생각해서 직접 시도해 보았습니다.
저는 threejs를 많이 사용하지 않아서 그냥 캔버스의 2D 드로잉 API를 이용해서 해봤습니다. 이 효과에는 threejs를 사용할 수 없을 것 같아서 입니다.
먼저 데모로 바로 이동하세요: http://whxaxes.github.io/canvas-test/src/Funny-demo/lowpoly/index.html (모바일에서도 시청하실 수 있습니다) 단말기인데 계산 때문에 금액이 상대적으로 크고 PC보다 모바일에서 계산하는데 시간이 더 걸립니다)
이 효과를 얻으려면 주로 이미지를 삼각 측량하고 이미지에서 가장자리 감지를 수행해야 합니다. 이 두 가지 중 첫 번째는 delaunay 삼각측량 알고리즘을 사용하고 두 번째는 sobel edge 감지 알고리즘을 사용합니다. 매우 고급스러워 보이지만 두 알고리즘 모두 직접 사용할 수 있는 해당 오픈 소스 구성 요소(ironwallaby의 delaunay 구성 요소 및 Miguel Mota의 sobel 구성 요소)를 가지고 있습니다.
이 두 가지 알고리즘 중 sobel이 더 좋지만 delaunay는 나중에 연구해 볼 수 있습니다. 그러나 지금 당장 효과를 생성하려는 경우에도 이러한 구성 요소를 사용할 수 있습니다.
이제 가장 중요한 두 가지 구성 요소가 준비되었으므로 나머지는 매우 간단합니다.
먼저 캔버스에 이미지를 그립니다.
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);
그런 다음 캔버스의 imgData를 가져온 다음 소벨 계산을 통해 새 imgData를 반환합니다
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); var newImgData = Sobel(imgData);
newImgData를 캔버스에 넣으면 컬러 이미지가 다음과 같이 회색조 이미지가 되는 것을 알 수 있습니다.
위에서 언급한 소벨 컴포넌트는 제 용도에 별로 적합하지 않고, 코드도 부적절해서 적절하게 수정 및 최적화하고, 루프 방식을 최적화하고, 계산 속도를 높이고, 콜백 함수를 추가했습니다. 자세한 내용은 프로젝트 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 구성요소를 사용하여 올바른 순서로 삼각 좌표 배열을 계산하고 얻을 수 있습니다. 이러한 배열의 점을 연결하면 다음과 같은 효과를 얻을 수 있습니다.
물론 우리가 원하는 효과는 선을 연결하는 것이 아니라 모든 삼각형을 색으로 채우는 것, 즉 삼각형의 세 좌표를 구한 다음 중심점의 좌표를 계산하고 그에 해당하는 값을 구하는 것입니다. 중심점 좌표 RGB 색상 값을 기준으로 imgData의 좌표를 지정한 다음 삼각형 영역에 채웁니다.
// 使用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 인덱스를 가져올 때 반올림하면 색상 값이 잘못될 수 있습니다. 이렇게 얻은 픽셀은 우리가 원하는 중심 픽셀이 아니기 때문입니다.
색상을 얻은 후 간단한 연결 후 채우기 작업을 수행하면 최종 효과는 다음과 같습니다.
위 내용은 캔버스를 사용하여 그림을 삼각측량하는 효과(LOW POLY)를 얻는 방법을 소개합니다.