TextMesh Proを印字風に加工する【前編】
こんにちは、カキレモンです。急に肌寒くなってきましたね。
今回はUnityのTextMesh Proで遊びます。ちょっと長くなりそうだったので前後編に分けることにしました。前編では主にTextMesh Proの仕組みの理解に努めます。
最終的にはシェーダーを改造してこんな感じのことをします。(実際はシェーダー以外にも若干ポストエフェクトをかけています)
TextMesh ProにおけるSDFとは?
シェーダーを書くにあたって、まずTextMesh Proが文字を描画するプロセスを大まかに知っておく必要があります。その描画方法やシェーダーの書き方については主に以下の記事を参考にします。
この記事でも書かれているように、TextMesh Proの描画にはSDF(Signed Distance Field)と呼ばれる概念が使われています。参考記事だとSDFの説明は淡白めですが、それほど一般的な用語ではないように思うのでここで改めて解説しておきます。特に必要なければスキップしてください。
。:+* ゚ ゜゚ *+:。。:+* ゚ ゜゚ *+:。。:+* ゚
SDFは、ある特定の形を表す手法の一つです。(ちなみに同じ略称で"Signed Distance Function"というものもありますが、ほとんど一緒なのでここでは特に区別しません。)一般的なSDFは、対象図形の輪郭からの距離を各点ごとに計算し、その値に符号(プラスかマイナス)をつけることで図形の内部の点か外部の点かを区別できるようにします。
試しに、半径1の円を表す簡単なSDF(どちらかというとFunctionの方)を考えてみましょう。ここではプラスが外側でマイナスは内側とし、また値が0の部分はちょうど円の輪郭を表しています。
このとき、例えば0を基準にして「d<0の点のみを塗る」ということをすればもともとの形を描画できます。要は、SDFの値を特定の閾値と比べた大小関係が重要になります。
ところでTextMesh Proにおける距離の値の表し方は上図とは少し異なっていて、文字の内部が1で、外側に進むにつれて0へと小さくなるようになっているらしいです。これはおそらくテクスチャの仕様に合わせた結果ですが、いずれにせよ「何らかの閾値で区切る」ことで描画する仕組みは同じです。
TextMesh Proのシェーダーを読む
先の参考記事にならって、TMP_SDF-Mobile.shaderをベースとします。これはプロジェクト内のAssets>TextMesh Pro>Shadersの中に入っています。(TextMesh ProのTextをScene View内で使えばインポートできます)
オリジナルのシェーダーはとても長いですが、今回注目する部分はPixShader関数の最初の方のたった2行だけ(200行目あたり?)です。
half d = tex2D(_MainTex, input.texcoord0.xy).a * input.param.x;
half4 c = input.faceColor * saturate(d - input.param.w);
ここでdはSDFから取り出した「距離」の値であり、cが文字を塗るピクセルの色になっています。
もう少し詳しく見ると、SDFテクスチャから取得した0~1の値をinput.param.x倍したものがdで、それに対しinput.param.wを閾値として色を塗るかどうかを決めています。つまり、
dが十分大きいとき:ピクセルの色はinput.faceColor(塗られる)
dが小さすぎるとき:ピクセルの色は0(塗られない)
ちなみにsaturate(x)はxを0~1の範囲に抑える関数です。max(0, min(1, x))と同じ意味らしい?
実験:極太にしてみよう
前編の締めくくりとして手軽な改造を試してみたいと思います。
単純にdを大きくすれば塗られる範囲が大きくなるはずなので、とりあえず2倍にしてみます。
half d = tex2D(_MainTex, input.texcoord0.xy).a * input.param.x;
d *= 2; <- 追加
half4 c = input.faceColor * saturate(d - input.param.w);
結果:
文字が極太になりました。確認できたら一旦コードは元に戻しておきます。
雑な改造なだけあって少し不格好ですね。dの値の増減ををピクセルごとにうまく制御すればもうちょっと上品な加工ができそうです。
つづく
つづきます。後編ではもう少し細かい実装をして、最初に見せたツイートと同じことができるようにします。
この記事が気に入ったらサポートをしてみませんか?