canvas要素にJSでAS3のdrawTrianglesみたいなことをやってみる

trianglejs_demo.jpg

以前HTML5案件で作ったもので、canvasで上の図のようなことができるJavaScriptファイルなんですが、これをライブラリ化してみました。
まだ欠陥が多いしAPI仕様もまとまってないのですが、ひとまずこんな表現が可能です。
ソースはここにあげときました。(使い方よくわかんないけど)
ActionScript 3.0にはGraphicsクラスにdrawTriangles()ってメソッドがあります。
何をするかって言うと、ビットマップ画像をテクスチャマッピングすることが目的です。
これにより一枚の静止画をユラユラ布のように揺らしたり、3Dでは遠近感をつけたりと、画像を自由に変形することが可能になるコンピュータグラフィックの手法です。
FlashPlayerやActionScript 3.0が素晴らしいと思うのが、こういったマッピング処理の数学的な難しい処理をすでにAPIとして実装してくれています。
僕達は、頂点座標を指定して、それをどう動かすか?に専念したらいいんです。
ところが、HTML5の新要素、canvasとかでこれを表現し、アプリなどに乗せてしまうようなお仕事が来た時、「iPadだったらHTML5で出来ますよ」なんて言ってしまったらさあ大変!
そんな機能はネイティブで搭載されていません。
つまり、「無いものは作る」or「仕様を満たすライブラリを探す」or「出来ません、と謝る」という選択に迫られます。
実は色んなライブラリがもう既に出まわっていますが、いざ案件でHTMLによるテクスチャマッピングを実装するには、可能な限り自分で内容を知っておきたいという気持ちがあったので、これに挑戦してみました。
これを使ったら、ポリゴンの頂点座標のx座標やy座標をずらすと、画像もその頂点にあわせて変形できます。
頂点座標の取得はgetMesh()で、頂点オブジェクトが配列で返ってくるので、例えば


インスタンス名.getMesh()[41].x += 40; // ------------- 頂点座標の移動

インスタンス名.draw(); // ------------- 再描画実行

とかにすると、左上の頂点から41頂点目のX座標を40px右にずらします、
最後にdraw()メソッドで再描画すると、図のように表示されるというものです。
getmesh.jpg
メッシュの表示、非表示
あくまで、今出ている黒い線(メッシュ)は、制作者が分かりやすいように引いているガイド線のようなものなので、案件で納品するときは消さないといけないケースがあります。
以下のようにstroke()メソッドを用意したので、引数にtrue / falseを与えてください。


インスタンス名.stroke(false);

これで再度draw()すると、メッシュは表示されなくなります。(今回のデモ図版では、わざとtrueに設定しましたが、デフォルトはfalseです。)
GitHubに置いた、今回のサンプルは波のように揺らしてますが、これは三角関数を使って、X座標を行ったり来たりさせてます。メッシュ非表示にするためにstroke(false)にしたときの結果、こんな感じになります。
chibikoro_wave.png
欠点として、一部、ポリゴンの隙間が出来ることもあるということや、
ふちはジャギーが出て汚い、ということです。
場合によってアニメーションで見せる場合、そこまで気がつかずごまかせることもあるけど、
その場合かなりパフォーマンスが低下するので、注意が必要です。
何か案件で使えそうな時は自己責任でご自由にお使いください。

HTML5-WEST.jp 勉強会「HTMLでつくるカメラ&落書きアプリ」フォローアップ

2011-09-24 15.11.25.jpgのサムネール画像

始まって2回目のHTML5-WEST.jp勉強会なんですが、50人も集まりました。

みなさんお疲れさまでした。
僕は「HTMLでつくるカメラ&落書きアプリ」というセッションでお話をさせてもらいました。

スライドはこちらです。

セッションの中でお話しましたけれど、まだ現在では、HTMLやJavaScriptベースでカメラを使ったWebアプリは作れません。
これは各ブラウザがまだこの機能をサポートしていないからです。
ただ、テストとして唯一Operaの特殊なビルドがそれを可能にしています。
Webの未来という意味では面白い試みだったと思います。
もうすでにW3Cのワーキングドラフトに上がっているので、「Webでこういったことが将来出来るようになる」ということを示唆するものなんですね。
1行のHTMLと簡単なJavaScriptのサンプルでカメラアプリが作れるデモを実際披露すると、色んな人の反応が多くて楽しかったですね。
getUserMedia()というJavaScriptのメソッドは、カメラやマイクを取得する機能ですが、仕様はまだあやふやでこれからもう少し整備されていくでしょう。
最後にアイデアネタとして「変顔カメラアプリ」として画像がウネウネ動いていたのは、実はとても頑張って作ったんです。
将来はもうちょっとアレをアプリっぽくしますのでご期待ください!!
スクリーンショット(2011-09-24 17.34.21).png
デモでお見せしたテクスチャマッピングによる画像を変形させて撮影

『第一回 全国統一HTML5実力テスト』を受験してみて改めて感じたこと

