2012年5月16日水曜日

Canvasを使って知識ゼロから独学してみた「エセ」テクスチャマッピング入門以前編 | 秋葉秀樹個人ブログ


赤い点をドラッグしてみてください。

あくまで基礎中の基礎なので、もっと詳しい方はこちらのほうがおすすめです。
「2009-02-11 - 最速チュパカブラ研究会」

■画像を3D的に遠近感を出すには?

例えば正面を向いている画像が上を向いているように見せるにはどうすればよいでしょう?
それは、画像を台形にして上辺が狭くなれば、立体的に上を向いているように見えますよね?

今回はcanvas要素を使ってこういった画像を変形させることにします。
しかし、現在仕様が固まりつつあるcanvasの2Dレンダリング方式では、こういった画像に奥行を与える直接的な機能を持っていません。
つまり長方形のカタチから台形に変形させるのは簡単にできないというべきです。
Flash Playerはvar10からムービークリップ等を3D的に回転させたりすることが可能になりましたが、canvasの場合は何らかの方法で実現しないといけません。
(canvas、ではなく、正式には「CanvasRenderingContext2Dというcanvas内のオブジェクト」なんですが、便宜上canvasと言うことにします。)

■canvasでできる変形の基本

画像が3D的に見えるように台形に変形させる前に、canvasでできる変形とはどういうものか、おさらいしておきましょう。

ここでいう「変形」というのは、以下のとおりで、これらは全てcanvasに直接備わっているので簡単に変形が可能です。

しかし、先ほども言いましたとおり、台形みたいに、あるいはもっと不規則な四角形に変形する命令(メソッド、といいます)は直接canvasには備わっていません。
例えば以下のような台形や不規則な形状の変形は簡単にはいきません。

よって、直接できないこれらの変形は次の方法を使うことで実現できます。

■2つの三角形に画像を割って個別に変形させる

1枚の長方形を台形に変形させるには、左上と右下で三角形を作って2つに割ってしまいます。
三角形はマスク領域となり、左上と右下のそれぞれの三角形にクリッピングマスクするように画像を処理していきます。


空間と幾何学はどのように関連していますか?

なぜこういうことをするかというと、2つの三角形にそれぞれ別の変形を与えると、おおよそどんなカタチの四角形でも作ることが可能になります。
下図では、それぞれの三角形に対し、拡大または縮小、傾斜、移動といった、canvasで簡単にできるメソッドを使って別々の変形を与えてます。

■左上の三角形の画像をまずは変形させてみる

まずは呼び方、座標のルールから決めましょう。
左上の三角形を「セグメント1」、右下の三角形を「セグメント2」と呼ぶことにします。
セグメント1、セグメント2の頂点は当然それぞれ3箇所あります。(三角形ですから)
この中で2つのセグメントは2つの頂点を共有しないといけません。
どこでしょう?
右上と左下ですね、これがくっついているべきですよね?
便宜上、ポイント0を左上、ポイント1を右上、ポイント2を左下、ポイント3を右下、
と、呼ぶことにしましょう。
ここまでが呼び方と座標のルールです。
そして今回使う画像のサイズは横400px、縦300pxとしましょう。

先ほど、canvasで簡単にできる変形として、拡大または縮小、傾斜、移動などがあると言いましたが、実はこれらを一回で行うメソッド、今回はsetTransformというメソッドを使います。

こちらにて解説されています。

計算方法は基本的な算数なので、苦手な僕でも何とか理解できました。
では実際画像を描画してみましょう。

セグメント1の「変形の基準点」、つまりどこを中心に変形していくかを決定するのはとても重要です。
これができないと、カタチ自体は変形できても、三角形の外にはみ出したりします。
セグメント1の「変形の基準点」はポイント0の位置にします。
その指定のやり方は、setTransformの第5,6番目の引数に、ポイント0のXとY座標を指定します。
例えば、画像のポイント0(左上)の位置がcanvasの左上からXが80px, Yが40pxだとしたら、
画像のdrawImage(img, 0, 0)にしておいて、setTransform(1, 0, 0, 1, 80, 40)とします。
ここ、大事です。
間違ってdrawImage(img, 80, 40)にすると、変形の基準点がポイント0基準になりません。
drawImageする座標は、canvasの左上の座標にしましょう。
setTransformによって 80px, 40pxの位置にちゃんと移動してくれます。


