【Unity/VRChat】半透明が正しく表示されない理由と対処法【描画順,設定】

3DCG
スポンサーリンク

はじめに

今回は、UnityとVRChatで半透明が正しく表示されない理由と対処法を紹介します。

まず、この動作を確認するためにUnityでの半透明の作り方から解説。
その後、原因 → 対処法(描画順の設定)という流れで解説していきます。

ーーーーー

Unityが導入されてる事を前提に進めます。
↓VRChatユーザー向けですが、Unityの導入方法などはこちらをご覧ください。

この問題に至ってる時点で中~上級者だと思うので…
細かい操作説明は省いてます。

下準備:半透明の設定方法

まず、これから紹介する内容を実験するための半透明を設定した立方体を作ります。

Unityを立ち上げ。
「Hierarchy」の空白の所を右クリック。
3D Object → 「Cube」などを追加。

もしHierarchyなどのウインドウが表示されてない方は…
右上の3つの点 → Add Tab → 「表示させたいウインドウ」を選択して表示してください。

Hierarchyで、制作したCubeをクリックで選択。
Ctrl+Dキーで複製。

SceneでWキーを押します。
そして、どちらか一方の立方体を選択。
出てきた矢印を使って位置をズラします。

次は、このCubeに色を塗るためのマテリアルを作ります。
Projectの何もない所を右クリック → Create → 「Material」を選択。

Projectで、制作したマテリアルをクリックで選択。
→ Ctrl+Dキーで複製。

そして、Projectでどちらか一方のマテリアルを選択。
Inspectorを確認。
Albedoの所で “色” と “透明度” を設定します。

透明度はColorの「A」という所で調整できます。

そしたら、制作したマテリアルをドラッグ&ドロップで割り当てます。

すると… 色は変わりますが半透明にはなりません。
大丈夫です、正常な挙動です。

設定した色は「半透明」になってます。
が、データ的には不透明のままなので、半透明の処理が入りませんでした。

そしたら、水色にしたマテリアルを選択。
→ Rendering Modeを「Transparent」に設定。

これで、半透明が表示されます。

以上で、下準備の半透明設定が完了です。

半透明バグの原因

半透明がバグる理由「描画順の問題」です。

まず、3DCGの不透明オプジェクトについて考えます。
3DCGの不透明のモノは、前から順に描かれます。
そして、重なって見えない部分は描画が省略されます。(Zバッファ法の影響)

そして不透明のモデルであれば、描画順が前後しても形の崩れは発生しません。
重なった部分が無駄になりますが…
前後関係を見て、手前の所は上から描くという調整が入ります。

なので、あなたはこれまで描画順を気にしなくても
不透明オプジェクトなら、正しい形で表現できてました。

つまり、不透明は描画順が適当でも…
下記の2つのアプローチを組み合わせることで正しく立体が見えます。

・手前で隠れてる所を書かない
・奥にある、手間のモデルで隠れた所を消す

↓絵で描くと、このような形になります。

そしたら、次は半透明オプジェクトの事を考えてきます。
不透明は前から順に描かれ、重なって見えない部分は描画が省略される事を紹介しました。

では、半透明のモノも同じ要領で「手前 → 後ろ」の順に描いた場合はどうなるでしょうか…?
正解は、裏側の描画が省略された部分が透けて見えるです。

これが、半透明のモノ越しで3Dモデルを見ると一部画消える不具合の発生原因です。

また、ソフトの設定によっては奥のモデルの描画が貫通することもあります。
(Unityでデータ的に半透明なモノを2つ重ねた時の挙動(Render Queue2501以上))

いずれにせよ、半透明バグの原因は…・
半透明オプジェクトの描画順が「前 → 後ろ」になったことです。

細かい事は後で解説しますが…
UnityのRender Queue「2501」以上の設定は…
裏面の描画省略を行わないようです。

なので「手前 → 後ろ」の順で描くと…
そのまま、後ろのオプジェクトが描画されて手前の描画を貫通します。

なので半透明バグは、半透明を「後ろ → 前」の順に描画すれば治ります。

↓詳細はこちらにまとめてます。

以上が、半透明バグの原因です。

半透明バグを引き起こす

では、実際に半透明バグを引き起こして理解を深めていきます。
まずは先ほど半透明を設定した、水色のマテリアルを確認。

マテリアルのInspectorを見てください。
下の方にRender Queueがあり、数値が3000になってると思います。

Render Queueは1 → 5000の順に描画されます。(5000上限)
今は半透明が「後ろ(白) → 前(青)」の順に描かれてます。
なので、正常に動いてます。

そしたら、次はもう1つのマテリアルを選択。
InspectorでRendering Modeを「Transparent」に設定。
これで “実際の色設定が不透明” でも描画処理の設定は “半透明” になります。

