前回記事 を読んでいない方は、まずそちらに目を通しておきましょう。
よければ以下へどうぞ。
[問題]
「PMX概要」 動画内には間違った記述があります。それは何でしょうか?
[解答]
頂点-最大頂点数 : 65536(16bit) → 40億(32bit)
はい、ここが間違いの箇所です。
「32bit数の最大値は 10進数で 4294967295 になるので 40億ではなく約43億の間違い!」
...というように考えた方もいらっしゃるかもしれませんが、残念ながらそのような細かな違いではありません。
では実際の PMX仕様の当該箇所を見てみましょう。
----
■データフォーマット
(中略)
※データ数の範囲とバイトサイズの関係 - 頂点 8/16bitのみ符号なしとして扱います。
・頂点 - 符号なし
[バイトサイズ] : 頂点数範囲
1 : 0 〜 255
2 : 256 〜 65535
4 : 65536 〜 2147483647 ※符号ありになるので注意
・ボーン/テクスチャ/材質/モーフ/剛体 - 符号あり
[バイトサイズ] : データ数範囲
1 : 0 〜 127
2 : 128 〜 32767
4 : 32768 〜 2147483647
----
数字がずらずらと並んでいますが、頂点は上段の方。左端のバイトサイズ 4 が 32bitに該当します。
書いてある数字の最大値は 2147483647 となっていますね。もし最大頂点数が 40億の場合はこの数字が 4294967295 となっているはずです。
こうなる理由としては注釈にある通り、バイトサイズ4の場合は頂点も "符号ありの数値" として扱うためです。
つまり 31bit数の最大値となるわけで "(約)20億" という数字が PMXにおける最大頂点数ということになります。
実際のところは 20億だろうが 40億だろうが、そんな数の頂点数を取り扱うことは現状では不可能なので、こんな仕様上の制限を知っていても大して役には立ちませんけどね。
※なので特に訂正などはせず、ずっと放置のまま
1bitという [0/1] たった 1つ分の違いが、現実の値としては "20億以上もの差" になってしまう...みたいなあたりに驚いて頂ければ?幸いです。
以上、PMX(頂点数) における "1bitの違い" でした。以降は関連の解説を少々続けておきます。興味のある方はどうぞ。
■ 符号とは
+(プラス)/−(マイナス) が符号です。計算機内で取り扱う数字はデータ(箱) の大きさが決まっており、中に入る数字には制限範囲が付きます。
符号で 1bit使うことから符号の有る無しにより (正の)最大数は倍違う結果となります。
まあちょっと詳しい方なんかは 「整数の負数は 2の補数表現になっているから符号で 1bit使っているわけではない!」 みたいな指摘をしたくなるのかもしれませんが、情報量から見た場合はどのような表現になっていても "1bit分の情報" になっているだけなので、結局同じことになります。
ちなみに実数(浮動小数点数) の方は実際のデータ上でも符号で 1bit使っていたりします。
■ 可変バイト
PMXでは頂点の最大数を 16bitから 32bitの範囲へ拡張したわけですが、そのまま全数値データを 32bitのデータ幅で持たせてしまうと、PMDからの変換だけでファイルサイズが随分膨らんでしまう懸念がありました。
それ故、必要数に応じてデータサイズを最適に変更できるような仕組み=可変バイト方式を導入しています。
具体的な数値範囲は仕様にある通り。横着をしようとすれば全部 4byte(32bit) で保存することも可能なので「同じデータ内容なのにファイルサイズが山ほど違う」 みたいな状況になる環境もあるのかもしれませんね。
なお標準モデル(初音ミクVer2.pmd) を PMX変換した場合は、695 KB (712,126 バイト) → 709 KB (726,199 バイト) と、ほぼ変化ない程度の範囲に納まっていることがわかります。
といっても昨今は GPUで巨大なテクスチャ(画像)データを取り扱えるようになった影響で、モデルデータ側をいくら節約したところでほとんど意味はありません。残念。
■ 32bitだけ符号付き
理由としては二つほど。一つは 20億もあれば十分すぎる、といった点。
PMXはリアルタイムレンダリング、すなわち GPUでの直接運用を前提としているため、そもそもグラフィックメモリ(VRAM) に入る分しか動かせません。
頂点一つの最小データを
位置: float(4byte) x3(x/y/z)
法線: float(4byte) x3(x/y/z)
画像UV: float(4byte) x2(u/v)
としても 4x(3+3+2) で計 32byte
符号あり32bit範囲ギリギリで 32x2G = 64G byteとなるわけで 「これを易々と越えるような状況には当分ならないだろう」 という予測が働きます。
二つ目はプログラミング(言語) の都合上、"符号ありと符号なしの数値は別物として取り扱われる" という基本的な動作仕様のため。
両者の数値に対して計算をおこなう場合、擦り合わせ用の記述(キャスト) が必要になります。さらに記述面だけでなく僅かな処理(オーバーヘッド) も発生します。
また一般的なデータ管理の要素数は基本的に符号あり32bit数を基準にして用意されているため、最大数が符号なしの場合、管理上不足してしまう問題が発生します。
結局のところ符号なし数の取り扱いは "無駄と面倒が増えるだけでいいことがない" わけですね。
※一応擁護しておくと主に bit演算やメモリアドレス、ハンドルなどを直接取り扱う場合に必要になります
ここで 「じゃあなんで 8bit/16bitは符号なし数値にしているんだ?」 という疑問を持たれるのは当然です。
こちらの理由は簡単で以下の二点。
・内部的な動作では 符号あり32bit数で すべての取り扱いをおこなっている (=符号による問題は出ない)
・頂点は参照されるデータ量が多いので変更点を大きめの位置(サイズ) にとりたい
ローポリ系にそれなりに無駄なく対応しつつ、大きなデータも遜色なく扱える...あたりを熟慮した結果です。
また元々 PMDでは 16bit範囲で頂点数 6万程度が最大だったこともあり、その辺の仕様を PMXでもそのまま引き継いだ経緯もあります。
■ 「データ数範囲の数値、1つズレてません?」
鋭い ご指摘ですね。
本来データの要素数から考えるとバイトサイズとの比較は 2^n (2のn乗) の数値になるはずです。
実は本文中に抜粋した "■データフォーマット" の内容は "○Index参照のバイトサイズ" の項目に記載されています。
頂点数やデータ数と書いてはありますが、実際の取り扱いとしては Index の値を対象としているものです。
Indexは端的に言えば数字による番号。ただし開始番号は 1 ではなく 0 からにすることが通常です。
※意味合い的には 「開始位置 + Index」 で場所を指定するため 0開始の方が都合がよい
ということで 2^n ではなく 2^n-1 という一つズレた数値が範囲の限界になっているわけです。
まあこの辺の差異はややこしいので、問題が出ない側 (=数が少ない方) を基準にして想定しておくと、安全策としてもよい選択になると思います。
データ数の表記が細かな数値ではなく、40億とか 20億とか比較的ざっくりした扱いになっているのも、そういう考え方がベースになっているからですね。
何事もギチギチまで詰めようとせず、ある程度の余裕を持った状態で対応できるようにしておくのが、後の 「こんなこともあろうかと!」 といった事例に繋がるのかも?しれません。
以上、ここまで目を通して頂き ありがとうございました (解説のまとめはありません)
【関連する記事】