夜風のMixedReality

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

SpatialMeshのスキャン表現に使える SpatialScanShaderを作成した。

本日はShader勉強枠です。

今回はHoloLens,HoloLens 2で使用する想定のオリジナルShader[SpatialScanShader]を作成しました。

〇使い方

MRTKを導入したプロジェクトから[SpatialAwareness]の構成で[SpatialScanShader]を使用したマテリアルをアタッチします。

〇実機で動かす。

youtu.be

〇解説

このShaderはMRTKで提供されているWireframe ShaderをベースにSR_TrianglesShaderのように空間メッシュに波紋が広がるような表現を目指しました。

WireframeShader、SR_TrianglesShaderではメッシュの情報を扱えるジオメトリシェーダーを使用することでメッシュごとに発色し表現を行っていました。

この場合ポリゴン単位の色の変化になるため滑らかな波を作成することはできませんでした。

このShaderではフラグメントシェーダーで重要な処理を行っています。

        float4 frag(v2f i ):COLOR{
            float radius;
            
            #if   _AUTO_WAVE_ON
            float speed = 1/_Speed;
                 radius =_WaveSize*(_Time/speed - floor(_Time/speed+1/2));
            #else
               radius = _Radius;
            #endif
          float dist = distance(_Center, i.worldPos);
            float Isize =_LineSize +_InnerSize;
            float intensity =1/_MasterIntensity ;
                float val = 1 - step(dist, radius - _LineSize) * _Inner;
                val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);
        }
return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);

valは次のようになります。

最終的な出力でvalという変数に_Colorで指定した色の要素が積算され出力されます。

                float val = 1 - step(dist, radius - _LineSize) * _Inner;
                val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);

lerp関数は第三引数で指定された割合で第一引数、第二引数の間を滑らかに補間する関数です。

                float val = 1 - step(dist, radius - _LineSize) * _Inner;

上部のこの式では円の内側を決める処理になります。

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

step関数は段階的に値が変換される式です。これによってグラデーションを描くようになります。

_Innerはプロパティの[InnerIntensity]を指し、値を変えることで内部の発色具合を変えることができます。

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

次の処理をコメントアウトすることでこの一行で行っている処理を可視化できます。

                float val = 1 - step(dist, radius - _LineSize) * _Inner;
              //  val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);

f:id:Holomoto-Sumire:20210117125142p:plain

次の行では円の外側の描画に関する処理を行っています。

                val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);

●AutoWave

このチェックボックスにチェックを入れることでスクリプトなどで外部からRadiusを扱わなくても、Shaderの処理だけで自動的に波紋を広げます。

      #if   _AUTO_WAVE_ON
            float speed = 1/_Speed;
                 radius =_WaveSize*(_Time/speed - floor(_Time/speed+1/2));
            #else
               radius = _Radius;
            #endif

この場合radiusは時間で変化するノコギリ波のグラフになります。

ノコギリ波とは次のような図形のことです。

f:id:Holomoto-Sumire:20210117132443p:plain
https://ja.wikipedia.org/wiki/%E3%81%AE%E3%81%93%E3%81%8E%E3%82%8A%E6%B3%A2 より引用

式で表すと次のようになります。

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

これによって周期的に波が一方的に広がります。 波は三角関数を使用することでも可能ですが、この場合広がるだけではなく収縮するため今回はノコギリ波を使用しました。

_WaveSizeは振幅を指します。 これはUintyの場合メートル単位に相当しており、最終的に広がる波の大きさに相当します。

_TimeはUnityでの時間に相当してます。このノコギリ波の場合横軸に相当します。

sppedはノコギリ波の周期に相当します。speedが大きいほど素早く波が広がります。

 

〇shader全文

Shader "HoloMoto/SpatialScener"
{
    Properties {
        _Color ("Color", Color) = (1, 1, 1, 1)
        _Center ("CenterX", vector) = (0, 0, 0)
         [Toggle] _Auto_Wave("AutoWave", Float) = 0
        _WaveSize("MaxWaveSize",Float)=10
        _Speed("WaveSpeed",float)=1
        _Radius ("Radius", float) = 5
        _LineSize("LineSize",Range(0,1))=0.075
        _Inner("InnerIntensity",Range(0,1))=0.806
        _InnerSize("InnerSize",Range(0,200))=0.14
        _MasterIntensity("MasterIntensity",Range(0,10))=0.2
    }
    SubShader
    {
      Tags{"RenderType"="Opaque"}
      Blend SrcAlpha OneMinusSrcAlpha
      BlendOp Add
      ZTest LEqual
      ZWrite On
      Cull Back
        Pass{
        CGPROGRAM
        #pragma  vertex vert
        #pragma  fragment frag
        
        #pragma shader_feature _AUTO_WAVE_ON
        #include  "UnityCG.cginc"
            float4 _Color;
            float3 _Center;
            float _Radius;
            float _LineSize;
            float _Inner;
            float _InnerSize;
            float _MasterIntensity;
        struct v2f
        {
            float4 pos : SV_POSITION;
            float3 worldPos : TEXCOORD1;
        };

        v2f vert(appdata_base v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
            return o;
        }
        float _Speed;
        float _Test;
        float _WaveSize;
        float4 frag(v2f i ):COLOR{
            float radius;
            
            #if   _AUTO_WAVE_ON
            float speed = 1/_Speed;
                 radius =_WaveSize*(_Time/speed - floor(_Time/speed+1/2));
            #else
               radius = _Radius;
            #endif
          float dist = distance(_Center, i.worldPos);
            float Isize =_LineSize +_InnerSize;
            float intensity =1/_MasterIntensity ;
                float val = 1 - step(dist, radius - _LineSize) * _Inner;
                val = lerp(radius - Isize, dist,radius - 3) * step(dist, radius) * val/intensity;     
                return fixed4(val * _Color.r, val * _Color.g,val * _Color.b, 1.0);
        }
        ENDCG    
            }
        
    }
    FallBack "Diffuse"
    FallBack "Mixed Reality Toolkit/Standard"
}