こちらのマテリアルをドラッグ&ドロップで読み込み。
すると… 問題無く表示されます。

細かい事は後で解説しますが…
UnityのRender Queue「2501」以上の設定は…
数値が同じだと「手前 → 後ろ」の順で描かれます。
(半透明は正しく表示されます)

では、どうすれば半透明バグを引き起こせるか。
正解は、白色の方のマテリアルのRender Queueを「3001」にする。です。

Render Queue「3001」は、Inspectorの所で数値入力できます。

これで、3000が先 → 3001が後。
つまり半透明のモノが「前 → 後ろ」の順に描かれるようになりました。
そして、表示エラーが発生しました。

つまり、半透明は「後ろ → 前」の順で描くと正しく表示される。
「前 → 後ろ」の順で描くとエラーが出る。

以上が、半透明バグを引き起こす方法です。

あとは、この実験に関する細かい情報や、今後の実験ための設定を行っていきます。

StandardシェーダーはRender Queueの数値制限がある

UnityのStandardシェーダーは有効範囲の数値制限があります。
この数値制限は、Rendering Modeの種類で決まります。

Transparentに設定。
すると、初期状態ではRender Queueは自動で3000になります。

そして、Render Queueには「2500以下」と「4000以上」の数値が入りません。

Unityは2501~3999は半透明向けの一工夫を入れてるので…
そこを使ってくださいよ、という意味で弾いてきます。

次は、不透明の時の挙動を見ます。
Rendering Modeを「Opaque(不透明)」に設定。

そして、Render Queueの状態で2501以上数値入力。
すると… 弾かれて、自動で2000に戻ります。(0~2459が設定可能)

2450~2500は、カットアウトというぶつ切りの透過処理で使う事を想定されてます。
なので、2450以上で弾かれます。

ちなみに、Render Queue横の「From Shader」は変更できません。

このように、UnityのStandardシェーダーはその用途に合わせたRender Queueの数値制限があります。

【Unity側のRender Queueの想定数値】

・0~2449 → 不透明
・2550~2500 → カットアウト
・2501~3999 → 半透明

以上が、StandardシェーダーはRender Queueの数値制限があることの解説です。

実験用にシェーダーをlilToonに置き換える

StandardシェーダーのRender Queue数値制限は実験には向きません。

なので、ここからはlilToonというシェーダーを使います。
これを使うと、半透明の設定でもRender Queue設定0~5000を設定できます。

↓のページにアクセス。