カヤックさんのWebサービスで、HTML5関連の自己スキルが試せるというものが公開されています。
html5cat.png
これはエンジニア向けのJavaScriptコース、そしてデザイナー向けのHTML / CSSコースの二つの科目(?)があり、それぞれ好きな方を選べるし、両方受験することもできます。
まあ、内容はもちろんここでは書けないけど、結構難しいことから、割と誰でも知ってそうなことまで幅広いので、自分のスキルを試すということで、やってみてはいかがでしょう?
高い点数だったら、みんなに自慢しちゃおう、ということでTwitterのアカウントでログインして受験してみるのもよいでしょう。
逆に点数が低いからといって、一方的に公開されるわけではありませんのでご安心を。
ちなみに僕はJavaScriptは専門外なので、HTML / CSSコースだけ受験。
惜しくも93点。
一問間違えてしまいました。。
よく考えたら分かることを、、、悔やんでしまいます。
score.png
HTML5がテーマとなる試験なので、セマンティックな考え方を理解しておいた方がよいですし、CSS3の知識も必要になってきますし、JavaScript APIとの絡みも理解が必要になってきます。
と、書くと、「何でJavaScript APIとの絡みが必要なん?」って思うかもしれません。
しかしこれは僕としては当たり前のことと思い、苦手だけど勉強中です。
(オライリーから出ているJavaScriptのパターン本を読んで、確かに面白いですが、これは僕がやる分野じゃないな、と思っています。)
ただプロジェクトって、制作に関わる人全てが同じゴールを見ないとチグハグなものが出来上がってくるのにこの日本ってのは、フロントに関わる大事な作業をする人がディレクターからの指示だけで動いているという現状があまりにも多く、ヒドい場合には途中で大幅な仕様変更などに何故そうなったかも理解出来ず、苦しむ場合があります。
「上が言ってるからそれは当たり前」とか「徹夜してでもやるべき」とかそんな言う人もいるけど、もの作りの本質からは確実にずれている気がします。
ただ、大きなプロジェクトになれば(何十人、何百人のエンジニアが動くプロジェクト)になると、さすがにそうも行かないケースもあるので一概には言えませんが。
少なくとも数人体制で制作にかかる場合、もっとフロント制作に関わる人(フロントエンドエンジニアやデザイナー)も会議に参加させて、その人のことをちゃんと信用していこうよ。
フロントに関してはその人の意見が一番知識があるし、的を得ているはずなのに、何故違う人が仕様を決められるのか?は問題として認識しておくべきかな?と。
理想的な話ばかりしてるけど、制作者は全てコミュニケーション能力がないとダメ。
上記のように、フロントエンドの人やデザインの人などは、信用されている以上、プロジェクト全体のゴールを見据えて話が出来て提案も出来ないと、ただの言いなりにしかなれない存在なんです、悲しいかな。
だから僕らの立場を5年先、10年先でももっと価値のあるものにし、言いなりにならずに「この人にお願いしたい」と言われたいので、今が訓練の時期だと思う訳です。
だから話が戻るけど、デザイナーの知識にとって「何でJavaScript APIとの絡みが必要なん?」という答えは、自分が作ったUIと密接に関わるからです。
自分がUIを設計する以上、最後はプログラムによってどのように動くかなんて知っていて当然、その挙動が予測できるようにならないといけません。
少なくとも、スクリプトを書けなくても、何が出来るか?
ここを最初に設計/定義しておかないと後々困る、という部分だけは抑えておきましょう。
これが今後、複雑な仕様になって広大なHTML5の技術に対する、僕らデザイナーの向き合い方だと考えて勉強しています。
こう考えると、色んなスキルが必要となってくるので、ひと独りでこのスキルを理解出来る人なんていないんじゃないかと思ってきます。
JavaScriptエンジニアと言っても、今までDOM操作が得意だったエンジニアだけでなく、描画アプリをcanvasやSVGなどを使って作ることを得意とするエンジニアやAjaxやWebSocketなどの通信系が得意なエンジニアとか色々、エンジニアのなかでも幅広く分野が分かれていくものと予感しています。
これはあくまでディレクターとしての一つの意見ですが。
デザイナーもそうです。
ビジュアルを担当するデザイナーから、ユーザの指や目の動きを考えたり使いやすい設計を考えるデザイナーなど、デザイナーといってもWebに関わらずプロダクトやエディトリアルデザインの世界でもいっぱいいます。
どれがWebの世界で一番重要かだなんて順位をつけられるわけがありません。
見た目も機能性も、全てにおいてクオリティを保たないとそれをクオリティとは言わないからです。
さ、そんなわけで、コレからも勉強、勉強、と。
お互いいい仕事をしたいですね!

canvasを使って知識ゼロから独学してみた「エセ」テクスチャマッピング入門以前編

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

あくまで基礎中の基礎なので、もっと詳しい方はこちらのほうがおすすめです。
「2009-02-11 – 最速チュパカブラ研究会」
http://d.hatena.ne.jp/gyuque/20090211#1234364019

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

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

【画像(写真)が上を向いているように見せたいとき】

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

■canvasでできる変形の基本

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

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

【これらはcanvasで直接変形が簡単】

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

【これらはcanvasで直接変形はできない】

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

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

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

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

【2つの三角形に別々の変形を与えると、どんな四角形でもつくれる】

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

【必要な座標の情報は4箇所。】

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

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

こちらにて解説されています。
http://www.html5.jp/canvas/ref/method/transform.html

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

セグメント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は完成

setTransformの第2,3引数を変更

セグメント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メソッド実行時に戻すという繰り返しを行いましょう。

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


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形状にしたり、となると、今回の写真をもっと多く分割していかないといけません。
すると、今回の方法から違う計算方法などを使って実装しないといけなくなるかもしれません。
とにかくまだまだ勉強することはあるってことで。

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