夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

UnityでShaderを勉強する その⑪ スライスを扱うShaderを編集する。

本日はShader勉強枠です。

前回はオブジェクトをスライスするShaderを書きました。

redhologerbera.hatenablog.com

前回のShaderのままではスライスする箇所はワールド座標で固定されており、オブジェクトが移動した場合クリップする範囲がずれてしまいます。

f:id:Holomoto-Sumire:20200427085258g:plain

今回前回のスライスするShaderを加工していきます。

〇スライスする範囲をMaterialで扱えるようにする。

 まずはスライスする線の太さをUnityのマテリアル側から扱えるようにします。

●Example/Diffuse DetaiShader
//シンタックスShaderの名前と格納場所
Shader "Example/Diffuse Detail"{

    Properties{
        _MainTex("Texture",2D) = "white"{}
        _BumpMap("Bumpmap", 2D) = "bump"{}
        _RimColor("Rim Color", Color) = (0.26,0.19,0.0)
            //リムpowerのレンジ defaultでは3に設定される。
            _RimPower("Rim Power", Range(0.5,8.0)) = 3.0
            //SecondryTexture
            _Detail("Detail", 2D) = "gray"{}
    }
        //Shaderの中身サブシェーダーではレンダリングパスの一覧を定義し、任意のオプションとしてすべてのパスに共通の State (状態)を設定します。追加で、サブシェーダーの特定の Tag を設定できます。
        SubShader{
        //RenderingType
            Tags{"RenderType" = "Opaque"}
            CGPROGRAM
            #pragma surface surf Lambert
        //Input構造体はテクスチャ座標として扱うものを記述します。
       struct Input {
           float2 uv_MainTex;
           float2 uv_BumpMap;
           float3 viewDir;
           float2 uv_Detail;
           float3 worldPos;
       };
       sampler2D _MainTex;
       sampler2D _BumpMap;
       float4 _RimColor;
       float _RimPower;
       sampler2D _Detail;
       void surf(Input IN, inout SurfaceOutput o) {
           o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
           //_Detailのrgbをo.Albedoに乗算
           o.Albedo *= tex2D(_Detail, IN.uv_Detail).rgb * 2;
          clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);
           o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
           //rim = 1.0 -viewDirの正規化したものとノーマルマップの内積(0~1の値を取る)
           half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
           //Emissionに代入 propertyのRimColorの値(RGB)× rim^_RimPower= 0~1の値を取る 参考https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-pow 
           o.Emission = _RimColor.rgb * pow(rim, _RimPower);
       }
       ENDCG
    }
        Fallback "Diffuse"
}
●スライスしている処理のパラメータ

 前回の記事で詳しく勉強しましたが、スライスはClip()を用いています。

  void surf(Input IN, inout SurfaceOutput o) {
          clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);
       }

 ここで登場するパラメータは

 ・IN.worldPos.y=ワールド座標のy

 ・IN.worldPos.z=ワールド座標のz

 ・IN.worldPoszに掛かっている定数0.1

 ・(IN.worldPos.y+IN.worldPos.z*0.1) に掛かっている定数 5

 ・frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) に減算されている定数 0.5

 このうちワールド座標はすでに変数として固定されているので固定されている定数は0.1、5、0.5の3つになります。

 これらをマテリアルで扱えるようにします。

●Propeties

 UnityのマテリアルでShaderのプロパティを扱うためにはPropeties{}に記述します。

Propeties{
        _MainTex("Texture",2D) = "white"{}
        _BumpMap("Bumpmap", 2D) = "bump"{}
        _RimColor("Rim Color", Color) = (0.26,0.19,0.0)
            //リムpowerのレンジ defaultでは3に設定される。
         _RimPower("Rim Power", Range(0.5,8.0)) = 3.0
            //SecondryTexture
          _Detail("Detail", 2D) = "gray"{}

        //追加
    _SlicePower1("SlicePower1",Range(0,1))=0.1
 _SlicePower2("SlicePower2",Range(0,10))=5 
 _SlicePower3("SlicePower3",Range(0,1))=0.5   
}