小さな犬の星座はどこから来るのか
もしもポイント1(右上)が右に動いたら、セグメント1の画像の横方向の拡大縮小率を上げます。
例えば、横幅400pxの画像があり、ポイント1(右上)の座標が右に120px動いたとしましょう。
そうすると、120÷400 = 0.3となり、原寸の1と足して1.3が拡大率になります。

setTransform(1.3, 0, 0, 1, 80, 40)

とすることで、画像の横の長さがポイント0から移動後のポイント1まで拡大されます。
この計算方法を使って、画像の縦の拡大および縮小率も応用することができます、値はsetTransformの第4引数にいれます。

■傾斜に対応させたらセグメント1は完成

セグメント1はあと傾斜させて完成です。
ポイント1(右上)が下に40px動いたとします。
この時点で画像は右下下がりに傾斜しないといけません。
縦方向の傾斜の計算方法はこれだけです。
40(移動距離) ÷ 400(画像の横幅) = 0.1
この結果をsetTransformの第2引数にいれます。

この流れで横方向の傾斜も行ってみましょう、縦方向の傾斜と考えは同じです。
例えばポイント2(左下)が左に60px動いたとしましょう。
横方向の傾斜の計算方法は、移動距離が左(負の方向)なのでマイナスとなり、
-60(移動距離) ÷ 300(画像の縦の長さ) = -0.2
これをsetTransformの第3引数にいれます。

これで変形完了です。

■画像をクリッピングする

三角形にするので、Illustratorでおなじみ「クリッピングマスク」と同じような機能をもつ、clip()というメソッドがあるので、それを使います。
canvasでパスによる図形を描くことができますので、moveToやlineToなどのメソッドで三角形を描いたあと、clipを実行してsetTransformで変形させて最後にdrawImageすると、画像は三角形の形で切り取られた格好になります。
原則、clipメソッドを使うときは、処理の前後にsaveメソッドとrestoreメソッドを使います。
例えばrestoreしないと、clip領域(マスク領域)が解除されません、状態をsaveメソッド実行時に戻すという繰り返しを行いましょう。

ここまでの結果にするには、以下のようなコードになります。


どのような非生物的要因は、 lynxを必要としていますか?
ctx.save(); //描画状態を記憶しておく(※1)
ctx.beginPath(); //パスを描画開始
ctx.moveTo(80,40); //ポイント0の座標
ctx.lineTo(600, 80); //ポイント1の座標
ctx.lineTo(20, 340); //ポイント2の座標
ctx.closePath(); //パスを閉じる、三角形の形が決まる
ctx.clip(); //上記のパス領域をクリッピング領域とする
ctx.setTransform(1.3,0.1,-0.2,1,80,40); //変形を実行
ctx.drawImage(img,0,0); //画像(写真)を描画、すでに変形されている
ctx.restore(); //クリップ領域を解除(※1)
ctx.strokeStyle = "red"; //線の色を赤にする(最終的に不要)
ctx.stroke(); //三角形の「線」を描く(最終的に不要)(※2)

(※1)
サンプルではこのコードが繰り返し実行されるため、クリップ領域も毎回解除しなければなりません。
saveによって、クリップ領域が存在しない状態を記憶しておき、restoreでsave時の状態に戻す作業をします。
この考え方はclipしながらアニメーションを行う際は必須のテクニックです。
(※2)
clipはstrokeして図形の線や塗りを描かないといけないと思われがちですが、closePathした時点でクリップ領域が決まるので、stroke(線)やfill(塗り)をする必要はありません。

セグメント1完成

↓↓このボタンを押して、クリップ状態と非クリップ状態を見比べてください。

■セグメント2の作成で気をつけておきたい点

セグメント2も基本はセグメント1と一緒です、応用は利きます。
ただ、大きく注意するべき点が1点、それは「変形の基準点」をどこにするか?です。

先ほどのセグメント1は分かりやすく、左上が基準点になれば良かったんですが、

『セグメント2にとってポイント0(左上)の座標は全く意味がない』

ということです。
セグメント2の持つ座標はポイント1から3までです。
ポイント0がどこに行こうが、セグメント2の三角形は影響を受けない(変形しない)というわけです。

要するにセグメント2になったら変形の基準点を別のどこかに指定し直さないといけないということです。

