
ポケコロツイン・アバター紹介
-
2021年3月10日
はじめまして、クライアント開発の N です。
今回はポケコロツインで使用されているアバターの紹介をしたいと思います。
以前の記事(ポケコロツインを支える新CCP Engineの紹介)では、Unity + Anima2D を使用して作成されていることをお伝えしましたが、今回はアバターに絞って少しだけその内部構成をボーンとメッシュに分けてお伝えできればと思います。
ボーン構成
まずはアバター素体のボーン構成から。
以下の画像 [ 1 ] のようになっており、首から下は非常にシンプルな構成ですが(ポケコロのレイヤーと同様に膝の関節が無いのが苦しいところでもあり、ポケコロらしいところでもありますが…)、首から上は逆に大量のボーンが配置されております。
これはフェイスパーツに関連したボーンで、ユーザーがパーツ毎に位置や大きさをカスタマイズできるようにする為の物と、さらにカスタマイズした物をアニメーション側から制御できるようにする為の物が含まれています。
フェイスパーツに関連するボーンは次のような種類があります。
カスタマイズ用 ボーン |
アニメーション 制御用ボーン |
---|---|
|
|
( ※1 ) 頰の輪郭制御 1 〜 3 というは、頰を膨らませたり引っ張ったりするような表現に仕様しているボーンです(画像 [ 2 ])。
( ※2 ) 頰の輪郭制御の 3 つのボーンだけ抜き出すと、画像 [ 3 ] のようになります。
( ※3 ) 頰の輪郭については、アニメーションで制御している途中にキャンセルされるケースが多々あるため、下記のように初期状態を保持し、いつでも関数でリセットできるようになっています。
// 初期状態の保持 (C#) checkOutlineInfo = new CheckOutlineBoneInfo[cheekOutlineTargetNum]; for (int i = 0; i < cheekOutlineTargetNum; i++) { if (TryFindChindNodeInBody(cheekTargetList[i], out var target)) { checkOutlineInfo[i].targetBone = target; checkOutlineInfo[i].defPosition = target.localPosition; checkOutlineInfo[i].defRotate = target.localRotation; } }
// リセット関数 (C#) public void ResetCheekOutline() { for (int i = 0; i < cheekOutlineTargetNum; i++) { var target = checkOutlineInfo[i].targetBone.transform; target.localPosition = checkOutlineInfo[i].defPosition; target.localRotation = checkOutlineInfo[i].defRotate; } }
ファッションアイテムのボーンについて
ファッションアイテムのボーンは大きく分けると、アバター素体と同名のボーン(画像 [ 4 ] で白色のボーン)とそれに付随するボーン( 画像 [ 4 ] でピンク色のボーン)の 2 種類で構成されています。
例えば、ボディーは「B」(Body)、左上腕は「ALT」(Arm Left Top)といったような名前のボーンを素体とアイテムの両方に用意しておいて、アバターにアイテムを着用させる際に、同じ名前のボーン同士を付けるといったことを行なっています。
一方、素体と同名でないボーンは、アイテム毎に存在する特に決まりのない自由なボーンで、主にスプリングボーンやアニメーション用のボーンとして用意されています。
[ 4 ] ワンピースのボーン
メッシュ構成
次にメッシュの構成についてですが、ポケコロのアバターとして最も重要な要素となるのは色換えとマルチレイヤーになります。
色換えは、文字通り肌や髪の毛の色をユーザーが自由に変更できるようにする為のもので、マルチレイヤーは、その色換えを行なったメッシュの上や下に別の画像を重ねるのに必要なものです。
特に後者は頂点数の多いメッシュにはメモリ削減という意味でも高い効果が得られます。この 2 つの要素には専用のシェーダーを用意しました。
メッシュの色替えについて
メッシュの色換えについては、グレースケールの画像を用意して頂点カラーで指定するといった方法もあったのですが、グレースケールでは表現できない組み合わせのグラデーションにも対応する為に、2 つの色の値をシェーダーに渡してフラグメントシェーダーにて 2 つの色を線形補間するような仕組みを作りました。
ポケコロツインのアバターでは、前髪の色に合わせて後髪の色を変化させる必要があります。
画像 [ 5 ] のように前髪をハイライト画像とベース色から影色までのグラデーション画像という構成で作成し、後髪をハイライト画像と予め決められた A 色から B 色までのグラデーション画像で作成します。
そうすると、最終的に画面に出力される後ろ髪は、フラグメントシェーダーによって A 色から B 色までの色を、前髪のベース色から影色までのグラデーションに変換します。
[ 5 ] 前髪の例
[ 6 ] 後ろ髪の例
(左がアイテム作成時、右が前髪の色を反映させた状態)
マルチテクスチャ構成 | ||
---|---|---|
手前 | ハイライトレイヤー | 固定色のレイヤー |
奥 | 色換えレイヤー | 2 色のグラデーションレイヤー |
あらかじめ決められた 2 色 ( A 色から B 色まで )を指定された 2 色(前髪ベース色から影色まで)に置き換える処理は、下記のようなフラグメントシェーダーで行っています。
(引数の c1 と c2 が A 色と B 色、d1 と d2 が前髪のベース色と影色に対応します。)
AA_Result checkColorAA () { AA_Result ret; if (c1.a == 0 || c2.a == 0) { ret.isAA = false; ret.dest = o; return ret; } fixed3 le = min(c1.rgb, c2.rgb); fixed3 gr = max(c1.rgb, c2.rgb); bool3 leb = (o.rgb < gr); // lessThan(o.rgb, gr) bool3 grb = (o.rgb > le); // greaterThan(o.rgb, le) ret.isAA = (all(leb) && all(grg)); ret.dest = o; if (ret.isAA) { fixed3 elm = smoothstep(le, gr, o.rgb); // 3次元エルミートhokan ret.dest.rgb = (elm * d1.rgb + (float3(1.0, 1.0, 1.0) - elm) * d2.rgb); } return ret; }
マルチレイヤーの使用例
もう一つ、マルチレイヤーを利用したアイテムとして、髪の毛よりも多くのレイヤーを必要とするアイパーツのアイテム例を見てみます。
[ 7 ] アイパーツの例 1
アイパーツでは、色換えを行なうレイヤーが 2 つと、それを挟むように固定色のレイヤーが 2 つの最大 4 レイヤーまで使用しているアイテムがあります。
[ 8 ] アイパーツの例 2
マルチテクスチャ構成 | ||
---|---|---|
最前面 | ハイライトレイヤー | 固定色のレイヤー |
2 番目 | 色換えレイヤー | 目の色( 2 色)のグラデーションレイヤー |
3 番目 | 下地レイヤー | 固定色のレイヤー |
最奥 | 色換えレイヤー | 肌色が反映されるレイヤー |
アイパーツは、髪の毛のアイテムに比べるとメッシュ自体の頂点数は少ないですが、ポケコロと同様にパターン切り換えによるアニメーションを行なうため、マルチレイヤーを利用しないと、(両目 × パターン数 × レイヤー数 = メッシュの数 となるため) メッシュ数がより増加してしまいます。
従って、アイパーツに関してもマルチレイヤー化のメリットは大きいと思われます。
最後に
今回は、ボーン構成とメッシュ構成に分けてポケコロツインのアバターをご紹介しました。
ポケコロのベクター+フラッシュベースのフォーマットと比較すると、Anima2D は一般的なボーンベースのフォーマットなので、様々な技術を取り込み易くなった(自由度が上がった)のではないかなと思います。
以上、ポケコロツインのアバター紹介でした。