これでUnityのマテリアル側でスライダーが扱えるようになります。

f:id:Holomoto-Sumire:20200427091739j:plain

●Surf関数の処理の定数を変数に置き換える。

surf関数でスライダーの値を扱うために宣言をします。

float _SlicePower1;
float _SlicePower2;
float _SlicePower3;
 void surf(Input IN,input surfaceOutput o){

}

次に定数を_SlicePowerに置き換えます。

  void surf(Input IN, inout SurfaceOutput o) {
          clip (frac((IN.worldPos.y+IN.worldPos.z*_SlicePower1) * _SlicePower2) - _SlicePower3);
       }

これでUnityのマテリアル側からスライダーを操作することでスライスする範囲や向きを変更できるようになりました。

f:id:Holomoto-Sumire:20200427092914g:plain

●スライスをワールド座標からオブジェクトのローカル座標に変更する。
  void surf(Input IN, inout SurfaceOutput o) {
          clip (frac((IN.worldPos.y+IN.worldPos.z*_SlicePower1) * _SlicePower2) - _SlicePower3);
       }

クリップはワールド座標のy,zが変数として与えられています。

ここをオブジェクトのローカル座標に変更します。

  void surf(Input IN, inout SurfaceOutput o) {
          clip (frac((IN.uv_MainTex.y+IN.uv_MainTex.z*_SlicePower1) * _SlicePower2) - _SlicePower3);
       }

  ここではテクスチャと同じようにuv座標を用いました。しかしこのままではエラーを吐いてしまいます。

f:id:Holomoto-Sumire:20200427093658j:plain

これはテクスチャのuv座標は平面空間を扱うためzを扱っていないためです。

  struct Input {
           float2 uv_MainTex;
       };

そのためx,yの座標に書き直します。

  void surf(Input IN, inout SurfaceOutput o) {
          clip (frac((IN.uv_MainTex.x+IN.uv_MainTex.y*_SlicePower1) * _SlicePower2) - _SlicePower3);
       }

これでローカルの座標でスライスが行えるようになりました。

f:id:Holomoto-Sumire:20200427094328g:plain

 ここまでの編集したShaderは以下になります。

●Diffuse LocalSlice Shader
//シンタックスShaderの名前と格納場所
Shader "Example/Diffuse LocalSlice"{

    Properties{
        _MainTex("Texture",2D) = "white"{}
        _BumpMap("Bumpmap", 2D) = "bump"{}
        _RimColor("Rim Color", Color) = (0.26,0.19,0.0)
            //リムpowerのレンジ defaultでは3に設定される。
            _RimPower("Rim Power", Range(0.5,8.0)) = 3.0
            //SecondryTexture
            _Detail("Detail", 2D) = "gray"{}
          _SlicePower1("SlicePower1",Range(0,1)) = 0.1
          _SlicePower2("SlicePower2",Range(0,10)) = 5
          _SlicePower3("SlicePower3",Range(0,1)) = 0.5
    }
        //Shaderの中身サブシェーダーではレンダリングパスの一覧を定義し、任意のオプションとしてすべてのパスに共通の State (状態)を設定します。追加で、サブシェーダーの特定の Tag を設定できます。
        SubShader{
        //RenderingType
            Tags{"RenderType" = "Opaque"}
            CGPROGRAM
            #pragma surface surf Lambert
        //Input構造体はテクスチャ座標として扱うものを記述します。
       struct Input {
           float2 uv_MainTex;
           float2 uv_BumpMap;
           float3 viewDir;
           float2 uv_Detail;
           //ワールド座標を使用しないためコメントアウト
           //float3 worldPos;
       };
       sampler2D _MainTex;
       sampler2D _BumpMap;
       float4 _RimColor;
       float _RimPower;
       sampler2D _Detail;
       float _SlicePower1;
       float _SlicePower2;
       float _SlicePower3;
       void surf(Input IN, inout SurfaceOutput o) {
           o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
           //_Detailのrgbをo.Albedoに乗算
           o.Albedo *= tex2D(_Detail, IN.uv_Detail).rgb * 2;
           clip(frac((IN.uv_MainTex.x + IN.uv_MainTex.y * _SlicePower1) * _SlicePower2) - _SlicePower3);
           o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
           //rim = 1.0 -viewDirの正規化したものとノーマルマップの内積(0~1の値を取る)
           half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
           //Emissionに代入 propertyのRimColorの値(RGB)× rim^_RimPower= 0~1の値を取る 参考https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-pow 
           o.Emission = _RimColor.rgb * pow(rim, _RimPower);
       }
       ENDCG
    }
        Fallback "Diffuse"
}

