質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • Unity

    3999questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

Transparentなunlitシェーダに影を落としたい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,198

gitalin

score 11

初心者なので、一部とんちんかんなことを言っているかもしれませんがご容赦くださいmm

 解決したいこと

Unlit/Transparentなシェーダのマテリアルを適用したquadオブジェクトに影を落としたい。

 進捗

Unlitなシェーダに影を落とすことはできたが、Transparentと共存できない。。。??
(影を落とすことはできたが、オブジェクトをColorのAlpha値などから半透明とかにすることができない)

以下に、現在のコードを記載します。

私自身、シェーダの知識が未熟でして、何がわからないのかも分からない状態です。
質問も意味不明なことを言ってしまっているかもしれませんが、なにとぞ、ご理解いただけますと幸いです。

Shader "Unlit/CastShadowTest1" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,1)
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _Cutoff  ("Alpha cutoff", Range(0,1)) = 0.5
    }
    SubShader {
        Tags {"Queue" = "AlphaTest" "RenderType" = "TransparentCutout"}

        Pass {
            Tags {"LightMode" = "ForwardBase"}
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma multi_compile_fwdbase
                #pragma fragmentoption ARB_fog_exp2
                #pragma fragmentoption ARB_precision_hint_fastest
                #pragma alphatest:_Cutoff

                #include "UnityCG.cginc"
                #include "AutoLight.cginc"

                struct v2f
                {
                    float4  pos         : SV_POSITION;
                    float2  uv          : TEXCOORD0;
                    LIGHTING_COORDS(1,2)
                };

                v2f vert (appdata_tan v)
                {
                    v2f o;

                    o.pos = UnityObjectToClipPos( v.vertex);
                    o.uv = v.texcoord.xy;
                    TRANSFER_VERTEX_TO_FRAGMENT(o);
                    return o;
                }

                sampler2D _MainTex;
                float _Cutoff;
                fixed4 _Color;

                fixed4 frag(v2f i) : COLOR
                {
                    fixed4 color = tex2D(_MainTex, i.uv);
                    clip(color.a - _Cutoff);

                    fixed atten = LIGHT_ATTENUATION(i); // Light attenuation + shadows.
                    //fixed atten = SHADOW_ATTENUATION(i); // Shadows ONLY.
                    return color * atten * _Color;
                }
            ENDCG
        }

        Pass {
            Tags {"LightMode" = "ForwardAdd"}
            Blend One One
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma multi_compile_fwdadd_fullshadows
                #pragma fragmentoption ARB_fog_exp2
                #pragma fragmentoption ARB_precision_hint_fastest

                #include "UnityCG.cginc"
                #include "AutoLight.cginc"

                struct v2f
                {
                    float4  pos         : SV_POSITION;
                    float2  uv          : TEXCOORD0;
                    LIGHTING_COORDS(1,2)
                };

                v2f vert (appdata_tan v)
                {
                    v2f o;

                    o.pos = UnityObjectToClipPos( v.vertex);
                    o.uv = v.texcoord.xy;
                    TRANSFER_VERTEX_TO_FRAGMENT(o);
                    return o;
                }

                sampler2D _MainTex;
                float _Cutoff;
                fixed4 _Color;

                fixed4 frag(v2f i) : COLOR
                {
                    fixed4 color = tex2D(_MainTex, i.uv);
                    clip(color.a - _Cutoff);   

                    fixed atten = LIGHT_ATTENUATION(i); // Light attenuation + shadows.
                    //fixed atten = SHADOW_ATTENUATION(i); // Shadows ONLY.        
                    return tex2D(_MainTex, i.uv) * atten * _Color;
                }
            ENDCG
        }
    }
    Fallback "Transparent/Cutout/VertexLit"
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Bongo

    2018/05/22 06:33

    いくつか確認したいのですが、まず、そのシェーダーファイルを選択するとインスペクタに何かエラーは表示されるでしょうか(たとえば「Shader error in 'Unlit/CastShadowTest1': syntax error: unexpected token ';' at line 95」のような...)?2つ目は、実現したい描画結果は「他のオブジェクトの影を受けるが、陰付けはされない...つまりQuadが光源の方を向いていようが逆向きだろうが同じ明るさで表示される」と「他のオブジェクトの影を受け、さらに陰付けもされる」のどちらでしょうか?

    キャンセル

  • gitalin

    2018/05/22 11:40 編集

    質問ありがとうございます! 1つ目ですが、とくにエラーは出ていないように思います。 シェーダーでエラーがあると、描画されたときになんか紫色っぽくなると思うのですが、描画自体は問題なくされているようです。 2つ目ですが、「他のオブジェクトの影を受けるが、陰付けはされない...つまりQuadが光源の方を向いていようが逆向きだろうが同じ明るさで表示される」が実現したい描画結果です。よろしくおねがいいたします。

    キャンセル