一番分かりやすかったのが、ポイント2(左下)の位置を基準として、そこから伸縮や傾斜を行おうと思います。
よってsetTransformの5,6引数目には、ポイント2のXとY座標をいれるのですが、ここでdrawImage(img, 0, 0)にすると、ポイント2から画像が描画されるので、ポイント2の下に画像が配置されます。
なので、drawImageの第3引数のY座標を画像の高さ分上に配置します。
つまり、今回の画像の高さが300pxなので、drawImage(img, 0, -300)とすることで、画像の左下が基準点にぴったり合った状態になります。


これまでが、セグメント1との基準点を考える上での違いです。

■セグメント2完成

↓↓このボタンを押して、クリップ状態と非クリップ状態を見比べてください。

■セグメント1とセグメント2を表示

さて、いよいよ結果を見ることができます。
セグメント1とセグメント2にそれぞれ別の変形を与えて表示させてみましょう。

これを見てどうでしょう?
満足しました?
僕はとても不満というか残念です、なんでしょう?この継ぎ目。
各ブラウザともどうやらこのような仕様になっているようで、クリップの痕が残ってしまい、継ぎ目に隙間が出ます。
三角形を継ぎ合わすだけでは解決できません。

ここは一つ、簡単な方法で解決してみましょう。
アイデアとしては、セグメント1のクリップ領域を、三角形ではなく、四角形にしようという単純なものです。
つまり、セグメント1の『左上→右上→左下』だった三角形を、『左上→右上→右下→左下』にして、四角形にしてしまい、上にセグメント2を「乗せる」ように見せて隙間をなくすという方法。

ただし、セグメント1は無駄な領域までレンダリングしてしまうので、パフォーマンス的に良いかどうかは、実用レベルだと別な方法も検討しないといけないかもしれません。

■アプリ開発のために使いどころとして学んでおきたかった

こういったことをやってくれるライブラリはあるんですが、HTMLをベースとしたPCやスマートフォンの描画系アプリケーションの開発のためには、原理くらいはある程度おさえておいた方がいいかな?と思ってたんですが、結構難しいですね。

何でもライブラリに頼ろうとすると、案件仕様によっては実現困難になりそうなので、画像を変形させる基本中の基本だけでも学んでおきたかったわけです。

もちろん、このままでは何にもなりません。
今回は一つの画像を2個の三角形で分割しましたが、例えば、画像を布やゴムのようにグニャグニャにしたり、扇形にしたり、3D形状にしたり、となると、今回の写真をもっと多く分割していかないといけません。
すると、今回の方法から違う計算方法などを使って実装しないといけなくなるかもしれません。
とにかくまだまだ勉強することはあるってことで。

以上、夏休みの自由研究でした。



These are our most popular posts:

A4 基本図形を操る

ドラッグで作る方法の基本的手順は、目的の図形の上で左クリックした時に表示される 四隅四辺の□を、反対側或は対角側にある□を越して ... 図A412-1 →の方向に ドラッグ 図A412-2 図A412-3 右辺の□を超える 図A412-4完成 ... 三角形、 平行四辺形、台形など基本図形を貼り付けた時の形と異なる任意の形を作る手順を 説明します。 read more

[CSS]正方形・ダイアモンド・台形・三角形・円・ハートなどを作り出す ...

2011年6月6日 ... 画像を使用せずに、div要素一つのみで正方形・ダイアモンド・台形・三角形・円・ハート などさまざまな形状を作り出すスタイルシートを紹介します。 チートシートのキャプチャ. CSS3 Simple Shapes – Cheat Sheet. 元記事では各スタイルシート ... read more

三角形と台形の面積

まず,三角形ABCの2倍の面積をもった平行四辺形AFBCを作ります。目的は,平行 四辺形AFBCは長方形DEBCと同じ面積になることの説明です。 図3. つぎに,三角形 FEBを矢印のように移動します。 図4. すると,平行四辺形AFBCの面積ぶんだけ ... read more

web帳

CSS3 三角形 ひし形 等の作成方法. Categories: CSS3. 前回の. CSSで図形 三角形 &台形. の応用で、二等辺三角形、ひし形を使い色々な図形を作成します。 ... 同様に 上向き、右向き、左向きの三角形は、向けたい方向(逆のボーダー)に#000 50px solid を ... read more

Related Posts



0 コメント:

コメントを投稿