【無料】lilToon - lilLab - BOOTH
アバターを用いたサービス(VRChat等)向けに開発したシェーダーで以下のような特徴があります。 ● 簡単(プリセットからワンクリック設定&自作プリセットの保存、色調補正機能による色替え&テクスチャ書き出し) ● 美麗(白飛び防止、水中などでの透け防止、アンチエイリアスシェーディング) ● 軽量(エディタが自動でシェー...

ダウンロード。

.zipを展開 → ファイルの中に入ります。

中にある.unitypackageをUnityの「Project」にドラッグ&ドロップ。

すべてにチェックが入ってる事を確認。
「Import」で読み込み。

これで、lilToonの導入が完了。

そしたら、マテリアルのInspectorを開きます。
ここのShaderの「右側の名前の所」をクリック → 「lilToon」を選択。

これで、マテリアルのシェーダーをlilToonに変更できました。
そしたら、描画モードを半透明に設定。

この描画モードはStandardシェーダーだと、下図のように対応してます。

lilTonnの基本設定を開きます。
ここにRender Queueが入ってます。

そしてlilToonの半透明はデフォルトで「2460」です。

UnityはRender Queue2500以下になると…
同じ数字が被った場合で半透明が正しく表示されなくなります。

なので、若干挙動が変わります。

2501以上問題については、後で解説します。

以上で、lilToonへの置き換えが完了です。

理不尽なバグが発生したら再起動

lilToonを導入した所… 本来ならありえないバグに遭遇しました。
なぜか、カメラ位置の移動(ズームイン/アウト)で描画順が変わります。

ちゃんとカメラを置いて、Frame Debuggerで動作を確認。
距離的には「白が後ろ」 → 「青が前」で変わらないはずですが… 何故か描画順が変わります。

↓Frame Debuggerの使い方はこちらで解説。

これは… シンプルにUnityの理不尽なエラーだったようです。
Unityのプロジェクトを再起動すると治りました。

困った時は再起動してください。

後、再起動してもダメだった場合は…
Transformの位置を「0,0,0」に戻してからもう一度動かすと治りました。

(正直、謎です。

そしたら、次は…
先ほどから何回か紹介しているRender Queue 2500問題について解説します。

Render Queueは「~2500」と「2501~」で挙動が変化する

Unityは下記のようにRender Queueの用途を想定して作りました。

・Render Queue 2500以下 → 半透明以外に使う
・Render Queue 2501以上 → 半透明に使う

そして… 半透明は「後ろ → 前」の順に描画すれば正しく描画されます。

そこで、Unityは親切心で1つ理不尽な挙動を残しました。

◆ Render Queue 2500以下
→ Unity側が半透明以外に使う事を想定
→ 不透明は「前 → 後ろ」に描くのがセオリー(処理の軽量化)
→ 2500以下の場合、数値が同じなら「前 → 後ろ」の順で描く

Render Queue 2501以上
→ Unity側が半透明に使う事を想定
→ 半透明は「後ろ → 」に描くのがセオリー(半透明エラー対策)
→ 2501以上の場合、数値が同じなら「後ろ → 」の順で描く

そう、最初に紹介したStandardシェーダーのQueueは3000。
つまり、2501以上なので同じ数字でも正しく表示されました。

ですが、VRChatなどでメインに使われるシェーダーlilToonの半透明Queueは2460。
2500以下なので… 同じ数値の半透明が重なると正しく表示されません。

先ほどは「半透明」に「不透明」を重ねた場合は…
Zバッファ法で重なった部分が消えると紹介しました。
これが発生します。

↓そして、こちらの挙動のように裏にあるオプジェクトの重なった部分が消えます。

ちなみに「2501」以上は “数値が同じ” 場合で正しく表示されるだけです。
2501以上でもQueueの数値が手前後ろになっていれば表示エラーが起こります。

2501以上の数値で表示エラーを引き起こした場合…、奥の描画が貫通するようです。
※変化が分かりやすいように、赤色で塗りました。

2500以下は不透明を想定してます。
なので、前から描いた場合、重なった部分の描画を消す処理が入ります。
そして奥のモデルの重なった部分が消えます。

2501以上は半透明を想定してます。
半透明は基本は最後に描かれます。
なので、後から描くオプジェクトの重なった部分を消す処理などは不要なので入ってないようです。
そして奥のモデルを後から描いた場合… 重なった部分が消えずに前のモデルを貫通します。

どちも、半透明を「前 → 後ろ」の順で描いたことが原因。
この点だけ抑えてもらえばOKです。

なので、どの数値のQueueでも…
半透明は「後ろを先、前を後に描く」を徹底すれば、表示エラーは防げます。

以上が、Render Queueは「~2500」と「2501~」で挙動が変化する事の解説です。

半透明バグの対処法

半透明のバクの原因は描画順です。 
半透明のモノは「前 → 後ろ」の順で描かると、奥の省略された部分が見てしまいます。

なので、半透明部分だけ「後ろ → 前」の順に描くと解決します。

つまり…
半透明バグの対処法は、この描画順を設定する事になります。
そう、半透明の部分だけ「後ろ → 前」の順になるよう設定すれば解決します。

そして、この設定を行うためにUnityの描画順は何処で決まるのか?
を知る必要が出てきます。

この描画順が決まるポイントが分かれば、あとはそれを使って正しし順を設定できます。

以上が、半透明バグの対処法です。
そしたら、この描画順の決まり方を解説していきます。

Unityの描画順が決まるポイント

Unityは下記の要素で描画順が決まってます。

【オプジェクトが分かれてる場合】

・Render Queueの設定

・Mesh Rendererなら視点からの距離(Transformの位置)
 → 中心点の位置が同じなら、マテリアルが生まれた順(?)

・Skinned Mesh RendererならBoundsの中心点がある位置
 → 中心点の位置が同じなら、マテリアルが生まれた順(?)

ーーーーー

【オプジェクトが同じ場合】

・Render Queueの設定
 → Render Queueが同じなら、マテリアルが生まれた順(?)

「マテリアルが生まれた順」については…
Unityを再起動すると、反映されなかったり…
もう1回再起動すると反映されたりとまちまちでした。

こちらを挙動を理解し、正しく設定できれば半透明エラーに勝てます。
なので、こちらの要素を解説していきます。

一番はRender Queueの設定

Render Queueは「0~5000」の順番に描画順を決定します。

重要なのは「Render Queue」が描画順決定の上で最強のルールということです。

「半透明」や「不透明」といった描画方法の種類は描画順に影響を与えません。
Render Queueが絶対的なルールです。

試しに、両方とも半透明設定の立方体を用意。
前が2460 → 後ろが2461なので、表示エラーが出てます。

Render Queueはそのまま、後ろの形状だけ「不透明」に設定。
すると、表示エラーが治りません。

ここで、何故か複製した2つのマテリアルで別々の描画モードを設定できないバグに見舞われました。
もし、同じ現象になった方は「新規マテリアルを作成」して実験してください。

そしたら、前が半透明、後ろが不透明設定のまま…
Render Queueの値を前2461 → 後ろ2460に変更。
すると、描画順が「前 → 後ろ」になったので表示エラーが治りました。

つまり「不透明」や「半透明」の設定は見た目に影響を与えますが…
描画順には影響を与えません。

描画順を絶対的に決めてるモノは「Render Queue」です。
以上が、一番はRendeer Queueという事の解説です。

次は、Render Queueが同じ場合の挙動を見ていきます。

別オプジェクトでRender Queueが同じ場合の挙動

別オプジェクトで、Render Queueが同じ場合は視点からの距離で描画順が決まります。

【視点からの距離の判定方法】

・Mesh RendererならTransformの位置
・Skinned Mesh RendererならBoundsの中心点の位置

・Mesh Renderer
→ 主に、ボーンなどで動かない普通のオプジェクトに使われる

・Skinned Mesh Renderer
→ 主に、ボーンなどで動くオプジェクトに使われる

この2つについて見ていきます。

Mesh RendererならTransformの位置

Mesh Rendererは、ボーンなどが入っておらず動かないオプジェクトに使われます。
Inspectorの所で確認できます。

これの視点からの距離の判定は…
Transformの「Positon」の位置で決まるようです。

つまり、動かすと描画順が治ります。
(Transformの位置が変わり、描画順が変わります)

↓の右側は横から見た図です。
 Transformの位置の境界を超えたあたりで、描画順が変わる様子が確認できます。

緑色のPositionの方がカメラに近いので奥に表示されてました。
(青 → 緑の描画順「前 → 後ろ」- エラー)

が、青色を動かしてPositionの位置が変えると…
緑色のPositionの方が遠くなった瞬間に、描画順が変わります。
(緑 → 青の描画順「後ろ → 前」 – 正常)

ただ… Unityの視点からの距離センサーはそこまで精度が高くないです。
なので、結構大きく動かさないと描画順が変わらない事があります。

なので、最後は運任せでUnityの気まぐれ次第な所があります。

ただ、1つ言える事は…
Positionが確実に描画順に影響を与えてると言う事です。

位置が同じならマテリアルが生まれた順(?)

では、TransformのPositionが同じ場合、前後関係はどうなるのか?
↓実験用にこちらのようなモデルを用意しました。

Plane2つを組み合わせたモデルです。
2つとも同じ位置に「Position」があります。

これは… マテリアルが生成された順で描画順が決まるようです。
↓は青色が先、緑色が後で作ってました。

早く生まれた方が “先”
遅く生まれた方が “後”

なので「青が先 → 緑が後」の順番で描画されてます。

その結果、先に描かれた青い部分の半透明の奥にある面が消えてます。

内部的な処理を軽く解説すると…
↓下図のような処理が入りました。

では、青色を後で生まれたようにするために…
Shift+Dキーで複製して見ました。

これで、生まれ順は下図のようになります。
(生まれ順以外、操作してません)

そして、3番目に生まれた青色をドラッグ&ドロップで割り当て。

すると、これで描画順が変わります。

実際には、Unityを再起動したりすると、描画順が生まれ順で変わらなくなる
→ この状態で、もう一度再起動すると描画順が生まれ順で変わるようになる
…みたいな、謎に不安定な挙動をしました。

なので見出し位置が同じならマテリアルが生まれた順(?)の “?” を付けました。

メタデータ系の何かの影響と思いますが…
もうUnityの気まぐれと考えてもいいかもしれません

緑が先 → 青色が後になったので、下図のような見た目になってます。

手前が緑なので、この緑が前で重なった部分の青色が消えてます。

まとめると、下図のようになります。

以上が、位置が同じならマテリアルが生まれた順(?)の解説です。

Skinned Mesh RendererならBoundsの中心点の位置

Skinned Mesh Rendererは、ボーンなどで動くようにしたモデルに使われます。
↓Blenderを使い、立方体をボーンで動くように設定したモデルを用意。

↓ちょっと古い記事ですが… 作り方はこちらで解説。

こんな感じで動きます。
この “動きが” Unity的には嫌みたいです。

ボーンを動かすと、Blenderで言う原点、Unityで言うとTransformのPosition位置とメッシュを大きくズラすことができます。

この問題を解決するために使われるのが「Skinned Mesh Renderer」です。
これは、ボーンに連動して動く仮想の描画範囲を指定する処理が入ってます。(Bounds)

Unityの処理的な問題で、ボーンで動かすモデルはこの白い枠が必要らしいです。

そして、Transformの値はボーンの動き次第で実態と合わなくなります。
なので、Skinned Mesh Rendererは「Bounds」の中点を “位置” として使うようになってます。

UnityはBlenderデータを読み込めるので、ボーンを入れた立方体をそのまま読み込み。

Hierarchyにドラッグ&ドロップ。
(実験でいろいろモデル増えてますが、気にしないでください)

Hierarchyで「▶」ボタンを押してモデルを開きます。
中にある「モデルデータ」を選択。
すると、Skinned Mesh Rendererが使われてる事が確認できます。

このBoundsは、モデルの形に合わせて自動設定されます。
そして「Extent」で大きさを変えれます。

大体の場合、自動設定されたBoundsはキツキツすぎて…
消えてしまう不具合を起こすので、少し人力で大きくし直した方が良いです。

そして、Centerを動かすと… モデルが消えます。

このBoundsの外に出たモデルは消える仕様になってます。

ただ、Unityの距離センサーの精度が悪いので…
かなり大きくはみ出さないと消えないです。

そして、Skinned Mesh Rendererはエラー表示が出ますが…
ボーンが無くても設定可能です。

ボーンなどで動かさないのにSkinned Mesh Renderer使うとは正気か?
みたいなエラーが書かれてます。(多分)

やり方は、Inspector → Add Componentをクリック。
「Skinned Mesh Renderer」を選択するだけ。

これで、ボーンが無くても自動で置き換わります。

そして、Skinned Mesh Rendererは形状に関係無く「Bounds」の中点で位置を取ります。

この中心点と、カメラの視点の関係で位置を決めます。
そして、Render Queueの番号が同じなら…
2500以下なら前 → 後ろの順、2501以上なら後ろ → 前の順に描画順を設定します。

↓は2500以下なので「手前 → 後ろ」の順で描かれてます。
 (そして、表示エラーが発生してます)

そしたら、次は青い箱のBoundsを凄く大きくし、Centerを使って位置をズラす。
これで、手前のモデルより奥のモデルの方が中心点が前にある状態を作りました。

相変わらず、距離センサーの精度が悪いので大きめにズラしました。

この状態で「Center」を動かすと…
メッシュの位置が変わって無くても、中心点の位置が遠くなれば描画順が変わります。

以上が、Skinned Mesh Rendererなら…
Boundsの中心点の位置で描画順を決めるという事の解説です。

中心点の位置が同じならマテリアルが生まれた順(?)

Skinned Mesh RendererでBoundsの中心点の位置が同じだとどうなるのか?
↓のようなモデルを用意。

こちらは… 「Mesh Renderer」の時と同じ。
マテリアルが生まれた順で決まるようです。

挙動は完全に同じでした。
以上が、中心点の位置が同じならマテリアルが生まれた順(?)の解説です。

オプジェクトが同じ場合の描画順

Unityは1オプジェクトに2つ以上のマテリアルを設定できます。
(要:Blender)

これは、Blenderなどの3DCG作成ツールを使い、2つ以上のマテリアルを割当てると設定できます。

↑こちらは球の一部分を複製 → 拡大してます。
なので、半透明の重なり問題が発生します。

そしたら、ここの重なった部分の挙動を見ていきます。

こちらは「球(後ろ) → 複製した一部(手前)」の順に描かれれば正しい見え方をします。

1番はRender Queueの設定

同一オプジェクト、別マテリアルでも… Render Queueが最強です。
Render Queueは「0~5000」の順番に描画順を決定します。

このルールが最強と言う点には変わりません。

同一オプジェクト、別マテリアルの場合…
“位置” の差は使えないので、Render Queueの差を作るのが一番の理想。

Render Queueが同じならマテリアルが生まれた順(?)

1オプジェクトが共通してる場合…
「Transformの位置」や「Boundsの中点」が同じで、差を作ることができません。

↓データ的な “位置” は同じになります。

なので… こちらは「マテリアルが生まれた順」で描画順が決まるようです。

ちなみに、下図のような表示になってます。
(緑色がちょっと明るくなった方が正常(背景の色が出ている))

ちょっとわかりにくいので、色を変えて見ました。
(裏面に球の面があるので、後ろが透過しないのが正常な見え方)

こちらは、下図のような見た目になります。

ちょっと見た目的に分かりにくいですが…
マテリアルの生まれた順(?)で描画順が変わってる事には違いがありません。

VRChatでの挙動

最後に、VRChatでよくあるトラブルと実際に半透明はどう設定したらいいかについて考えます。

VRChatの半透明は2つの問題によって発生しやすくなっている

1つ当たり前の事を言いますが…
このような問題を発生させないために存在するのがRender Queueです。

…が、VRChatのモデルはユーザーそれぞれが思い思いにアップロードします。
なので、何番か分からないRender Queueが無差別に飛んできます。

ワールド:ちるなでるーむ / by うみねここ

あと、マテリアルの制作順も無差別です。
距離センサーも制度はあまり高くないので…

描画順は “ランダムで変わるモノ” と思った方が良いぐらいです。

つまり、事実上Render Queueなどによる描画順の指定が使えない。
これが1つ目の問題です。

ーーーーー

そして、2つ目の問題は…
VRChatで、一番普及しているシェーダー「lilToon」の半透明の初期値です。
これが、デフォルトで「2460」になってます。

2500以下のRender Queueは、同じ数値が重なると「前 → 後ろ」の順に描画されます。
そして、多くのユーザーはこんなRender Queueなんて気にしてません。

つまり、ほとんどは2460の半透明で重なるなので…
半透明に半透明が重なった時に、手前の半透明が奥の半透明を消すという挙動になります。

2501以上であれば、同じ番号で重なった際に正しく見えるのですが…
何か、事情があって2460に設定されてるようです。

VRChatは、この2つの問題で半透明エラーが発生しやすくなっています。

服などが水面で消える理由

仕組みは、これまで紹介した半透明エラーと同じです。
関係するモデルが1つ増えてちょっとややこしくなっただけです。
まず「不透明(肌)」 → 「半透明(服)」 に描かれたアバターを用意したとします。

こちらに水面を追加…。
すると、描画順がわるいと “灰色の所” だけ消えます。

なにが起こったかというと…
「不透明(肌)」 → 「半透明(水面)」 → 「半透明(服)」の順に描画されたのが原因です。
肌 → 水面の間では正しい描画順なので消えませんが、服 → 水面は間違った描画順なので消えます。

こうして肌だけ残ります。
→ 水面で服や髪の毛だけ消える現象が発生します。

おさらい。
正しい描画順は「後ろ → 前」です。
(不透明の場合)

ではどうすればいいか、
答えは3つとも正しい描画順にするだけです。

これで治ります。

見る角度によって服が消えたり消えなかったりする時の挙動

たまに、見る角度で服が透けたり透けなかったりすることがあります。
これは、ワールドの「水面」と「服」などのRender Queueの番号が被った時に発生します。

Render Queue 2500以下で被った事を想定して紹介します。

まず、アバターはボーンが入って動きます。
なので、Skinned Mesh Rendererが使われてます。
これは、Bounds中心点で位置を取り、描画順を決めてます。

水面は動かないです。
なので、だいたいTransformのPosition位置で位置が決まります。
(だいたい中点)

ではこの2つが… 下図のような視点で見た場合、
距離の判定は「人が前 → 水面が後ろ」になります。

そして、Render Queueが2500以下なら「人(手前) → 水面(後ろ)」の順。
Render Queueが2501以上なら「水面(後ろ)→ 人(手前) 」の順で描画されます)

実は、服越しで見ると水面が削られるのですが…
こちらが、問題になる事は少ないです。

では、これを浅い角度で見ると… 距離の判定が入れ替わります。
水面の方が “近い” 存在になります。

そして、Render Queueが2500以下なら「水面(手前) → 人(後ろ)」の順。
Render Queueが2501以上なら「人(後ろ)→ 水面(手前) 」の順で描画されます。

Unityの距離センサーは精度が低いので…
実際に再現するなら変化を大きくしないと動かない可能性があります。

こうして、視点による描画順の変更が発生します。

これが、見る角度によって服が消えたり消えなかったりする原因です。

服などが消えるのはある程度仕方ない

VRChatは、様々な番号のRender Queueが無差別に飛んできます。
そして、Render Queueは最強の描画順指定です。

これが、ランダムで飛んでくるので…
結論としては「服などが消えるのはある程度仕方ない」です。

ワールド:ちるなでるーむ / by うみねここ

服を消されないためにできる事

どうしても消されたく無いモノはRender Queueを小さめの値設定します。

ただ、Render Queueを大きくし過ぎると…
「他人の服を消す可能性」が出てくるので注意。

「2459」や「2501」あたりが無難。

どうしても、絶対に、他人の服やUIなどの要素を壊してでも
自分の服を消したくない場合は「0」に設定すると、絶対に消えない服ができあがります。

ただ、小さい値過ぎると… ワールドなどにある…
(パネルなどが消え始めるので、おすすめはしません)

傘やスカートなどの透過エラー対策

傘やスカートなどに半透明を入れると裏側が表示されないという半透明エラーが出ます。

理由は、前の半透明で奥の半透明を見る事になるからです。

厚みの無いメッシュの場合、Cull Modeを「Off」にすると、背面を表示できます。
(背面 = 裏側の面)

そして、 Z Writeをオフにすると… 正しく見えます。
…が、Z Write オフは他のモデルとの見え方に影響を与えるのでおすすめしません。

Z Writeがオフになると、半透明に謎のエラーが発生し始めます。

↑は表現の為に中心点の距離エラーを使いましたが…
VRChatで見たエラー事例だと、
スカートの脚の間の部分だけ透過が消えるなどのちょっと謎なエラーが出ました。

なので、Z Writeはオン。
Cull Modeを「Back」にして作ることをおすすめします。

では、Z Writeオンでどう透過を表示するか?
現在できる対処法は厚み付け+そこだけマテリアルを変える方法です。

厚みのつけ方はこちらで解説。

Blenderの編集モードで選択。
「Shift+Dキー」や「Alt+E → 法線に沿って面を押し出し」
などで、簡易的に厚みを作れます。

そして、内と外に別のマテリアルを割当て。

あとは面の向きなどを調整。

↓面の向きの調整方法はこちらで解説。

そして、Unityで2つのマテリアルを作りRender Queueを調整して割り合て。
これで、傘やスカートなどの半透明を綺麗に表示できます。

Render Queueを正しく調整すれば、他のモデルの半透明なども綺麗に表示できます。

特に目立った表示エラーも無いようです。

gifにした関係で、一瞬灰色の立方体の一部が貫通しますが…
これは、gifによる劣化の問題。
(元の画面では正しく表示されてます。

ただ、この方法にも下記のようなデメリットがあります。

・厚み部分の挙動がバグる
・マテリアル数が増える
・厚み分のポリゴンが増える

それでも現状、傘やスカートなどを綺麗に出すにはこの方法しかないです。

諦めて、ポリゴン数とマテリアル数を増やしましょう。

半透明が消されることを前提としたデータを作る

Render Queue 0の絶対に消えない服を作るより…
消える事を前提としたデータ作りをする方が現実的です。

【服が消えることを前提としたデータの作り方】

・服が消えるのは仕方ないので、服と一緒に "体" も消消えるようにする
・服と体のRender Queueを2051以上でなるべく近いモノに設定する

例えば「体(不透明)」と「服(半透明)」の設定の場合…
体「2000」 → 服「2460」 → 前に重なったもの「2001~2459」の場合、服が消えます。

そこで「服(半透明)」を「2501」以上にします。
そして、「体(不透明)」を「2500」と差が-1しか無い数値にします。

肌と服のQueueが同じ番号だと… 視点によって前後が変わる不具合が発生し、
体を服が消す可能性があるので、数値に差をつけました。(肌2500 → 服2501)

この設定であれば、よほど悪質な事をされない限り服が透けなくなります。

【「服:2501」+「体:2500」の時の挙動】

・2500より小さいを当てる
 → 服+体が消える

・2500を当てる
 → 2500以下は「前 → 後ろ」の順なので、服+体が消える

・2501を当てる
 → 2501以上は「後ろ → 前」の順なので、服+体が残る

・2501より大きいを当てる
 → 2501以上は「後ろ → 前」前の順なので、服+体が残る

つまり、UnityのRender Queue2500の挙動の違いを使って設定すると最強になれます。

少しでも数値がズレると、2500の挙動差が使えなくなり服などが透けるリスクが出るので注意。

理想はコレです。
この2500と2501の間にある、奇跡的な挙動を活用するのがおすすめ。

ただし、Unityの距離センサーは精度が悪いので注意。
(前後が入れ替わると服が消えます)

ただ、服が透ける虫メガネのQueueを「2501」にし、
Skinned Mesh Renderer設定で、Boundsの中心点を大きくズラし…
“中心点” の位置だけ後ろにするみたいな設定を持ってこられると対応できませんが…

そこまでやる人は少ないよね、という。
(もはや、作り手側が対策するバグの範疇ではない)

そして、実際のキャラクターモデルは形状が「服」と「肌」の2つじゃないです。
「服」と「尻尾の毛(ファーは半透明表示)」と「肌」みたいな2つ以上の要素が関係してきます。

なので、落としどころとしては…

【服+体は基本最強だが、透過虫メガネ+Unityの誤差対策なし】

・体 = 2500(不透明) 
・服 = 2501(半透明)
・尻尾の毛など = 2502(ファーによる半透明)

「体」と「服」は一番安定するが…
透過虫メガネでQueue2501番などの分かりやすい数値を尻尾が消えるリスクが出る。
 → ありがちな設定で、部分的に消されやすいという意味

※他にも、体や服も2501のようなありがちな数値が重なり…
Unityの距離センサーが誤差ると透ける

ーーーーー

【服だけ消える可能性があるが、透過虫メガネ対策あり】

・体 = 2666(不透明) 
・服 = 2667(半透明)
・尻尾の毛など = 2668(ファーによる半透明)


透過虫メガネで「服」と「尻尾」が消えるリスクが出る。
消えるリスクは出るが… Queue2666という微妙な数値を使わないと消せない
 → ありがちな設定ではないので、部分的に消されにくいという意味

※他にも、体や服も2666のようなありがちな数値ではないと…
Render Queueが正しく動き、Unityの距離センサーが誤差っても大丈夫

…みたいな感じになると思います。

【2つ目の設定の補足】

 → 他の人が作る透過モデルは2460やその付近が多いと想定
 → 自分の透過で、他の人の服を消さないように2460より大きい値を設定する
 → 2500と2501の差を使えないなら、どこかで透過問題は必ず発生する
 (尻尾の毛は消されるのは仕方ない)
 → 世の中には、Render Queueの仕組みを使った人の服を透かす透過虫メガネがある
 → こちらの透過虫メガネ対策で、やや分かりにくい2666の値を基準値に設定
 → 自分の服で体などを消さないように「2666を不透明」「2667を透明」としてモデルを作る
 → これで、ピンポイントで2667の透過素材を持ってきて重ねられても大丈夫
 → 2501以上のRendere Queueは数値が重なると「後ろ → 前」の順に描かれる
 → つまり、同じRender Queueでも基本は服が透けない
 (Boundsの位置をズラした、2669の虫メガネを持ってこられた場合は諦めるしかない)

このあたりは、作り手の価値観次第な所があります…。

正直、2666~2668のような分かりにくい数字より…
Queue被り+距離の計測誤差で服だけ消える可能性を許容し、
2500や2501のような分かりやすい数字を使った方が、
ユーサーフレンドリーかな… (アバターを配布する場合)

↓こちらの記事では下記のように設定してます。

【Queue値の設定例】

・体 = 2666(不透明)
・下着 = 2667(半透明)
・服 = 2668(半透明)
・尻尾の毛 = 2669(ファーシェーダー)

※下着やファーシェーダーは半透明を使ったので…
自分で半透明の上に半透明を重ねる事になりました。
なので、Boundsの中点ズレで描画順が変わらないように、仕方なく+1ズラしました

今思うと、下着の半透明は無駄だったと思います。(テクスチャに描いたら良かった)

【もう1つのおすすめ設定の例】

・体+下着 = 2500(不透明)
・服 = 2501(半透明)
・尻尾の毛 = 2502(ファーシェーダー)

※2501透過を重ねられると、尻尾は消えますが…
服ほど致命的なエラーではないので、消えることを許容して、
「体+下着」と「服(半透明)」の挙動を安定させた方が良かったかな…という考え。
(Queue被りの、距離センサーの誤差エラーは考えないモノとする)

そして、どちらにせよ、このようなデータを作る上で重要になる事は…
「半透明」を必要以上に使わない事です。

【半透明を使わないデータ作りノコツ】

・不透明+テクスチャで透過っぽい表現を "描く"
・アルファクリップで透過させる
 (ブツ切りの透過で、ほぼ不透明して扱える(同一Queueが重なっても消えにくい))

…など

これらを守る事で、自アバターで半透明が重なる量を減らせます。
そして、Render Queue差を減らし、透過問題が解決しやすくなります。

ちなみに、【3Dキャラモデル制作】シリーズで作ったこの青い子は…
今の私の技術レベルで見ると、色々データ的な失敗をやらかしてしまってる事が分かってしまうので…

作り直した方が良いなと思って、ここでモチベーションが無くなり開発が止まりました。

ただ、100%の解決法は無いです。
そして至る結論は… センシティブな部分は描き込まない。
見えてもOKな前張り方式にすること… ぐらいですね。

まとめ

今回は、UnityやVRChatで、半透明が正しく表示されない理由と対処法を紹介しました。

・半透明の表示エラーは描画順の問題
・半透明は「後ろ → 前」の順で描くと正しく表示される
・描画順はRender Queueで決まる
・Render Queueの数値が同じなら、視点からの距離で決まる
・Queue2500以下で同じ数字なら、視点から近い方から描画
・Queue2501以上で同じ数字なら、視点から遠い方から描画
・Queueと距離が同じならマテリアルが生まれた順で描画順が決まる
・視点からの距離はTransformのPositionや「Bounds」の中点で取る
・Unityの距離センサーは精度が悪く、バグりやすいので注意
・VRChatで服が消える問題などは仕方ない所がある
・対策としてできる事は、肌と服のQueueを近い数字に設定すること(体ごと消す)

また、他にも3DCGやUnutyの事について解説してます。

ぜひ、こちらもご覧ください。

コメント

タイトルとURLをコピーしました