飴屋

Flash3D/AGAL実践編 Part2

漫画のようなシェーダー

もうひとつ、ちょっと変わったシェーダーをAGALで書いてみようと思います。自分はよく漫画を読むのですが、漫画のような画面がFLASHで描画できたら面白いと思います。しかし、漫画の画面はしばしば3Dから2Dへのデフォルメ表現が変換行列を一つ掛け合わせたぐらいじゃ表現できないことがしばしばあります。パースを正確に取る漫画家さんもいらっしゃいますが、多くの場合、注目させたい部分を大きく描いて強調するため、複雑な座標空間を構成します。光を屈折させるブラックホールのようなオブジェクトやレンズがいくらでも変形するカメラオブジェクトを実装すれば解決するかもしれませんが、今のところその勇気はありません。

続いて、漫画の画面を構成するものといえば、主線と呼ばれる描線です。これは入りと抜きと呼ばれるカリグラフィー独特の表現を省略してしまえば、いわゆるトゥーンレンダリングと呼ばれる手法を使って、モデルオブジェクトとそれ以外のオブジェクトの境界に線をひくことができます。よく使われている手法は、ポリゴンの法線が視線と垂直に交差する場合に線の色で描画する方法とか、モデルオブジェクトを包むような一回り大きなオブジェクトのポリゴンを反転して、内側の片面だけ線の色で描画する方法があります。どちらも意図するような描画に調整するのが大変だそうです。前者はシェーダーだけでも実装できそうですね。ジオメトリシェーダーがあったら、後者もあるいは・・・。ポストプロダクション的に、Zバッファを微分してもそこそこきれいな線が出るかもしれません。

主線で囲んだ部分を塗りつぶすベタと呼ばれる要素は、シェーダー的には単色で塗りつぶすだけでいいので簡単そうです。そして、同じく領域の質感を表現するためにスクリーントーンと呼ばれる印刷物のシールが貼られることがあります。最近の漫画では使われていないことの方が珍しいですね。今回はこのスクリーントーンのような効果をシェーダーで生み出してみたいと思います。

スクリーントーンとは

一口にスクリーントーンといっても種類がたくさんあるようです。

アミトーン点が整列して印刷されている
グラデーショントーン点の密度や大きさでグラデーションを表現している
砂トーン点が一定度のランダムなサイズでランダムに配置されている
柄トーン特定のパターンがシームレスに印刷されている
効果トーン集中線やベタフラッシュのような効果線が予め印刷されている
背景トーンよくある背景の絵が予め印刷されている

スクリーントーンの可否についてかつては漫画界でいろいろな論争があったそうです。突き詰めて考えるとキャラクターまでスクリーントーンになったら、作画の必要がないので、誰でもレイアウトとセリフを考えるだけで漫画がかけそうです。それが面白いかどうかは別の問題ですが。

今回、シェーダーで表現するのは代表的なスクリーントーンであるアミトーンにしようと思います。

アミトーン

アミトーンは元々設計図などの製図で、特定の部分を強調するために使われていたそうです。印刷が単色刷りの場合、色を塗るわけにはいかないので、代わりに点群の濃淡で強調表現をしていたわけです。それを漫画家さんが応用して、流行して、浸透したとのことです。印刷を前提としているので、印刷機の解像度に合わせて点の数が決定されます。また、色の濃淡は点の大きさで決定されます。

一般的に「61番トーン」と呼ばれるものが好んで使われるらしいです。スクリーントーンには種類に応じて番号や記号がついていて、メーカーによって同じトーンでも番号が異なることもあるそうなのですが、61番と言えば大抵の漫画家さんはピンとくるようです。61番という数字にはちゃんと意味があって、「55線」の「濃度10%」を指すらしいです。55線というのは1インチあたりの点の数が55個ということで、濃度10%というのは1インチ四方あたりで黒い点が占める面積の割合ということです。同じ55線のスクリーントーンは60番台のラインナップで、濃度が20%なら62番、30%なら63番となっているそうです。

アミトーンを使う上で注意する点が、原稿に貼り合わせるときの角度があるそうです。アミトーンは点が整然と並んでいるため、重ね張りする際に角度が不揃いだとモアレと呼ばれる干渉縞が発生して見苦しくなるとのことです。このモアレという現象は一定パターンの事象を量子化するときにもよくおこるので、デジタル制作の現場でもおなじみの現象です。デジカメで縞模様や格子模様やテレビを撮影するときにも、肉眼とは異なるものが写ることがありますが、あれも似たようなものでしょう。

