4

ここで説明する「キーストーン」テクニックを使用して、html5 キャンバスを使用した「ハード カバー」の本のページにパースペクティブを追加しました。

基本的に起こることは、イメージ/キャンバス要素がソース テクスチャとして定義されることです。各レンダー ループ中に、これは定義された幅のセグメントに分割され (1 ピクセルが最高品質)、各セグメントの高さは、テクスチャの X 軸上の位置に応じてスケーリングされます。

これにより、以下に示すように、特定のソース イメージ/テクスチャで適切な遠近法の錯覚が作成されます。

ここに画像の説明を入力

これは、「ハード カバー」のページめくりでは問題なく機能し、含まれるテキストと一緒にテクスチャが素晴らしい印象を与えます。ただし、同じ種類のキーストーンを「ソフト」なページめくりに適用する必要があります。問題は、以下に示すように、ページ自体が曲線で定義されているため、単純な透視変換が機能しないことです。

ここに画像の説明を入力

現在、ページ テクスチャは、画像/テクスチャをクリップするように設定されたページ エッジ二次曲線パスを使用して、ページめくりの任意の時点で曲線の最大高さにスケーリングされます。さまざまな標準キャンバス関数を使用して影とページ ラインを動的に描画するので、ページ ライン (2 次曲線で描画) がページめくりに自然な遠近感を提供するため、これは許容できるように見えます。

ただし、テキスト自体 (別のキャッシュされたキャンバス/画像要素から取得されたもの) は見栄えがよくなく、常に完全にフラットなままです。

私がやりたいことは、上記と同じスライシング/セグメンテーション/スケーリング キーストーン テクニックを何らかの形で適用することctx.quadraticCurveTo();です。

私の例の画像では、実際にはそれほど悪くはありませんが、テキストがページの上部/下部に近づくと、ワープ効果はもちろん大きくなります. それだけでなく、ページの折り目に最も近いテキストを押しつぶすために、水平方向の倍率も計算する必要があります。

残念ながら、サンプルコードを提供する立場にはありません。しかし、基本的には、上記のリンクで説明されているキーストーン処理と非常によく似た方法で行っています。要約すると、二次曲線の座標/値を使用して、以下に示すスライス/レンダリング関数の各セグメントのスケール係数を計算できるようにする必要があります。

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キャンバスで定義された二次曲線に基づいて、各セグメントの高さスケールとy位置を簡単に変更できる回答に満足しています。

より良い例の画像を次に示します。

ここに画像の説明を入力

ご覧のとおり、ページめくりに自然に追従するために、曲線係数とその y 位置によって 1 ピクセル幅のセグメントを計算する必要がある場合、大きなテキストは、キーストーン化されていない単一の画像テクスチャで完全にまっすぐになります。この場合、半現実的な遠近効果を達成するための他の「ハック」または方法に失敗すると、非常に役立ちます。

4

1 に答える 1

4

私はついに解決策を見つけました....

ここでの素晴らしい答えのおかげで:html二次曲線の中心点

二次曲線上の任意の点のy値を取得するために必要な方程式がありました。

次に、このy値を使用して、テキストテクスチャの高さの調整を計算するという単純なケースでした。最良の二次曲線計算を見つけるためにいくつかの調整を行う必要がありますが、結果はそのままでもかなり素晴らしいものです。

下の画像を参照してください。

ここに画像の説明を入力してください

現在、Firefox /Chromeでは約60fps、IE9/10では約45fpsで動作します。最適化の余地はあると思いますが、結果には非常に満足しています。明らかに、カーブに沿ってテクスチャを水平方向に引き伸ばしたり押しつぶしたりすることはありませんが、そうするためには、少なくとも1回、おそらくそれ以上の余分なパスが必要になり、パフォーマンスが低下します。

もう1つのオプションは、実際のAffine 3dテクスチャマッピングに頼ることでしたが、私の試みでは、この方法は、明らかに少しの精度を犠牲にして、パフォーマンスと品質の点ではるかに優れていることがわかりました。

ページめくりのメインレンダリングループ内にあるループは、次のようになります。

    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);

    }

関連する2次点関数は次のとおりです。

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

と:

getQBezierValue : function(t, p1, p2, p3) {
                var iT = 1 - t;
                return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3;
        },
于 2013-02-13T20:07:48.150 に答える