今日まなび-008(240903):ステンシルバッファを使って画面内の一部にだけポストプロセスをかける2(達成!!)

「今日まなび」は最低1日1h、ゲーム作るために学びたいことなんでもいいから学んでいくコーナー。
完全自分用でまとめることは考えなくてOK.1記事1h。(1hでどんだけ学べるかのスピード感もみていきたいので)
1hのうちに次回学ぶことも決定しておくこと

環境:unity2021.3.40f


今日の学び

今の課題を整理

ここのところステンシルバッファを少し学んできたわけだが、ここでやりたいことを整理します。
キャラだけそのままの色、キャラ以外の部分にだけモノクロをかけたい!がやりたいことです。

今時点で学んだことを自作ゲームに反映してみたら、こういう四角の形でマスクされてしまった…
キャラの画像はQuadに張って描画している。こういうことではなく、透過部分も考慮してモノクロする部分とそうでない部分を分けたいのだ。

これはすでにUIのMaskで行われているように見受けるのでみてみたところ、
描画したくない部分をclipなどで描画しないようにしているのではなく、透明なまま描画していることが原因であることがわかった。

clip関数はマスクによく使われるもので、引数が0以下であればフラグメントシェーダをその時点でスキップというか描画しないようにするものである。

clip(col.a - 0.001);

意図した表示にできた!!

Shader "Test/test20240902write"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Tint("Tint",Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        LOD 100

        Pass {

            Stencil
            {
                Ref 1
                Comp Always
                Pass Replace
            }

            Tags { "LightMode" = "ForwardBase" }
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
           #pragma target 2.0

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Tint;

            struct appdata_t {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                float4 pos : TEXCOORD1;

                UNITY_FOG_COORDS(1)
                UNITY_VERTEX_OUTPUT_STEREO
            };

            v2f vert (appdata_t v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            fixed4 frag (v2f_img i) : COLOR
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                clip(col.a - 0.001); //透過部分は描画しない ステンシルバッファにも書き込みたくないので透過ではなく描画しないようにする

                return col * _Tint;
            }
            ENDCG
        }
    }
}

ここまでわかったステンシルバッファ回りのことまとめ

・ステンシルバッファは画素単位で設定できる数値。
・この数値に値を書き込み、別のShaderでその値を参照、比較することでフラグメントシェーダの処理を分岐できる。
・ステンシルバッファへの書き込み+参照はStencilタグを使い、これはPassタグ内にもかけるし、SubShaderタグ単位でもかける
・ステンシルバッファへの書き込みはStencilタグ内でreplaceを指定することで書き込みが行われる。Compは書き込む条件を指定できる。

//このshaderパスのフラグメントシェーダで描画した画素のステンシルバッファに2を書き込む例
Stencil {
        Ref 2
        Comp always  //条件なし
        Pass replace  //ステンシルバッファを書き換える
}

・ステンシルバッファへの書き込みは画素単位に行われ、フラグメントシェーダで描画した画素が書き込まれる対象となる。
 →つまりフラグメントシェーダでclip関数などを使い描画しない画素と判定された画素はステンシルバッファへの書き込みも行われない。
 →透明(アルファ値=0)で描画しているのと描画しないことは見た目は同じに見えるが、ステンシルバッファへの書き込む有無の違いもあるので注意。

・ステンシルバッファの参照および比較はStencilタグ内のCompで行うことができる。

//このshaderパスのフラグメントシェーダは条件に一致した場合のみ行う
Stencil {
        Ref 2
        Comp Equal  //Refで指定した値とステンシルバッファが同じという条件
}

・Pass単位にステンシルバッファを参照比較できるため、ステンシルバッファの値によってPass単位に分岐できる。
 →1つのPass内でステンシルバッファの値をif文のように比較はできないため、複数Passとして定義する必要がある。

TODO (調べたいこと、やりたいこととか疑問)

・CommandBufferってなに?
・Unityで深度バッファの値をどうやって取得できる?

・法線マップを扱う
・Unityのシャドウマップってなんだろう
・影を制御したい、動かしたり形を変えたり、色をかえたり


・StandardShaderでできることを把握しておきたい
・FrameDebuggerの使い方まなびたい
・shaderで図形描いたりするのによく使う関数と使い方まとめ