というわけで、アミトーンをシェーダーで表現するために「線数」「濃度」「角度」という3つのパラメーターを受け付けることにしようと思います。

アミトーンの実装

パラメーター

  • lpi:Number = 線数
  • density:Number = 濃度(0 < density < 0.785)
  • angle:Number = 角度(degreeの方)

頂点バッファ

頂点バッファに必要な情報は座標の位置だけです。バーテックスシェーダーのva0に座標をセットします。

定数

バーテックスシェーダーの定数vc0には透視射影変換行列が入っています。自分で作ったライブラリではvc0には常にこれが入ってます。

フラグメントシェーダーの定数には一つのベクタ(fc0)と一つのマトリックス(fc1)を用意しました。

fc0 = Vector.<Number>([0.5, density * 4 / Math.PI, 0, 0]);
var rad:Number = angle * Math.PI / 180;
fc1 = new Matrix3D(Vector.<Number>([
Math.cos(rad)*lpi*aspect, -Math.sin(rad)*lpi, 0, 0,
Math.sin(rad)*lpi*aspect, Math.cos(rad)*lpi, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]));

fc0.xの0.5は点の中心を決定するのに利用します。fc0.yは点のサイズに関係するものなので、濃度パラメーターを元に点の半径にあたる数値を計算して設定しています。
fc1はスクリーントーンを回転させたり、拡大縮小する行列です。lpiは線数ですので、線数の分だけ拡大変換をしていることになります。線数が多いほど拡大することになるので、1インチに入る点の数は減りそうに思われますが、そうはならないのです。

aspectという変数が出てきてますが、これはStage3Dの縦横サイズのアスペクト比です。透視変換行列で射影された座標が画面のサイズに応じて変化するので、その変化を吸収するために掛け算しています。

バーテックスシェーダー

m44 vt0, va0, vc0
mov op, vt0
mov v0, vt0

バーテックスシェーダーのAGALでは、アウトプット(op)に透視射影変換行列をかけた座標を出力すると同時に、同じ情報をフラグメントシェーダーに渡しています(v0)。

フラグメントシェーダー

m44 ft0, v0, fc1
div ft0, ft0, ft0.z
frc ft0, ft0
sub ft0, fc0.x, ft0
mul ft0, ft0, ft0
add ft0.x, ft0.x, ft0.y
div ft0.x, ft0.x, fc0.y
div ft0.x, ft0.x, fc0.y
mov oc, ft0.x

フラグメントシェーダーのAGALでは、バーテックスシェーダーで計算した各ピクセルの座標位置に回転拡大縮小行列(fc1)を掛けます。XY座標を回転して拡大したら、次にXY座標をZ座標の数字で割り算します。透視行列変換の結果、近くのものが大きく遠くのものが小さくなっているので、その効果を打ち消すような意味があります。

続いてfrcというOpCodeでXY座標の整数部分をなくします。XY座標が(0.0〜0.9999)の小数値の連続になってやっと特定のパターンを描画する土壌が整いました。平面上の距離1.0ずつにXY軸に平行の直線をひいて、方眼紙を作るような作業ですね。方眼紙の眼は先の行列によって回転縮小された結果が得られています。

XY座標と0.5の差を取って二乗し、XとYの値を足すと方眼紙の眼の中央からの距離が各ピクセルに対して得られます。これで既に円っぽいものが描画されますが、円のエッジのコントラストが不明瞭であり、また円のサイズを濃度パラメーターで制御するという目標があるので、最後にfc0.yの二乗で除算してあげます。これできれいに白と黒のコントラストがつくようになるはずです。(fc0.yは逆数の二乗を入れておいて掛け算した方がよかったと書いていて気がつきました。)

#swf(/images/2011/12/11/tone.swf,500,300)

サンプルでは一枚の板ポリゴンがぐるぐる回っているところにスクリーントーンを張ったものです。テクスチャを張るのとは違って移動してもトーン自体は変形しません。これを応用して、グラデトーンを作ったり、柄トーンを作るのもそんなに難しくないと思います。

Date: 2011/12/11

Flash3D