回答 1

checkベストアンサー

+1

陰付けは不要とのことですので、それを前提に方法を調査してみました。
ですので、陰付けも一緒に行うLIGHTING_COORDSLIGHT_ATTENUATIONは使用せず、影用のマクロを用いることにしました。

現行バージョンでは、自身の上に落ちる影は下記3つのマクロが面倒を見てくれるようです。

  • UNITY_SHADOW_COORDS(idx1)...v2f定義内で使う。バーテックスシェーダーからフラグメントシェーダーへの情報伝達のための変数を追加する。引数に番号を指定することで、データ伝達に何番のテクスチャ座標用セマンティクスを使うか選べる。
  • UNITY_TRANSFER_SHADOW(a, coord)...バーテックスシェーダーで使う。av2f構造体で、それにフラグメントシェーダーに伝達するべき情報が書き込まれる。GI対応のため、coordにライトマップ用UV座標を渡してやる必要がある。
  • UNITY_SHADOW_ATTENUATION(a, worldPos)...フラグメントシェーダーで使う。aにはバーテックスシェーダーから送られてきたv2f構造体を、worldPosにはフラグメントのワールド座標を渡す。すると影係数が返ってくるので、これを出力色に掛けてやれば影の部分が暗くなる。

これらを使って下記のようにしてみましたが、あちらを立てればこちらが立たずといったジレンマが随所にあって、どこかしらに不満の残る結果になってしまいました。

  • 半透明を正しく描画するためにキューを「Transparent」にしたかったが、そうすると影描画ロジックが動かない
  • キューを「AlphaTest」にすれば影描画が行われるが、プレイモードではスカイボックス描画がAlphaTestよりも後に配置されているため、スカイボックスを透かして見る構図になると描画が破綻(背景がSolid Colorか不透明オブジェクトなら大丈夫そうです)するので対策が必要
  • 影描画はデプスシャドウ技法に基づくロジックになっているが、半透明をうまく扱うのは難しそう(シャドウマップ作成時にディザリングを行ってみたものの、ノイズが気になる...ノイズ除去フィルタを加えればマシになるかもしれませんが未実験です)
