FBのグループにあるjThreeユーザー助け合い所に透過オブジェクトに関する問題が投稿されました。
自分はjThreev3の開発者なのですが、jThreev2で発生している問題、場合によってはv3でも同様の問題が起きるケースはよくあると思うのでこの原因について整理しようと思います。
この記事はこの問題の原因について記述されています。具体的な対処法についてはあまり言及していません。
問題提起
比較的大きなステージの中で透過オブジェクトを設置した場合に、カメラの位置によっては透過が無効になってしまう場合がある問題。以下のように特定のカメラの位置によっては上手く透過されない。
原因
そもそも、レンダリングはどのように起きているかについて知る必要があります。
jThreev2では(three.jsでは)、これらのレンダリングはオブジェクトごとに行われています。オブジェクトごととは、例えばMMDのモデル、ビルボード、キューブジオメトリー等、レンダリング可能なポリゴンの集合体であるとここでは定義します。
例えば、上記のシーンではレンダリングが必要なオブジェクトは2つ存在すると考えられます。
- ステージのMMDモデル
- 正面のテクスチャが貼られているビルボード
これらがレンダリングされる順番は、2通り考えられます。
- ステージ→テクスチャ
- テクスチャ→ステージ
先に問題について言及するとすれば、この場合、テクスチャ→ステージの順番で描画された時のみこの問題は発生します。
描画順序と透過の問題
バックバッファ上の線形合成
3DCGの描画では、バックバッファと言われるメモリ上のスケッチブックに記述されます。
レンダリングされる順番に応じて、このバックバッファに色が乗っていくと考えてください。ただし、3DCGでは透過情報を処理するためにアルファ値という名前の透過成分が保存されています。
バックバッファにあるオブジェクトが書き込まれる際に、バックバッファ上のある点Pにおける新たに書き込まれる色がC=(Cr,Cg,Cb,Ca)として表されるとします。(それぞれ、赤色、緑色、青色、アルファ値のベクトルだと解釈してください。)
また、元々のPのバックバッファの色がS=(Sr,Sg,Sb,Sa)だったとします。つまり、これはもともとSの色をした上にCの色を塗り重ねるケースを想定しています。
この際、グラフィックボード上では通常の透過合成で行われる線形合成という数式に則ってバックバッファ上のP点の結果の色D=(Dr,Dg,Db,Da)を確定します。
深度テスト
ところで、これは新しく描かれるオブジェクトが前のオブジェクトよりもカメラに対して手前に存在する場合のみです。
これは毎回描画時に画面上のすべての点についてカメラからの距離を保存し、それ以上奥にある場合は描画しないようにしているのです。こうすることにより、奥の物体が手前に描かれてしまうことはありません。この機能を深度テストと言います。
通常、この機能は何も深いことを考えなくとも、オブジェクトが不透過な物体であれば問題なく動くのですが、透過な場合には話が変わってきます。
つまり、不透過な場合、ランダムな順序で描こうとも問題なく描画されますが、透過オブジェクトの場合は奥から描かないとすべての透過した結果映るオブジェクトがうまく合成されず不透過なように見えてしまうのです。
さて、この問題に対処するため一般的なゲームエンジンの場合は以下のような対処をすることで解決をします。
- 不透過オブジェクトを片っ端から描画する
- 透過オブジェクトをカメラから遠い順に描画する
こうすることにより、うまい感じに透過オブジェクトが映ることになります。これはjThreeでも例外ではありません。
jThreeにおけるこの問題について
さて、MMDモデルの場合、透過オブジェクトか透過じゃないオブジェクトなのかを自動的に判別するのは至難の技なのです。
PMXファイルフォーマットにはそのモデルが不透過なオブジェクトなのか透過なオブジェクトなのかを判別するようなフラグはないため、テクスチャに1ピクセルでも透過するピクセルが含まれるならば、本来は透明オブジェクトとして描画するべきです。
しかし、テクスチャの全ピクセルにアルファ値が1以外が存在するかどうかを検証し続けていたとしたら、ロードが重くて重くて仕方ありません。
そこで、jThreev2では、すべてのMMDオブジェクトは透過オブジェクトであるとして扱われています。
結論から言えばこれが問題なのです。
というのも、「透過オブジェクトをカメラから遠い順に描画していく」と述べましたが、このカメラから遠いの基準は、オブジェクトの中心座標なのです。オブジェクトごとの描画順序の話をしているので、特定のピクセルにおける描画順序だけを変えることができないので、どちらのオブジェクトを描くかはオブジェクトの情報から決定しなければならないからです。
これは、2つのオブジェクトのサイズが著しく異なる場合に問題が出ます。
例えば、透過が上手くいかないこの例では、テクスチャの中心座標がステージの中心座標よりカメラから見た時遠いという場合に起きていると考えることができます。ステージの中心座標の方が近いため、先ほどのルールで言えば、透過オブジェクトをカメラから遠い順に描画する、すなわちテクスチャ→ステージの順番で描画されます。
しかしながら、テクスチャオブジェクトの後方に関してのみ言えば、当然ながらステージの方が遠いため合成してみる際に、深度テストでステージが描画されなくなります。こうして問題が起きているのです。
対策
対策案その1(PMXモデルをいじる方法)
ステージのPMXモデルの中心座標をずらすことにより対応できます。
ステージのPMXモデルの中心座標を非常に奥にして、頂点だけ手前にくるようにモデルを細工することにより中心座標をどんな位置でもステージの方が奥にあると認識することができます。
対策案その2(three.jsインターフェースを用いる方法)
three.jsインターフェースを用いてステージの材質をtransparentフラグをfalseにすることによりどんなオブジェクトよりもステージが先に描画されるように対策することができます。
three.jsは僕は全くいじったことないんで、これの詳細については独自で調べていただけると。