【UnityLab,Cg】基礎的なシェーダーを試してみる【リムシェーダー】

# はじめに
Unityシェーダーに入門したので基本的なシェーダーのメモを残したいと思います。
EditorはVSCodeで下記の拡張と組合わせて使用していますが、もっと使いやすい環境があれば教えて頂けると幸いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/51f36df4-9701-e054-f268-18c32b3337d5.png)


# 最低限のリムシェーダー
* メッシュの淵部分が光るシェーダー
* 法線が横を向いている場合、視線(viewDir)の内積を取るとゼロになるが、法線がカメラの方向を向いている場合の内積は１になる。<br>
それを利用して、1-(法線と視線の内積)でリムの強度を算出することで実現できる。
* ゲームのダメージを受けている感、パワーアップしている感、状態異常感とかを出したり、選択されている状態を可視化するとかで使えそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/6609890c-be35-da6e-4e41-275a2d9b5ea4.png)

powで計算する係数をInspectorで設定できるようにすると、強度調整もできるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/3f0a17f0-f02b-72a4-1d44-19342a853f4e.png)

```hlsl:rim.shader
Shader "Marron/Rim" {
  // Inspectorからアクセス可能にする
  Properties {
    _texture("Texture", 2D) = "white" {}
    _RimColor("RimColor", Color) = (0.5,0.5,0.5,0.0)
    _RimPower("Pow", Range(0.1,8.0)) = 3.0
  }
  // シェーダー本体
  SubShader {
    // 記述言語
    CGPROGRAM // C for Graphic

    // Surface Shader関数の関数名をsurfとして定義、
    #pragma surface surf Lambert
    sampler2D _texture; // プロパティの"Texture"をシェーダー内で使用できるようにする
    float4 _RimColor;   // プロパティの"RimColor"をシェーダー内で使用できるようにする
    float _RimPower;    // プロパティの"RimPower"をシェーダー内で使用できるようにする
    // surf関数内で使用できるInput構造体の定義、
    struct Input {
      float3 viewDir;     // 視線
      float2 uv_texture;  // "sampler2D _texture"の反映先の座標
    };

    void surf (Input IN, inout SurfaceOutput o) {
      half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal)); // 1-(法線と視線の内積) つまり横を向いてる面は1で正面を向いている面は0
      o.Emission = _RimColor.rgb * pow(rim, _RimPower); // リムを設定
      o.Albedo = tex2D(_texture, IN.uv_texture);        // _textureの色情報をuv座標に反映
    }
    ENDCG
  }
  Fallback "Diffuse" // エラーが発生したらピンク表示
}
```

### 透過対応
* alfaパラメーターを追加
* Surfaceシェーダー設定でalfaを追加する
* 
```hlsl:RimTransparent.shader
Shader "Marron/RimTransparent" {
  Properties {
    _texture("Texture", 2D) = "white" {}
    _Alfa("Alfa", Range(0.0,1.0)) = 1.0
    _RimColor("RimColor", Color) = (0.5,0.5,0.5,0.0)
    _RimPower("Pow", Range(0.1,8.0)) = 3.0
  }
  // シェーダー本体
  SubShader {
    Tags { "RenderType"="Transparent" "Queue"="Transparent"}
    CGPROGRAM // C for Graphic

    // サーフェスシェーダーにalpha対応させる
    #pragma surface surf Lambert alpha
    sampler2D _texture;
    float _Alfa; // Inspectorで設定した_Alfaをシェーダーで使用できるようにする
    float4 _RimColor;
    float _RimPower;

    struct Input {
      float3 viewDir;
      float2 uv_texture;
    };

    void surf (Input IN, inout SurfaceOutput o) {
      half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal)); // 1-(法線と視線の内積) つまり横を向いてる面は1で正面を向いている面は0
      o.Emission = _RimColor.rgb * pow(rim, _RimPower);
      fixed4 c = tex2D(_texture, IN.uv_texture);
      o.Albedo = c.rgb;        // 色を渡す
      o.Alpha = _Alfa;         // アルファを渡す
    }
    ENDCG
  }
  Fallback "Diffuse" // エラーが発生したらピンク表示
}
```


これでシェーダーを反映させると、尻尾部分のリムが見えてしまう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/48339796-8cd8-9f4f-ba6a-d2ef6f55d863.png" width="50%">
これはデプスバッファ(Zバッファ)への書き込みを行っていないため、裏側が後から描画され上書きされている状態です。

### 透過対応修正版
* 裏側も見えてもいい場合は対応不要
* ２パスに分け、１パス目は描画を行わずにデプス値だけを取りこむようにする。

```hlsl:RimTransparent.shader
Shader "Marron/RimTransparent" {
  Properties {
    _texture("Texture", 2D) = "white" {}
    _Alfa("Alfa", Range(0.0,1.0)) = 1.0
    _RimColor("RimColor", Color) = (0.5,0.5,0.5,0.0)
    _RimPower("Pow", Range(0.1,8.0)) = 3.0
  }
  // シェーダー本体
  SubShader {
    // 1回目のレンダリング
    Tags { "RenderType"="Transparent" "Queue"="Transparent"}
    Pass
    {
      ZWrite ON   // デプスバッファ書き込みモードを有効に設定、デプス値を読み込む
      ColorMask 0 // レンダリングしない
    }

    // 2回目のレンダリング ※SurfaceシェーダーではPassは記述不要
    Tags { "RenderType"="Transparent" "Queue"="Transparent"}
    // 以下デフォルト設定だが理解の為に記述
    ZWrite OFF   // デプスバッファ書き込みモードを無効に設定(デフォルト)
    Ztest LEqual // すでに描画されているオブジェクトと距離が等しいかより近い場合に描画(デフォルト)

    CGPROGRAM
    #pragma surface surf Lambert alpha

    sampler2D _texture;
    float _Alfa;
    float4 _RimColor;
    float _RimPower;

    struct Input {
      float3 viewDir;
      float2 uv_texture;
    };

    void surf (Input IN, inout SurfaceOutput o) {
      half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal)); // 1-(法線と視線の内積) つまり横を向いてる面は1で正面を向いている面は0
      o.Emission = _RimColor.rgb * pow(rim, _RimPower);
      fixed4 c = tex2D(_texture, IN.uv_texture);
      o.Albedo = c.rgb;        // 色を渡す
      o.Alpha = _Alfa;         // アルファを渡す
    }
    ENDCG
  }
  Fallback "Diffuse" // エラーが発生したらピンク表示
}
```

これで透過処理で背面が透けなくなる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/ccfa6efe-6068-850b-c692-8479d62b5003.png" width="50%">