終わったやつ

・自作shaderを適応したQuadでも影を落としたい ・ポストプロセスで例えばモノクロを適応したときに、一部分(例えば特定のキャラの描画だけとか)には適応したくない場合がある。
これまでカメラをわけて対応してきたが1つのカメラでもそういった制御ができる方法はないか
・Quad内の不透明部分のピクセルだけステンシルバッファに書き込むことはできないか

今日まなび-007(240902):ステンシルバッファを使って画面内の一部にだけポストプロセスをかける

「今日まなび」は最低1日1h、ゲーム作るために学びたいことなんでもいいから学んでいくコーナー。
完全自分用でまとめることは考えなくてOK.1記事1h。(1hでどんだけ学べるかのスピード感もみていきたいので)
1hのうちに次回学ぶことも決定しておくこと

環境:unity2021.3.40f


今日の学び

ステンシルバッファとはピクセル数分、値を保持することができる領域。
たとえば、ステンシルバッファの値が1の場合だけ描画する、といったことでマスク処理なんかに使われたりする。

nn-hokuson.hatenablog.com

これを使うには、


・ステンシルバッファに値を書き込む
・ステンシルバッファの値を参照してやりたい処理をやる

の2段階踏むことになる。

今回はQuad以外(↓の絵だと赤い四角)の部分をモノクロにするものを作ってみる。

ステンシルバッファに値を書き込む

Shader "Test/test20240902write"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Tint("Tint",Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        LOD 100

        Pass {

            Stencil
            {
                Ref 1
                Comp Always
                Pass Replace
            }

            Tags { "LightMode" = "ForwardBase" }
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
           #pragma target 2.0

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Tint;

            struct appdata_t {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                float4 pos : TEXCOORD1;

                UNITY_FOG_COORDS(1)
                UNITY_VERTEX_OUTPUT_STEREO
            };

            v2f vert (appdata_t v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            fixed4 frag (v2f_img i) : COLOR
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col * _Tint;
            }
            ENDCG
        }
    }
}

今回の学び部分は以下。
これはステンシルバッファに1を書き込むだけの定義である。
ステンシルバッファはSubShader単位でもかけるし、Pass単位でもかけるみたい。

Stencil
{
        Ref 1 //対象の値は1
        Comp Always //条件を定義できる。Alwaysは判定なしにTrue
        Pass Replace //条件がTrueの場合に行う処理。 Replace=書き込む ということを表す
}

ステンシルバッファの値を参照してやりたい処理をやる

値を参照する側は、このようなコードになる。
これは1つ目のPassでステンシルバッファが0ならモノクロ化、2つ目のPassで1ならそのままの色を描画するという処理になる。

Shader "Test/20240831"
{
    Properties {
        _MainTex("MainTex", 2D) = ""{}
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Stencil
            {
                Ref 0
                Comp Equal
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }
            
            fixed4 frag(v2f_img i) : COLOR {
                fixed4 c = tex2D(_MainTex, i.uv);
                float gray = c.r * 0.3 + c.g * 0.6 + c.b * 0.1;
                c.rgb = fixed3(gray, gray, gray);
                return c;
            }
            ENDCG
        }


        Pass
        {
            Stencil
            {
                Ref 1
                Comp Equal
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }
            
            fixed4 frag(v2f_img i) : COLOR {
                fixed4 c = tex2D(_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }
}

ポストプロセス用のShaderでステンシルバッファの値がうまく参照できない?

さて、上述したモノクロ化のshaderはポストプロセスとしてカメラに写っているものに対して行おうとした処理なのだが、
これまで通り以下のコードでポストプロセスをかけた際に、ステンシルバッファの値を意図したように参照できていなかった。

   public void OnRenderImage(RenderTexture source, RenderTexture dest)
    {
        Graphics.Blit(source, dest, mat);
    }

このあたりは正直現状よくわからないが、CommandBufferという仕組みをすることでポストプロセスでもステンシルバッファを使うことができる。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class Test20240902 : MonoBehaviour
{

    /// <summary>
    /// コマンドバッファ名
    /// </summary>
    private const string CommandBufferName = "StencilImageEffect";

    /// <summary>
    /// イメージエフェクトで使用するマテリアル
    /// </summary>
    [SerializeField] private Material material;

    /// <summary>
    /// イメージエフェクト用コマンドバッファ
    /// </summary>
    private CommandBuffer commandBuffer;


    private void OnEnable()
    {
        if (material == null) return;
        if (commandBuffer != null) return;

        var cam = GetComponent<Camera>();
        var cbs = cam.GetCommandBuffers(CameraEvent.BeforeImageEffects);
        foreach (var cb in cbs)
        {
            // 多重登録を回避するため、名前でチェック
            if (cb.name == CommandBufferName) return;
        }

        commandBuffer = new CommandBuffer();
        commandBuffer.name = CommandBufferName;

        // Blitでmaterialを適用してイメージエフェクトをかける
        commandBuffer.Blit(
            BuiltinRenderTextureType.CameraTarget,
            BuiltinRenderTextureType.CameraTarget,
            material);

        // カメラにコマンドバッファを登録
        cam.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer);
    }

    private void OnDisable()
    {
        if (commandBuffer == null) return;

        var cam = GetComponent<Camera>();
        cam.RemoveCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer);
        commandBuffer = null;
    }
}

