夜風のMixedReality

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

MRTK StanderdShaderを読み解く その10 透明度

本日はMRTKShader調査枠です。

本日は透過率に関して学んでいきます。

〇Blending

ShaderLabでは透明度を持つShaderを作成するためにBlendingを使用します。

docs.unity3d.com

〇MRTKの透過率

MRTKStandardShaderではBlendはPassブロック内の頭、Tag{}の次に記述されています。

       ...
        Pass
        {
            Name "Main"
            Tags{ "RenderType" = "Opaque" "LightMode" = "ForwardBase" }
            
            Blend[_SrcBlend][_DstBlend]
         ...

MRTKStandardShaderのBlendは[ _ SrcBlend]と[_ DstBlend]の変数によって決まっています。

[_SrcBlend]はPropertiesで定義されています。

        [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Source Blend", Float) = 1                 // "One"

[Enum()]アトリビュートデータ形式Int/Floatの場合に利用でき、インスペクタ上にプルダウンリストを表示します。

ここではUnityEngine.Rendering.BlendModeで記述されているものがリスト表示されます。

docs.unity3d.com

 初期値では右辺が1となっているためOne =Blend factor is (1, 1, 1, 1).が選択されます。

[ _DstBlend]もPropertiesで定義されており、同様にBlendModeのZeroが初期値で選択されます。

        [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("Destination Blend", Float) = 0            // "Zero"

つまり初期の状態の場合Blendは

Blend One Zero

というようになります。

Oneはレンダリング結果の色をそのまま使用(=100%)する際に使用され、Zeroはレンダリング結果の色を削除(=0%)する際に使用します。

Blend One ZeroではShaderのレンダリング結果をそのまま使用するのと削除してしまうものをBlend(=合成)しています。

 イメージとして二枚の画像(レンダリング結果)があって下画像が透明、上画像がオリジナルのレンダリング結果を持ち、透明画像の上で描画されているのでColorのα値によって上画像が半透明に描画できるといった感じでしょうか? 

 筆者はスケッチブック(Blend One One)にスケッチしていた場合とガラス板の上でスケッチしていた場合という理解をしました。

 前者の場合スケッチの絵の具がいかに透明度を持っていたとしてももともとの紙が透明度を持っていないため最終的な絵は透明度を持たないものになります。

 後者の場合絵の具が透明度を持っていたら最終的な絵はステンドグラスのように透明度を持った絵になります(違ったらこっそりご指摘ください。)

 Blendingを指定することで透過率を使用できるShaderとなりました。

f:id:Holomoto-Sumire:20200801200941j:plain   透過率はカラーのα値に対応しているためプロパティ側で自由に変えることができます。

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

〇今回作成したShader

Shader "Custom/Standard"
{
    Properties
    {
        // Main maps.
        _Color("Color", Color) = (1.0, 1.0, 1.0, 1.0)
        _MainTex("Albedo", 2D) = "white" {}
        _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
        _Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5

        // Rendering options.
        [Toggle(_DIRECTIONAL_LIGHT)] _DirectionalLight("Directional Light", Float) = 1.0
        [Toggle(_SPECULAR_HIGHLIGHTS)] _SpecularHighlights("Specular Highlights", Float) = 1.0
     
        // Advanced options.
        [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Source Blend", Float) = 1                 // "One"
        [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("Destination Blend", Float) = 0            // "Zero"
    }

    SubShader
    {
        Pass
        {
            Name "Main"
            Tags{ "RenderType" = "Opaque" "LightMode" = "ForwardBase" }

            Blend[_SrcBlend][_DstBlend]//Blending
             
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag


            #pragma multi_compile_instancing
            #pragma multi_compile _ LIGHTMAP_ON
            #pragma multi_compile _ _CLIPPING_PLANE _CLIPPING_SPHERE _CLIPPING_BOX
        
            #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON

            #pragma shader_feature _DIRECTIONAL_LIGHT



            #define IF(a, b, c) lerp(b, c, step((fixed) (a), 0.0)); 
            
            #include "UnityCG.cginc"
            #include "UnityStandardConfig.cginc"
            #include "UnityStandardUtils.cginc"
            #include "MixedRealityShaderUtils.cginc"

            // This define will get commented in by the UpgradeShaderForLightweightRenderPipeline method.
            //#define _LIGHTWEIGHT_RENDER_PIPELINE

#if defined(_DIRECTIONAL_LIGHT)
            #define _NORMAL
#else
            #undef _NORMAL
#endif



#if defined(_NORMAL)
            #define _WORLD_POSITION
#else
            #undef _WORLD_POSITION
#endif



#if defined(_DIRECTIONAL_LIGHT) 
            #define _FRESNEL
#else
            #undef _FRESNEL
#endif


            #define _UV


            struct appdata_t
            {
                float4 vertex : POSITION;
                // The default UV channel used for texturing.
                float2 uv : TEXCOORD0;

                // Used for smooth normal data (or UGUI scaling data).
                float4 uv2 : TEXCOORD2;
                // Used for UGUI scaling data.
                float2 uv3 : TEXCOORD3;

                fixed3 normal : NORMAL;

                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f 
            {
                float4 position : SV_POSITION;
#if defined(_UV)
                float2 uv : TEXCOORD0;
#endif


#if defined(_WORLD_POSITION)

                float3 worldPosition : TEXCOORD2;

#endif

#if defined(_NORMAL)

                fixed3 worldNormal : COLOR3;
#endif
                UNITY_VERTEX_OUTPUT_STEREO
            };


            fixed4 _Color;
            sampler2D _MainTex;
            fixed4 _MainTex_ST;

            fixed _Metallic;
            fixed _Smoothness;


#if defined(_DIRECTIONAL_LIGHT)

            fixed4 _LightColor0;
#endif



#if defined(_DIRECTIONAL_LIGHT)
            static const fixed _MinMetallicLightContribution = 0.7;
            static const fixed _IblContribution = 0.1;
#endif



#if defined(_FRESNEL)
            static const float _FresnelPower = 8.0;
#endif






            v2f vert(appdata_t v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

                float4 vertexPosition = v.vertex;

#if defined(_WORLD_POSITION) 
                float3 worldVertexPosition = mul(unity_ObjectToWorld, vertexPosition).xyz;
#endif
                fixed3 localNormal = v.normal;

#if defined(_NORMAL) 
                fixed3 worldNormal = UnityObjectToWorldNormal(localNormal);
#endif

                o.position = UnityObjectToClipPos(vertexPosition);

#if defined(_WORLD_POSITION)
                o.worldPosition.xyz = worldVertexPosition;
#endif


#if defined(_UV)
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
#endif


#if defined(_NORMAL)
                o.worldNormal = worldNormal;
#endif

                return o;
            }

            fixed4 frag(v2f i, fixed facing : VFACE) : SV_Target
            {
                fixed4 albedo = tex2D(_MainTex, i.uv);

                albedo *= _Color;


                // Normal calculation.
#if defined(_NORMAL)
                fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPosition.xyz));
                fixed3 worldNormal;
                worldNormal = normalize(i.worldNormal) * facing;
#endif

                fixed pointToLight = 1.0;
                fixed3 fluentLightColor = fixed3(0.0, 0.0, 0.0);

                // Blinn phong lighting.
#if defined(_DIRECTIONAL_LIGHT)

                float4 directionalLightDirection = _WorldSpaceLightPos0;

                fixed diffuse = max(0.0, dot(worldNormal, directionalLightDirection));

                fixed specular = 0.0;
#endif

                // Image based lighting (attempt to mimic the Standard shader).

                fixed3 ibl = unity_IndirectSpecColor.rgb;


                // Fresnel lighting.
#if defined(_FRESNEL)
                fixed fresnel = 1.0 - saturate(abs(dot(worldViewDir, worldNormal)));

                fixed3 fresnelColor = unity_IndirectSpecColor.rgb * (pow(fresnel, _FresnelPower) * max(_Smoothness, 0.5));
#endif
                // Final lighting mix.
                fixed4 output = albedo;

                fixed3 ambient = glstate_lightmodel_ambient + fixed3(0.25, 0.25, 0.25);

                fixed minProperty = min(_Smoothness, _Metallic);
#if defined(_DIRECTIONAL_LIGHT)
                fixed oneMinusMetallic = (1.0 - _Metallic);
                output.rgb = lerp(output.rgb, ibl, minProperty);

                fixed3 directionalLightColor = _LightColor0.rgb;
                output.rgb *= lerp((ambient + directionalLightColor * diffuse + directionalLightColor * specular) * max(oneMinusMetallic, _MinMetallicLightContribution), albedo, minProperty);
                output.rgb += (directionalLightColor * albedo * specular) + (directionalLightColor * specular * _Smoothness);
                output.rgb += ibl * oneMinusMetallic * _IblContribution;
#endif

#if defined(_FRESNEL)
#if !defined(_REFLECTIONS)
                output.rgb += fresnelColor;
#else
                output.rgb += fresnelColor * (1.0 - minProperty);
#endif
#endif

                return output;
            }

            ENDCG
        }
    }
    
    Fallback "Hidden/InternalErrorShader"
}

〇参考にしたブログ

baba-s.hatenablog.com