2013-02-12 2 views
4

here에 설명 된 '키스톤 (keystoning)'기술을 사용하여 html5 캔버스가있는 '하드 커버'책 페이지에 원근감을 성공적으로 추가했습니다.HTML5 캔버스로 이미지/텍스처의 원근감 변환

근본적으로는 이미지/캔버스 요소가 원본 텍스처로 정의됩니다. 각 렌더 루프 동안 이것은 정의 된 너비의 세그먼트 (최고 품질 인 1px)로 분할되고 각 세그먼트는 텍스처의 X 축에서의 위치에 따라 높이가 조정됩니다.

아래에서 볼 수 있듯이이 주어진 소스 이미지/텍스처와 관점의 좋은 환상을 만들어 :

enter image description here

'하드 커버'페이지가 전환을 위해이 어떤과 함께 질감을 잘 작동 포함 된 텍스트는 좋은 인상을 준다. 그러나 나는 같은 종류의 키스톤을 '부드러운'페이지 전환에 적용해야합니다.

enter image description here

현재 페이지 질감 특정의 곡선의 최대 높이로 스케일링 아래 알 수있는 바와 같이, 문제는 단순한 투시 변환 자체가 곡선으로 정의 된 페이지로 작동하지 않을 것이다 이미지/텍스처를 클립하도록 설정된 페이지 가장자리 이차 곡선 경로로 페이지를 회전합니다. 다양한 표준 캔버스 함수를 사용하여 그림자와 페이지 선을 동적으로 그렸기 때문에 2 차 곡선으로 그려진 페이지 표선이 페이지 넘김에 자연스러운 시각을 제공하므로이 기능이 허용됩니다.

그러나 텍스트 (다른 ​​캐시 된 캔버스/이미지 요소에서 가져온 것임)는보기에 충분하지 않으며 항상 평평합니다.

내가 뭘하고 싶은지는 위에서 언급 한 것과 같은 슬라이싱/분할/스케일링 키스톤 기법을 적용하고 있지만 어쨌든 이차 곡선 (경로로 그려진 ctx.quadraticCurveTo();) 경로와 수직 위치를 기반으로 각 1px 세그먼트의 높이를 계산합니다. 캔버스.

내 예제 이미지에서는 실제로보기가 너무 좋지 않지만 텍스트가 페이지 위쪽/아래쪽에 가까워지면 뒤틀림 효과가 커야합니다. 뿐만 아니라 페이지 접기에 가장 가까운 텍스트를 스쿼시하기 위해 수평 축척 비율을 계산해야합니다.

저는 실제로 두려운 예제 코드를 제공 할 입장이 아닙니다. 근본적으로 우리는 위에서 제공 한 링크에서 키스톤이 묘사되는 방식과 매우 유사한 방식으로 작업하고 있습니다. 그리고 아래의 기능 쇼 렌더링/슬라이싱에 각 세그먼트의 축척 계수를 계산하기 위해/값을 차 곡선을 좌표를 사용할 수 있어야합니다 요약 :

function keystoneAndDisplayImage(ctx, img, x, y, pixelWidth, scalingFactor) { 
     var h = img.height, 
      w = img.width, 

      // The number of slices to draw. 
      numSlices = Math.abs(pixelWidth), 

      // The width of each source slice. 
      sliceWidth = w/numSlices, 

      // Whether to draw the slices in reverse order or not. 
      polarity = (pixelWidth > 0) ? 1 : -1, 

      // How much should we scale the width of the slice 
      // before drawing? 
      widthScale = Math.abs(pixelWidth)/w, 

      // How much should we scale the height of the slice 
      // before drawing? 
      heightScale = (1 - scalingFactor)/numSlices; 

      for(var n = 0; n < numSlices; n++) { 

      // Source: where to take the slice from. 
      var sx = sliceWidth * n, 
       sy = 0, 
       sWidth = sliceWidth, 
       sHeight = h; 

      // Destination: where to draw the slice to 
      // (the transformation happens here). 
      var dx = x + (sliceWidth * n * widthScale * polarity), 
       dy = y + ((h * heightScale * n)/2), 
       dWidth = sliceWidth * widthScale, 
       dHeight = h * (1 - (heightScale * n)); 

      ctx.drawImage(img, sx, sy, sWidth, sHeight, 
          dx, dy, dWidth, dHeight); 
     } 
    } 

