UIで扱うシェーダーは、PBR(物理ベースレンダリング)や VFX、ポストエフェクトに比べると、地味でささやかなものが多くなりがちです。ゲームのUI表示は半透明が基本なのと描画順によるところがその主な理由です。
シェーダーを使う=なんかスゴイイイカンジノミタメ になるというより、実装および調整コストを下げる、テクスチャのクオリティを下げずに能率的な使い方ができる、というところにフォーカスしつつ、UI開発の面白いと思える部分が少しでも増えたらいいなと、この企画を進めてきました。
ゲージに絡めたものがほとんどですが、UI表現で使う基礎的な技術は結構紹介できたと思ってはいるので、工夫と応用で次第では、面白いトランジションや見せ方を作ることができます。
今回のネタは、ちょっと味変的で、こんなこともできるんだという例として取り上げてみました。
最近は 3本ずつくらいのペースで挙げてきましたが、今回はバリエーションが思いつかなかったので 1本だけです。
95. 小数を整数にしてカウント表示する

GIFアニメでは、数字の下にゲージがありますが、これは飾りです。
で紹介した 5. 背景をつける と同じものです。
メインは 数字部分 です。

ゲージの増減はふつう 0~1.0 で管理します。
0~1.0 の範囲を『割合』として、色を塗り分けて表現することで視覚化します。
UV空間に面積があるのでこれを利用する方法が主になります。
あくまでも応用の範囲で「数字」に置き換えます。
やることは以下の2つ。
- 値を桁ごとに分解する
- 値によって数字が変わる
ここに大変重要な要素である数字のテクスチャが加わります。
32 x 512px
一文字あたりの高さは 44px で、0~9全部で 440px。縦の解像度をべき乗の 512px にしたので、下のほうは余白になってます。
左右に何もないのがポイントで、タイリング設定は Clamp です。

何度か取り上げていますが、この Clamp の理解はテクスチャを効率よく作るうえで重要です。
このテクスチャをサンプリングするときに、UVに掛け算します。
U方向(横)は3桁を想定しているので、3.0
V方向(縦)は 1文字の高さが 44なので、 44 ÷ 512 = 0.0859375
下図では、Constant2Vectorノードを利用して掛け算しています。

数字の [ 2 ] キーを押しながら左クリックでも取り出せます。

Y のところに 44/512 と入力して [ ENTER ]キーを押すと計算結果に書き換わります。
小数第 6位 で丸め(四捨五入)られますがこれはエンジンの仕様です。気になるようであれば、1文字の高さを 44px から 48px に変えると 5桁で割り切れるので 丸められることはなくなります。
U方向に 3倍することで、Clamp設定が活きてきます。

今回3桁表示ということで、TextureSampleノードを 3つ使って重ねます。

それぞれ 3つの桁を取り出す方法を順番に書いていきます。
まずは左端から。
ここは100の位です。 今回 最大の数字は 100なので ”1” 固定です。
1.0(満タン)の時だけ表示します。


Stepノードを条件分岐のように利用します。
Y を 1.0 とすると、
X が 1.0 のとき 1 が、1以下の時は 0 が結果として取り出せます。
次は 10の位の数字をゲットする部分。
10の位は、小数第一位の数字になります。
例えば 0.6789 だと 6 にあたります。
まず 10を掛けると、小数点が右にずれるので 6.789 になります。
これを Floorノードで切り捨てると 6.0 になります。
数字テクスチャの数字1つぶんの高さは 44/512 で 0.0859375
6.0 と掛けると、テクスチャの V方向のオフセットが求まります。
6.0 * 0.0859375 = 0.515625
44 * 6 = 264
264/512 = 0.515625

ノードにすると下のようになります。

1.0 のときに満タンで、ほとんどは 2桁です。1.0以下は問題ないのですが、1.0の場合だけ、10をかけると数字の 9 の次である 10 の位置になってしまいます。
テクスチャは 9までしか用意していないので、 Fracノードを挟むことで 1.0 を 0.0 にしています。Fracノードは強制的に整数部分を切り捨てます。
テクスチャに余裕がある場合、 01234567890 のように ゼロから始まってゼロまで並べることができれば、 Fracノードは外せます。
最後に 1の位の数字をゲットする部分。
1の位は、小数第二位の数字になります。
例えば 0.6789 だと 7 にあたります。
10の位と同じ要領で、いったん 100を掛けて 67.89 にします。
これを Fmodノードを使って 10で割った余り(剰余)を求めます。
小学校の時に習った言葉で書くと
商は 6
余りは 7.89
ということになります。
Windowsの電卓で求めるときは、関数電卓に切り替えて、 右のほうにある mod ボタンを使います。

これは modulo の略で、スクリプトでは算術記号として % が使われているのをよく見ます。
シェーダーは基本的に浮動小数なので、Float の F がくっついて Fmodになるようです。
10の位がいなくなるので、 7.89 になります。
この小数部分を切り捨てます。Floorノードではなく Truncateノードを使ってみました。
正の値(0以上)では Floor と Truncate では同じ結果になります。

10を掛けてFloor、に対して 10を掛けてFloor、に対して... を繰り返すと、Fmodノードを使わなくても桁をずらしていくことが可能ですが、冗長になるので今回はやめました。
これで桁をバラすことができました。
各桁のV方向は計算できました。残るは横方向。
これは最初に3倍しているので、桁ごとに -1 ずつずらします。
以下のようにイメージするとよいかもしれません。白い枠が表示範囲。

マイナス方向に移動すると、テクスチャの見た目は右に移動します。
UとVを MakeFloat2ノードで合体します。AppendVector でもOK。

この UV を

これと加算して、
それぞれのTextureSampleノードにつないで合成します。

ピクセルが 重なったりしないので、Addノードを使って合成しています。
これで完成です。
ゼロサプレスはしていないので 0 ~ 9 の時は 00 ~ 09 となります。
満タンの時だけ、100の位を表示したりしなかったりしているのに10の位もやった方がよくない?という場合は以下のように Step と Multiply を差し込みます。


GIFアニメだと引っかかるところがありますね。
サンプラーを複数使って重ねるので、面積をあまり大きくしないほうがよいです。
ゲージが主役で脇役として控えめに配置するのに向いています。
パーセントで表記するような場面。シェーダーウォーミング画面とかローディング画面とか、単にデバッグ用とか。
おまけ
デバッグ用途の場合は、テクスチャをうんと小さくして、エンジンにインポートした後のフィルタ設定を Nearest にすると、大きく表示したときにボヤケないのでそれっぽくなります。
8 x 128px

今回は節約したくてグレイスケールなテクスチャですが、
アルファチャンネルを追加するとフチドリをつけることもできます。


今回は以上です
0~1 の小数 を 0~100の整数にする方法でした。
ゲージの増減で 0~1 を使っているところに、あとから 同じ 0~1の値を数字にして見てみたい、というような激レアなシチュエーションでしか使い途がないと思います。
シェーダーでは小数が基本なので、そろそろ慣れてきたのでは?ということで取り上げてみました。
ではでは
素敵なゲージライフを!