TODO (調べたいこと、やりたいこととか疑問)

・CommandBufferってなに?
・Quad内の不透明部分のピクセルだけステンシルバッファに書き込むことはできないか


・Unityで深度バッファの値をどうやって取得できる?


・法線マップを扱う
・Unityのシャドウマップってなんだろう
・影を制御したい、動かしたり形を変えたり、色をかえたり


・StandardShaderでできることを把握しておきたい
・FrameDebuggerの使い方まなびたい
・shaderで図形描いたりするのによく使う関数と使い方まとめ

終わったやつ

・自作shaderを適応したQuadでも影を落としたい ・ポストプロセスで例えばモノクロを適応したときに、一部分(例えば特定のキャラの描画だけとか)には適応したくない場合がある。
これまでカメラをわけて対応してきたが1つのカメラでもそういった制御ができる方法はないか

今日まなび(20240829):深度バッファを表示してみる

「今日まなび」は最低1日1h、ゲーム作るために学びたいことなんでもいいから学んでいくコーナー。
完全自分用でまとめることは考えなくてOK.1記事1h。(1hでどんだけ学べるかのスピード感もみていきたいので)
1hのうちに次回学ぶことも決定しておくこと

環境:unity2021.3.40f


今日の学び

深度バッファはカメラからの距離を0~1の範囲で表した値をピクセル単位に定義されている領域というか情報群。
これがあることによってピクセル単位に手前のオブジェクトを判断して手前にあれば描画する、奥なら描画しない(Zテスト)。
といった制御ができるやつだ。

今回はこの深度バッファの値を実際に画面に表示してみる。
深度値を使ってできる表現を学んでいきたいのでその予備ちしき。

light11.hatenadiary.com

  • cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test20240829 : MonoBehaviour
{
    public Material mat;
    void Start()
    {
        GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;

    }

    public void OnRenderImage(RenderTexture source, RenderTexture dest)
    {
        Graphics.Blit(source, dest, mat);
    }
}
  • shader
Shader "Test/20240829"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _CameraDepthTexture;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture,i.uv));
                depth = Linear01Depth(depth);  //←これいれないとうまく表示できなかった
                return fixed4(depth,depth,depth,1);
            }
            ENDCG
        }
    }
}

なんかかっこいいのができた

今日は時間がもうないので明日深くこのコードを見ていく!

TODO (調べたいこと、やりたいこととか疑問)

・ポストプロセスで例えばモノクロを適応したときに、一部分(例えば特定のキャラの描画だけとか)には適応したくない場合がある。
これまでカメラをわけて対応してきたが1つのカメラでもそういった制御ができる方法はないか
・Unityで深度バッファの値をどうやって取得できる?


・法線マップを扱う
・Unityのシャドウマップってなんだろう
・影を制御したい、動かしたり形を変えたり、色をかえたり


・StandardShaderでできることを把握しておきたい
・名前わすれたけど、プロファイラーのPassごとの描画を調査できるやつの使い方まなびたい
・shaderで図形描いたりするのによく使う関数と使い方まとめ