을 ...하지만 난 정말 아무 생각이 곳이 없다 시작합니다. 어떤 도움이라도 대단히 감사하겠습니다.

편집 :

는 내가 달성을 기대하고 무엇에 대해 좀 더 생각하는 데, 나는이 방법을 통해 3D 텍스처 매핑의 완벽한 표현을 물어 조금 너무 많이 수 있다는 것을 깨달았다. 따라서 html5 캔버스에 정의 된 2 차 곡선을 기반으로 각 세그먼트의 높이 및 y 위치를 간단히 변경할 수있는 해답을 얻는 것이 더 이상 행복 할 것입니다.

enter image description here

는 내가 계산할 수있는 1 픽셀의 폭 세그먼트가 필요로 할 때 더 큰 텍스트가 바로 하나의 비 중추 왜곡 이미지 텍스처와 완전히 유지 볼 수 있듯이 : 여기

더 나은 예를 들어, 이미지 커브 팩터와 y 위치를 자연스럽게 페이지 넘김에 따르도록합니다. 이 인스턴스에서 반 사실적인 원근감 효과를 얻기위한 다른 '해킹'이나 방법이 도움이되지 않는다면 실패합니다.

답변

4

내가 .... 솔루션 드디어 환상적인 대답

감사를 발견 :

Center point on html quadratic curve 나는 차 곡선의 어느 지점의 y 값을 얻기 위해 필요한 방정식을했다.

그런 다음이 y 값을 사용하여 텍스트 텍스처의 높이에 대한 조정을 계산하는 간단한 경우입니다. 나는 최선의 2 차 곡선 계산을 찾기 위해 약간의 조정을 하였지만 그 결과는 여전히 환상적입니다. 그것은 현재 파이어 폭스/크롬과 IE9/10에서 45fps 주위에서 약 60 프레임을 실행

enter image description here

:

은 아래 이미지를 참조하십시오. 최적화를위한 여지는 있지만 확실하지는 않지만 그 결과에 매우 만족합니다. 분명히 곡선에 맞춰 텍스쳐를 수평으로 늘리거나 스쿼시하지는 않지만 텍스쳐를 통과하는 최소한 하나의 여분의 패스가 필요할 것입니다. 아마도 성능을 손상시킬 수 있습니다.

다른 옵션은 실제 Affine 3d 텍스처 매핑을 사용하는 것이었지만 약간의 정확도를 희생하면서 성능과 품질면에서 훨씬 뛰어났습니다. 페이지 플립 내 주요 렌더링 루프 내에

루프는 다음과 같습니다

getQuadraticCurvePoint : function(startX, startY, cpX, cpY, endX, endY, position) { 
      return { 
       x: this.getQBezierValue(position, startX, cpX, endX), 
       y: this.getQBezierValue(position, startY, cpY, endY) 
      }; 
     }, 

과 :

관련 차 포인트 기능으로 존재와
for (var i = 0; i < segments; i++) { 
      var sw = i >= segments - 1 ? segmentWidth : segmentWidth + 3; 
      var sourceLeft = texw * ((i * segmentWidth)/texw); 
      var sourceWidth = texw * (sw/texw) 
      var texleft = foldX - foldWidth + (i * segmentWidth);     
      var percent = ((i * segmentWidth)/foldWidth); 
      var curve = self.getQuadraticCurvePoint(foldX - foldWidth, 0, foldX, -verticalOutdent * 2, foldX, 0, percent) 
      var curvedheight = self.PAGE_HEIGHT + Math.abs(curve.y*2); 
      var y = -((curvedheight - self.PAGE_HEIGHT)/2); 

context.drawImage(self.flips[flip.index+1].leftcanvas, sourceLeft, 0, sourceWidth, texh, texleft, y, sw, curvedheight); 

    } 

은 다음

getQBezierValue : function(t, p1, p2, p3) { 
       var iT = 1 - t; 
       return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3; 
     },