【Unity】Transparentシェーダー レンダリングTips (RenderQueue編)
概要
UnityにおけるTransparentシェーダー(透過シェーダー)に関するTipsです。
Transparentシェーダーは3DCGにおいてよく使われる一方で昔から非常に悩ましい存在であったりします。
特に描画順の関係で思ったような絵にならなかったというのはよくあるパターンだと思います。
Unityでの透過オブジェクトを利用する場合の参考にしていただければと思います。
動作環境
- Windows 10
- Unity 2021.2.6f1
- Universal RP 12.1.2
検証対象のTransparentシェーダー
- Shader Graphを使用
_Color
プロパティのみを持ち、アルファも使用
Transparentシェーダーの特徴
アルファブレンドの場合、描画の順番で結果が異なる
デフォルトのブレンド設定であるアルファブレンド(Blending Mode が Alpha)の場合、オブジェクトの描画順で結果が異なります。
アルファ0.4の赤と緑のシェーダーを使ったオブジェクトを描画順を変えて見てみましょう。
上記のように描画順で描画結果が異なります。
Transparentシェーダーはアルファブレンドの場合に描画順で結果が変わるということを覚えておきましょう!
基本的なことですが、とても重要な性質になります。
アルファブレンドは描画順で結果が変わる理由
簡単に説明するとアルファブレンドの計算式のためです。
R * a + G * (1 - a) => 0.4R + 0.6G (緑 -> 赤の順番)
G * a + R * (1 - a) => 0.6R + 0.4G (赤 -> 緑の順番)
※上記はイメージのための計算式で実際は R * Ra + (G * Ga + Dest * (1 - Ga)) * (1 - Ra)
のような計算
加算ブレンドのみの場合、描画の順番に関わらず結果が同じ
加算ブレンド(Blending Mode が Additive)のマテリアル同士の場合は描画順に関わらず結果が同じになります。
加算ブレンドのみの場合ということに注意してください。
アルファの乗算色を単純に加算するため、計算の順番で結果が変わらないからです。
R * Ra + G * Ga(緑 -> 赤の順番) = G * Ga + R * Ra(赤 -> 緑の順番) * Ra: 赤のアルファ値, Ga: 緑のアルファ値
RenderQueueが2501以上の場合、ソートが発生
RenderQueue 2501以上かつ同一のRenderQueueの場合はカメラから遠いオブジェクトから近いオブジェクトの順番で描画する処理が発生します。
透明オブジェクトはカメラから遠いオブジェクトから近いオブジェクトの順番で描画したほうが結果が安定します。
Unityはデフォルトでソートを実施して、遠->近の順番でオブジェクトを描画してくれています。
UnityのTransparentシェーダー + BlendingMode AlphaでRenderQueueを変えたときの描画結果 pic.twitter.com/PjsRwgjHBz
— すぎしー (@tsgcpp) 2021年12月26日
※どちらもアルファは1.0
ポイントは以下です。
- RenderQueue 2501以上かつ同一のRenderQueueのマテリアルを持つRendererが複数ある場合はソートが発生
- カメラから見て遠いオブジェクトから近いオブジェクトの順番で描画
- ソートの基準位置はオブジェクトのTransformのposition (オブジェクトのピボット)
- 異なるRenderQueueのマテリアル間ではソートは発生しない
- 異なる場合はRenderQueueが小 -> 大の順番で描画
- RenderQueue 2500以下の場合はソートは発生しない
- 不透明オブジェクトはデプステストにより基本的に描画結果が安定するため
TransparencySortMode の説明にも以下のように記載されています。
By default, perspective cameras sort objects based on distance from camera position to the object center;
おまけ1: BlendingMode Additiveの場合
描画順に関わらず結果が一定になります。
BlendingMode Additive の場合。ずっと同じ pic.twitter.com/u7BRj6ph5I
— すぎしー (@tsgcpp) 2021年12月26日
※どちらもアルファは1.0
おまけ2: 赤がAlpha, 緑がAdditiveの場合
全体がAdditiveではない場合は結果が不安定になります。
マテリアルにBlendingMode Alphaが存在する場合はRenderQueueを意識する必要があります。
おまけ、赤がAlphaで緑がAdditiveの場合 pic.twitter.com/HQyGzn35lV
— すぎしー (@tsgcpp) 2021年12月26日
※どちらもアルファは1.0
デプス (深度) を通常は書き込まない
Transparentシェーダーはデプスを書き込まないように設定することが一般的です。
こちらも描画順で結果が不安定になるためです。
- Transparentシェーダーはデフォルト(
Depth Write: Auto
)の場合はデプスを書き込まない - 意図的に書き込むことは可能
ZWrite On
(Depth Write: ForceEnabled
) をシェーダー or マテリアルで指定
デプスを書き込まない場合と書き込む場合との違い
例えば以下のようにオブジェクトを配置した場合を見てみましょう。
- 赤Cubeと緑Sphereを重なるように配置
- 両マテリアルのアルファは0.4
- 両マテリアルのRenderQueueは3000
- 緑Sphereのほうがカメラに近い
- 遠い赤Cube -> 近い緑Sphereの順番で描画
デプスを書き込んだ場合、重なった部分の緑側が描画されていません。
これは赤 -> 緑と描画されて、赤側でデプスが書き込まれたため後続の緑側の深度テストによりピクセルシェーダーがスキップされたためです。
意図的にこちらの現象を利用することもありますが、基本的にはTransparentシェーダーはデプスを書き込まないのが一般的です。
Transparentシェーダーパフォーマンス
※本記事では詳しくは取り上げません。
ここまで読んだ方の中にはパフォーマンス面が気になった方もいらっしゃるかもしれません。
Transparentシェーダーはピクセルシェーダー処理が発生しやすい性質上、パフォーマンス面で注意が必要です。
簡単に言えばオーバードローという現象が確実に発生するようなものです。
安易にUIやパーティクルなどで使用すると描画負荷が一気に上がりますためご注意ください!
また、ソート処理も発生するためCPU側にも影響があると考えられます。
パフォーマンスに関しては別の機会に取り上げようと思います。
サンプルプロジェクト
雑感
今年最後の技術ブログになります。
今回は自分の復習も兼ねて3DCGっぽい記事にしてみました。
というかここ数ヶ月、3DCGっぽくない記事が多かったですね。。。
昔のUnityでは透明マテリアルはRenderQueue 2450だった気がしますが、いつの間にか3000になってましたね。
パフォーマンス面については別の機会に紹介しようとは思いますが、いつになることやら。。。
Unity 2021.2ではShader Graphに Allow Material Override
でBlending Modeを切り替えれたり、URPでVFXが使えたりと2020.3と比べて一気に使いやすさが上がっている気がしています。
そういえば、サンプルプロジェクトにTimelineでマテリアルをいじる簡易的なカスタムTrackも入れているので良かったら参考にどうぞ(Preview機能とか色々微妙ですが)。
来年もUnityを追求していきたいです!
それでは、良いお年を~