Shader "Unlit/CastShadowTest1"
{
    Properties
    {
        _Color ("Main Color", Color) = (1, 1, 1, 1)
        _MainTex ("Base (RGB)", 2D) = "white" { }
        _ShadowIntensity ("Shadow Intensity", Range(0.0, 1.0)) = 0.5 // 大きくするほど影が暗くなる
        // _Cutoff ("Alpha cutoff", Range(0, 1)) = 0.5 // 半透明物体の場合、アルファカットオフを可変にはせずに...
        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 // カットオフをトグルで切り替え、オンならば固定値0.001でカットオフするケースが多いようです
    }
    CGINCLUDE
    // 影係数の取得および緩和に使う
    // 1 - (1 - 影係数) * _ShadowIntensity を最終的な影係数とすることで、影が暗くなりすぎるのを防ぐ
    #define FADED_SHADOW_ATTENUATION(i) (1.0 - (1.0 - UNITY_SHADOW_ATTENUATION(i, i.worldPos)) * _ShadowIntensity)
    ENDCG
    SubShader
    {
        // Queueが「AlphaTest」になっていましたが、おそらく妥当な方法だと思います
        // 本来は「AlphaTest」はアルファを基に完全透明・完全不透明を切り替えるタイプの不透明オブジェクトに
        // 用いるもので、半透明オブジェクトは「Transparent」を使うべきかと思いますが、そうしてしまうと
        // 影機能も使えなくなってしまうようです
        // 半透明オブジェクトを正しくアルファ合成するには他の不透明オブジェクトよりも後に描画させたいところですが
        // 「AlphaTest」なら一般的な不透明オブジェクトの「Geometry」よりは後になるそうなので、描画不具合も
        // 起こりにくいのではないでしょうか
        Tags { "Queue" = "AlphaTest" "RenderType" = "TransparentCutout" }
        // シャドウキャスティングパス
        // LightModeが「ShadowCaster」のパスがないと、影を落とす・受けるのいずれも行われなくなるようです
        // コード末尾にFallbackを記述しておくことで他のシェーダーに処理を代替させることもできますが、
        // 今回は半透明の特殊なケースですので、自前で用意することにしました
        // なお、旧バージョンでは「ShadowCollector」パスが影を受ける処理を担当していたそうですが
        // 現在は廃止されたようです
        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }
            ZWrite On ZTest LEqual
            CGPROGRAM
            #define UNITY_STANDARD_USE_DITHER_MASK
            #define UNITY_STANDARD_USE_SHADOW_UVS
            #pragma target 3.0
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityStandardShadow.cginc"
            struct VertexOutput
            {
                V2F_SHADOW_CASTER_NOPOS
                float2 tex: TEXCOORD1;
            };
            void vert(VertexInput v, out VertexOutput o, out float4 opos: SV_POSITION)
            {
                TRANSFER_SHADOW_CASTER_NOPOS(o, opos)
                o.tex = v.uv0;
            }
            half4 frag(VertexOutput i, UNITY_VPOS_TYPE vpos: VPOS): SV_Target
            {
                // シャドウマップへの描画は原理的に完全透明か完全不透明でないとなりません
                // そこで、Unity組み込みのディザリング用テクスチャをアルファに基づいて参照することで
                // アルファが小さいほど高確率で透明、大きいほど不透明となるような動作をさせることができます
                // できあがるシャドウマップにはディザリングによるノイズが乗っていますが、ソフトシャドウモードなら
                // 後で近傍比率フィルタリングがかかるので、影に関してはさほど見た目は悪くないと思われます
                // 半透明部分のエッジにノイズが目立つかもしれませんが、これはポストエフェクトなどでごまかす...?
                half alpha = tex2D(_MainTex, i.tex.xy).a * _Color.a;
                alpha = tex3D(_DitherMaskLOD, float3(vpos.xy * 0.25, alpha * 0.9375)).a;
                // 場合によっては下記1行をコメントアウトして、Quad全面を完全描画させた方が良好な結果になるかと思います
                clip(alpha - 0.01); // ディザリングの結果アルファ0.01未満になればフラグメントを破棄
                SHADOW_CASTER_FRAGMENT(i)
            }

            ENDCG
        }
        // フォワードレンダリングベースパス
        Pass
        {
            Name "ForwardBase"
            Tags { "LightMode" = "ForwardBase" }
            Blend SrcAlpha OneMinusSrcAlpha // 一般的なアルファ合成方式を使用
            ZWrite On ZTest LEqual
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #pragma fragmentoption ARB_fog_exp2
            #pragma fragmentoption ARB_precision_hint_fastest
            #pragma multi_compile_fwdbase
            #pragma multi_compile _ UNITY_UI_ALPHACLIP // アルファクリップがオン・オフの場合でマルチコンパイル
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
            sampler2D _MainTex;
            fixed4 _Color;
            float _ShadowIntensity;
            struct v2f
            {
                float4 pos: SV_POSITION; // UNITY_TRANSFER_SHADOW内ではクリッピング座標の名前が「pos」であることが前提になっているため、そのようにする(ご質問者さんのコードでは、すでにそうなっていました)
                float2 uv: TEXCOORD0;
                UNITY_SHADOW_COORDS(1) // TEXCOORD1を影情報伝達に使用する
                float3 worldPos: TEXCOORD2; // TEXCOORD2をワールド座標伝達に使用する
            };
            v2f vert(appdata_full v) // 頂点入力データの形式として、appdata_tanに代わってappdata_fullを使う
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord.xy;
                UNITY_TRANSFER_SHADOW(o, v.texcoord1); // texcoord1にライトマップ用UVが格納されているので、それを使って影情報の計算を行う
                o.worldPos = mul(unity_ObjectToWorld, v.vertex); // worldPosに頂点のワールド座標をセットする
                return o;
            }
            fixed4 frag(v2f i): SV_Target
            {
                fixed4 color = tex2D(_MainTex, i.uv) * _Color;
                #ifdef UNITY_UI_ALPHACLIP
                    clip(color.a - 0.001); // アルファ0.001未満は完全透明と見なし、フラグメントを破棄してレンダリングしない
                #endif
                color.rgb *= FADED_SHADOW_ATTENUATION(i); // 影係数はアルファには作用させない
                return color;
            }
            ENDCG

        }

        // フォワードレンダリング加算パス
        Pass
        {
            Name "ForwardAdd"
            Tags { "LightMode" = "ForwardAdd" }
            Blend DstColor Zero // 普通は加算パスでは照明効果をどんどん加算していくかと思いますが、今回は影をどんどん乗算していくことにしました
            ZWrite Off
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #pragma fragmentoption ARB_fog_exp2
            #pragma fragmentoption ARB_precision_hint_fastest
            #pragma multi_compile_fwdadd_fullshadows
            #pragma multi_compile _ UNITY_UI_ALPHACLIP
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
            sampler2D _MainTex;
            fixed4 _Color;
            float _ShadowIntensity;
            // v2f、vertはベースパスと同じ(CGINCLUDEやcgincを使って共通コードをまとめることも可能かと思います)
            struct v2f
            {
                float4 pos: SV_POSITION;
                float2 uv: TEXCOORD0;
                UNITY_SHADOW_COORDS(1)
                float3 worldPos: TEXCOORD2;
            };
            v2f vert(appdata_full v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord.xy;
                UNITY_TRANSFER_SHADOW(o, v.texcoord1);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }
            fixed4 frag(v2f i): SV_Target
            {
                float alpha = tex2D(_MainTex, i.uv).a * _Color.a;
                #ifdef UNITY_UI_ALPHACLIP
                    clip(alpha - 0.001); // アルファ0.001未満は完全透明と見なし、フラグメントを破棄してレンダリングしない
                #endif
                float attenuation = FADED_SHADOW_ATTENUATION(i);
                return fixed4(attenuation, attenuation, attenuation, 1.0);
            }
            ENDCG

        }
    }
}

 描画結果1

