Movatterモバイル変換


[0]ホーム

URL:


マルシテイア

Unityで透明なオブジェクトにDepth of Fieldが効かない時の対処法

PostProcessingStack、便利ですよね~。僕はUnity新しいプロジェクト始めるとき、大抵最初にMain Cameraにセットして、Bloom、Depth of Field (DoF)、色収差ビネット、ACESトーンマップを有効にしてます。

ただ、最近の案件で、透過画像のスプライトにDoFが効かないという現象に出くわして苦労しました。なんとか対処したので、対処法を書いておきます。

半透明部分の表示がおかしいなど、完全な解決策ではないので、詳しい方はぜひ教えてください~

問題のシーン

今回問題になるのは、このような半透明部分を含んだ透過png画像です。
表面の光沢も表現したいので、Standardマテリアルで Renderting ModeをFadeにしてみます。
この状態でシーンに配置してみましょう。

f:id:amagitakayosi:20190509172909p:plain

うーん、ピントが合わない……

シーン上には、キューブと半透明のスプライトを等間隔に並べています。
左のキューブにはフォーカスが当たっていますが、右側のスプライトは全てボヤケてしまっています。

f:id:amagitakayosi:20190509142123p:plain

半透明な部分がない画像の場合、StandardマテリアルでRendering ModeをCutoutにしてしまえばピントが合うのですが、今回のような画像だと半透明の部分が消えてしまいます。

f:id:amagitakayosi:20190509143535p:plain

Transparentなオブジェクトはデプスを書き込まない

unity transparent dof でググってみると、PostProsessingStackのissueが見つかりました。

github.com

Transparentなオブジェクトはデプスバッファに書き込まないため、デプスを利用するDoFでは無視されてしまう。なので、明示的にデプスを書いてあげれば良いようです。

また、このissueでは、デプスを書き込むシェーダーのサンプルが紹介されています。

Dummy transparent shader that writes to depth (per request) · GitHub

シェーダーを読んで見ると、どうやらTags { "LightMode"="ShadowCaster" } なPassを追加し、フラグメントシェーダー内でSHADOW_CASTER_FRAGMENT(i) を呼んでやれば、デプスバッファに書き込めるみたいです。

ShadowCasterのシェーダーは名前の通り影の計算に用いられますが、デプスバッファにも反映されます。

さっそく問題のスプライトでもやってみましょう。シェーダーをCreate > Shader > Standard Surface Shader から作成し、ShadowCasterのSubShaderを追加して、スプライトのマテリアルに指定します。

SubShader{    Pass    {        Name "ShadowCaster"        Tags { "LightMode"="ShadowCaster" }        CGPROGRAM        #pragma vertex vert        #pragma fragment frag        #pragma multi_compile_shadowcaster        #include "UnityCG.cginc"        struct appdata        {            float4 vertex : POSITION;            float2 uv : TEXCOORD0;        };        struct v2f {            V2F_SHADOW_CASTER;            float2 uv : TEXCOORD1;        };        sampler2D _MainTex;        float4 _MainTex_ST;        float _Cutout;        v2f vert(appdata v)        {            v2f o;            o.uv = TRANSFORM_TEX(v.uv, _MainTex);            TRANSFER_SHADOW_CASTER(o)            return o;        }        float4 frag( v2f i ) : COLOR        {            fixed4 texcol = tex2D( _MainTex, i.uv );            if (texcol.a < _Cutout)            {                discard;                }            SHADOW_CASTER_FRAGMENT(i)        }        ENDCG    }}

実行!

f:id:amagitakayosi:20190509172400p:plain

あれー

不透明な部分だけデプスバッファに書きこむ

どうやら、半透明な部分もデプスが書き込まれ、黒として描画されてしまったようです。

仕方ないので、半透明な部分にピントを合わせるのを諦め、ShadowCasterのPassではdiscardするようにします。
幸い、今回の画像で半透明な部分はドロップシャドウだけなので、ボヤケててもあまり気にならないはず……

float4 frag( v2f i ) : COLOR{    fixed4 texcol = tex2D( _MainTex, i.uv );    if (texcol.a < _Cutout)    {        discard;    }    SHADOW_CASTER_FRAGMENT(i)}

終結

f:id:amagitakayosi:20190509144038p:plain

やったー

よく見ると半透明部分の描画がおかしいけど、許容範囲ということで……

f:id:amagitakayosi:20190509174757p:plain

まとめ

必要な手順をまとめると以下のとおりです:

  • カメラをDeferredにする
  • シェーダーにShadowCaster のパスを追加
  • 画像のアルファ値によって、透明部分をdiscardする

今回作ったSurfaceシェーダーはこちらに置いておきます。

https://gist.github.com/fand/31bcfa5cd9008a270efb628503200871

もっと良いやり方知ってる人教えてください、頼む!!!!!!

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です読者をやめる読者になる読者になる

[8]ページ先頭

©2009-2025 Movatter.jp