終わったやつ

・自作shaderを適応したQuadでも影を落としたい

今日まなび(20240828):Quadに張った透過テクスチャの形にあわせて影を落とす②(達成!)

「今日まなび」は最低1日1h、ゲーム作るために学びたいことなんでもいいから学んでいくコーナー。
完全自分用でまとめることは考えなくてOK.1記事1h。(1hでどんだけ学べるかのスピード感もみていきたいので)
1hのうちに次回学ぶことも決定しておくこと

環境:unity2021.3.40f


直近の目標まとめ。 →透過テクスチャを張ったQuadにカスタムShaderを適応したら影がでなくなってしまったので影をつけたい

今日の学び

前回調べた影を落とすコードを自作Shaderに適応してみる。…できた!!
右がStandardShader、左が自作Shaderである。
(赤いのは環境光を赤くしてるからで、自作Shader側は環境光の影響をまだうけていない、けど落ち影はでてるとわかる図)

Shader "20240828_custom" {
    Properties{
        _MainTex("Albedo", 2D) = "white" {}

        //落ちる影の描画この2行↓も必要だった!!!!
        _Color("Color", Color) = (1,1,1,1)
                _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
    }

    SubShader {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        LOD 100

        //1パス目はカスタムしたいことを書く。
        Pass {
            Tags { "LightMode" = "ForwardBase" }
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
           #pragma target 2.0

            #include "UnityCG.cginc"

            struct appdata_t {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            struct v2f {
                float4 vertex : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                float4 pos : TEXCOORD1;

                UNITY_FOG_COORDS(1)
                UNITY_VERTEX_OUTPUT_STEREO
            };

            v2f vert (appdata_t v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                return tex2D(_MainTex, i.texcoord);
            }
            ENDCG
        }


        //2パス目で、落ちる影の描画
        Pass {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }

            ZWrite On ZTest LEqual

            CGPROGRAM
            #pragma target 3.0

            // -------------------------------------


            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature_local _METALLICGLOSSMAP
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature_local _PARALLAXMAP
            #pragma multi_compile_shadowcaster
            #pragma multi_compile_instancing
            // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
            //#pragma multi_compile _ LOD_FADE_CROSSFADE

            #pragma vertex vertShadowCaster
            #pragma fragment fragShadowCaster

            #include "UnityStandardShadow.cginc"

            ENDCG
        }
    }
}

まとめ

というわけで、透過テクスチャにあわせて影を落とすのに必要な作業は2つだった。