半透明部分にディザリングによるノイズが目立ちます。また、スカイボックス描画順問題の対策をしていないので、ゲームビューではスカイボックスの半透明部分を透かして見た部分が描画されていません。シーンビューではスカイボックスが最初にレンダリングされるらしく、ちゃんと透けています。
結果1

 描画結果2

上記コードのシャドウキャスティングパスでclip(alpha - 0.01);をコメントアウトするとこうなります。デプスが単なる四角い板と同じになるので、落とす影が四角くなったり、前後関係を正しく表現できなかったりするかもしれませんが、ノイズは現れないのでこの方が好都合な場合もあるでしょう。
結果2

 描画結果3

スカイボックス問題の対策として、

  • カメラを複製する。複製したカメラは元のカメラの子にして、カメラを動かせば追従して動くようにする。
  • 元のカメラの「Clear Flags」を「Don't Clear」とし、背景描画をさせなくする。
  • 複製したカメラの「Culling Mask」を「Nothing」にし、背景だけを描画させる。
  • 複製したカメラの「Depth」を元のカメラより小さくし(例えば-2)、複製したカメラの方を先に描画させるようにする。

とした場合です。とりあえずゲームビューでも背景の破綻は起こらなくなりました。
結果3

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/28 15:23

    とても詳細な解説をありがとうございます!!
    いただいた回答の描画結果2で無事解決に致しました!
    仕事が忙しく返信が遅れてしまい大変申し訳ありませんでした!

    この度は本当にありがとうございました~mmm

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.51%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Unity

    3999questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。