〇裏面を表示させる

 sliceを行うことができましたが、メッシュの裏面が描画されないため現実では不自然なオブジェクトになっています。

 最後に裏面を表示させる処理を行います。

//シンタックスShaderの名前と格納場所
Shader "Example/Diffuse Detail"{

    Properties{
    }
        SubShader{
            Tags{"RenderType" = "Opaque"}

   //追加
            Cull Off

            CGPROGRAM
            ENDCG
    }
        Fallback "Diffuse"
}

追加したものは以下の一文です

Cull Off

この一文を記述することで裏面も描画されるようになります。

f:id:Holomoto-Sumire:20200427095038j:plain

完成したShaderは以下になります。

//シンタックスShaderの名前と格納場所
Shader "Example/Diffuse LocalSlice CullOff"{

    Properties{
        _MainTex("Texture",2D) = "white"{}
        _BumpMap("Bumpmap", 2D) = "bump"{}
        _RimColor("Rim Color", Color) = (0.26,0.19,0.0)
            //リムpowerのレンジ defaultでは3に設定される。
            _RimPower("Rim Power", Range(0.5,8.0)) = 3.0
            //SecondryTexture
            _Detail("Detail", 2D) = "gray"{}
          _SlicePower1("SlicePower1",Range(0,1)) = 0.1
          _SlicePower2("SlicePower2",Range(0,10)) = 5
          _SlicePower3("SlicePower3",Range(0,1)) = 0.5
    }
        //Shaderの中身サブシェーダーではレンダリングパスの一覧を定義し、任意のオプションとしてすべてのパスに共通の State (状態)を設定します。追加で、サブシェーダーの特定の Tag を設定できます。
        SubShader{
        //RenderingType
            Tags{"RenderType" = "Opaque"}
           //メッシュの裏表両面を描画
           Cull Off
            CGPROGRAM
            #pragma surface surf Lambert
        //Input構造体はテクスチャ座標として扱うものを記述します。
       struct Input {
           float2 uv_MainTex;
           float2 uv_BumpMap;
           float3 viewDir;
           float2 uv_Detail;
           //ワールド座標を使用しないためコメントアウト
           //float3 worldPos;
       };
       sampler2D _MainTex;
       sampler2D _BumpMap;
       float4 _RimColor;
       float _RimPower;
       sampler2D _Detail;
       float _SlicePower1;
       float _SlicePower2;
       float _SlicePower3;
       void surf(Input IN, inout SurfaceOutput o) {
           o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
           //_Detailのrgbをo.Albedoに乗算
           o.Albedo *= tex2D(_Detail, IN.uv_Detail).rgb * 2;
           clip(frac((IN.uv_MainTex.x + IN.uv_MainTex.y * _SlicePower1) * _SlicePower2) - _SlicePower3);
           o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
           //rim = 1.0 -viewDirの正規化したものとノーマルマップの内積(0~1の値を取る)
           half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
           //Emissionに代入 propertyのRimColorの値(RGB)× rim^_RimPower= 0~1の値を取る 参考https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-pow 
           o.Emission = _RimColor.rgb * pow(rim, _RimPower);
       }
       ENDCG
    }
        Fallback "Diffuse"
}

今回は前回のスライスを扱うShaderを編集して使いやすくしました。