・実体の描画用のPassのあとに影を落とす用のPassを追加する

   SubShader {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        LOD 100

        //1パス目はカスタムしたいことを書く。
        Pass {
            Tags { "LightMode" = "ForwardBase" }
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
           #pragma target 2.0

            #include "UnityCG.cginc"

            struct appdata_t {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            struct v2f {
                float4 vertex : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                float4 pos : TEXCOORD1;

                UNITY_FOG_COORDS(1)
                UNITY_VERTEX_OUTPUT_STEREO
            };

            v2f vert (appdata_t v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                return tex2D(_MainTex, i.texcoord);
            }
            ENDCG
        }


        //2パス目で、落ちる影の描画
        Pass {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }

            ZWrite On ZTest LEqual

            CGPROGRAM
            #pragma target 3.0

            // -------------------------------------


            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
            #pragma shader_feature_local _METALLICGLOSSMAP
            #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
            #pragma shader_feature_local _PARALLAXMAP
            #pragma multi_compile_shadowcaster
            #pragma multi_compile_instancing
            // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
            //#pragma multi_compile _ LOD_FADE_CROSSFADE

            #pragma vertex vertShadowCaster
            #pragma fragment fragShadowCaster

            #include "UnityStandardShadow.cginc"

            ENDCG
        }

・上記だけだと不十分でPropertyで以下の定義を追加して指定できるようにすることも必要。

Properties{
        _MainTex("Albedo", 2D) = "white" {}

    //落ちる影の描画この2行↓も必要だった!!!!
    _Color("Color", Color) = (1,1,1,1)
        _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
}

TODO (調べたいこと、やりたいこととか疑問)

・名前わすれたけど、プロファイラーのPassごとの描画を調査できるやつの使い方まなびたい ←次回
・ことなるポストプロセスを適応したい場合にカメラを分けなくてもいい方法あるんじゃないか
・Unityで深度バッファの値をどうやって取得できる?
・法線マップを扱う
・shaderで図形描いたりするのによく使う関数と使い方まとめ

終わったやつ

・自作shaderを適応したQuadでも影を落としたい

今日まなび(20240827):Quadに張った透過テクスチャの形にあわせて影を落とす①

「今日まなび」は最低1日1h、ゲーム作るために学びたいことなんでもいいから学んでいくコーナー。
完全自分用でまとめることは考えなくてOK.1記事1h。(1hでどんだけ学べるかのスピード感もみていきたいので)
1hのうちに次回学ぶことも決定しておくこと

環境:unity2021.3.40f


直近の目標まとめ。 →透過テクスチャを張ったQuadにカスタムShaderを適応したら影がでなくなってしまったので影をつけたい

今日の学び:

前回調べたとりあえず影を落とす処理をQuadに反映してみたらこのようになった。 右はStandardシェーダーで、左がカスタムShaderである。

たしかに影は落ちたが、透過部分が反映されておらずメッシュの形(今回はQuadを使っているので4角形)で影が落ちてしまった。


というわけでStandardシェーダーの中身をみて、透過部分を考慮して影を落としている部分を調査したら以下の部分のようだった。

 //  Shadow rendering pass
 Pass {
     Name "ShadowCaster"
     Tags { "LightMode" = "ShadowCaster" }

     ZWrite On ZTest LEqual

     CGPROGRAM
     #pragma target 3.0

     // -------------------------------------


     #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
     #pragma shader_feature_local _METALLICGLOSSMAP
     #pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
     #pragma shader_feature_local _PARALLAXMAP
     #pragma multi_compile_shadowcaster
     #pragma multi_compile_instancing
     // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
     //#pragma multi_compile _ LOD_FADE_CROSSFADE

     #pragma vertex vertShadowCaster
     #pragma fragment fragShadowCaster

     #include "UnityStandardShadow.cginc"

     ENDCG
 }

次回、これを自作Shaderに適応できるか試す

TODO (調べたいこと、やりたいこととか疑問)

・自作shaderを適応したQuadでも影を落としたい ←今進行中
・ことなるポストプロセスを適応したい場合にカメラを分けなくてもいい方法あるんじゃないか
・Unityで深度バッファの値をどうやって取得できる?
・法線マップを扱う
・shaderで図形描いたりするのによく使う関数と使い方まとめ

今日まなび(20240826):影を落とすShader+Unityのレンダリング基礎知識おさらい

「今日まなび」は最低1日1h、ゲーム作るために学びたいことなんでもいいから学んでいくコーナー。
完全自分用でまとめることは考えなくてOK.1記事1h。(1hでどんだけ学べるかのスピード感もみていきたいので)
1hのうちに次回学ぶことも決定しておくこと


本日は自作Shaderを適応すると影が描画されない課題を調査していきます。
▼右がStanderdシェーダー、左が自作スタムShader

今日の学び:とりあえず影を落とす処理を作成

影を描画する用のPassを追加すると影が出る。

 Pass
    {
        //実態を描く:自分がカスタムした部分
    }
    Pass
    {
        Tags{ "LightMode"="ShadowCaster" }

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #pragma multi_compile_shadowcaster

        struct appdata
        {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
        };

        struct v2f
        {
            V2F_SHADOW_CASTER;
        };

        v2f vert (appdata v)
        {
            v2f o;
            TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            SHADOW_CASTER_FRAGMENT(i)
        }
        ENDCG
    }

今日の学び:レンダリングパス

上記をコピペすれば影を落とせるがそれでは理解していないのでまずはレンダリングまわりの基礎知識をおさらいする。

レンダリングパイプライン

Unityでは現時点で、ビルドインパイプライン(BRPとか略されてることもよくある)、URP、HDRPの3つ用意されている。 これに+してレンダリングパイプラインをカスタムすることもでき、これをSRPという。

BRPのレンダリングパスは2つでカメラ単位に設定可能

で、まずは基本のBRPを見ていく。 BRPのレンダリングパスは、フォワードレンダリング、ディファードシェーディングの2つある(レガシーになっているのは省略する) デフォルトはフォワードレンダリングになっている。 ディファードシェーディングはより忠実なライトと影の表現ができるものだが、フォワードでできていた機能が一部使えないなど制限もある。 基本フォワードにしていて必要な場面でディファードを使っていくのがよさそう?

docs.unity3d.com

プロジェクト全体で設定する方法と、+カメラ単位に設定を上書きすることもできる

TODO (調べたいこと、やりたいこととか疑問)

・Quadの影を落としたい ←今進行中
・ことなるポストプロセスを適応したい場合にカメラを分けなくてもいい方法あるんじゃないか
・Unityで深度バッファの値をどうやって取得できる?
・法線マップを扱う
・shaderで図形描いたりするのによく使う関数と使い方まとめ

今日まなび(20240825):自分で4角形のメッシュを作成してテクスチャを張ってみる

「今日まなび」は最低1日1h、ゲーム作るために学びたいことなんでもいいから学んでいくコーナー。
完全自分用でまとめることは考えなくてOK.1記事1h。(1hでどんだけ学べるかのスピード感もみていきたいので)
1hのうちに次回学ぶことも決定しておくこと


本日はこちらの内容を学んでいきます。普段何気なく使っているQuadやPlaneのメッシュはどういう風に情報を持っているかが少しわかる nn-hokuson.hatenablog.com

今日の学び

4角形のメッシュを作成するには、ポリゴンが2つ必要になる。

・頂点
 →1ポリゴンは3つの頂点で構成される
・UV座標(テクスチャ座標)
 →頂点ごとにテクスチャのどの位置に対応するかを指定。0~1
・インデックス
 →頂点を表す番号。
 4角形を描きたい場合、ポリゴンは3角形なのでポリゴンが2つ必要になる。
 その際に2頂点は重なる。これは通常メモリの無駄使いであるため、
 別途インデックスの番号を付与して頂点配列のどの位置の情報を使うかを定義したものをインデックスと呼ぶ。
 4角形なら6頂点のうちの4頂点使うことになる。
 この値を時計まわりに見える順番で定義することによってその面が表だということを表すことができる。
 裏になっていればカリングされてしまうので順番は重要。

4角形のメッシュを生成してテクスチャを張る

using UnityEngine;

//4角形のメッシュ作ってテクスチャ張る
public class Test20240825 : MonoBehaviour
{
    public Material mat;

    void Start()
    {
        //頂点の位置
        float SIZE = 4;
        Mesh mesh = new Mesh();
        mesh.vertices = new Vector3[] {
            new Vector3 (-SIZE, -SIZE, 0), //左下
            new Vector3 (-SIZE, SIZE, 0), //左上
            new Vector3 (SIZE , -SIZE, 0),//右下
            new Vector3 (SIZE ,  SIZE, 0), //右上
        };

        //UV:頂点と画像側の位置の対応 ※0~1で定義する
        mesh.uv = new Vector2[] {
            new Vector2 (0, 0), //左下
            new Vector2 (0, 1), //左上
            new Vector2 (1, 0), //右下
            new Vector2 (1, 1), //右上
        };

        //インデックス:↑で作った頂点やUV配列のどの情報を使うか 
        //※1ポリゴン(3頂点分)時計まわりに見える順番に定義(表面という意味)する
        mesh.triangles = new int[] {
            0, 1, 2, //左下 - 左上 - 右下 の三角形
            1, 3, 2, //左上 - 右上 - 右下 の三角形
        };

        GetComponent<MeshFilter>().sharedMesh = mesh;
        GetComponent<MeshRenderer>().material = mat;


        int polygonCount = mesh.triangles.Length / 3;
        Debug.Log("ポリゴン数=" + polygonCount + " 頂点数=" + polygonCount * 3);
    }
}

TODO (調べたいこと、やりたいこととか疑問)

・Quadに影をつける
・Unityで深度バッファの値をどうやって取得できる?
・法線マップを扱う
・shaderで図形描いたりするのによく使う関数と使い方まとめ