【Unity(Shader)】VR空間で違和感なく透過する手のシェーダー


Shaderなんもわからん

本当に何もわかりません。
なので結果的にいい感じになりましたというメモです。

デモ

左手が今回いい感じになったShaderです。
透過しつつ、いい感じに立体感もあり、指が重なって二重に見えることはありません。

ちなみに右手はOculus Integrationの中に入っているHands_Transparentです。
左手はそれをちょっと変えただけです。

Shader

    Shader "Custom/SemiTransparent" {
    Properties { 
        _MainColor("_Color",Color) = (0,0,0,0)
        _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
        _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0
    } 

    SubShader {
        Tags { "Queue" = "Transparent" }
        LOD 200

        Pass{
            ZWrite On
            ColorMask A
        }

        CGPROGRAM
        #pragma surface surf Standard alpha:fade 
        #pragma target 3.0

        struct Input {           
           float3 viewDir;
        };

        fixed4 _MainColor;    
        float4 _RimColor;
        float _RimPower;   

        void surf (Input IN, inout SurfaceOutputStandard o) {
            o.Albedo = _MainColor;
            o.Alpha = _MainColor.a;
            half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
            o.Emission = _RimColor.rgb * pow (rim, _RimPower);
        }
        ENDCG
    }
    FallBack "Diffuse"
}

float? half? fixed?

まず変数に見たことがないものがあって混乱しました。
Scriptリファレンスには下記のようにありました。

できるかぎり低い精度を使用します。これは iOS や Android などモバイルプラットフォームでは特に重要です。経験則から以下のようにします。
・ワールド空間の位置とテクスチャ座標には、float 型を使用してください。
・上記以外 (ベクトル、HDR カラーなど) には、half で始めます。必要なときだけ、増加します。
・テクスチャデータのとても簡易な操作には、fixed を使用します。
実際には、正確にはどの数の型を使用すべきかは、プラットフォームや GPU に依存します。

深く調べるとさらに難しいことが書いてありそうだったので、
現段階では返す数値の精度が違うとだけ覚えておきます。

Pass

Passは簡単に言うとこんな感じに描画しますよってことらしいです。
複数回に分けて書くことができる(複数回に分けて描画することができる)みたいです。

Surface shaderはデフォルトではPassと書かれていないですが、
内部でPassとして展開されるそうです。

今回のshaderに当てはめると下記のようなイメージです。

SubShader {
        Tags { "Queue" = "Transparent" }
        LOD 200

        Pass{
            ZWrite On
            ColorMask 0
        }

        /*本来は内部ではPassとして展開される処理*/
        Pass{
            CGPROGRAM
            /*~省略~*/
            ENDCG
        }
     }

つまり、今回書いたシェーダーにはPassが2つあるということになります。

主に追加したのは1パス目だけです。
なので、その説明をしていこうと思います。

ZWrite1

ZWriteはDepth Bufferを書き込むかどうかOn,Offできます。

最初は意味不明でしたが、
Depth Buffer = 深度情報 = どっちが前にあるか(後ろにあるか)判別するための情報
という理解で着地しました。

Depth Buffer(深度情報)が与えられていない手のShaderを見るとよくわかります。

どの指が手前にあるのかを判別するZWriteをShader上でOnにしていないので、
人差し指より前側に他の指が描画されてしまっているということです。

ColorMask

ColorMaskは使用するカラーチャンネル(RGBA)を指定することができます。
0を指定するとすべてのチャンネルを無視することができるので何も書き込まれません。

つまり、1パス目はDepth Buffer(深度情報)を書き込むためだけのコードということです。

    Pass{
       ZWrite On
       ColorMask 0
    }

2パス目にZWrite On書いとけばいいんじゃないか?
と思って試しましたが、ダメでした。先にDepth Buffer(深度情報)を書き込んであげる必要があるようです。

Shaderなんもわからん

本当に何もわからないので間違い、アドバイス等ありましたらよろしくお願いします。