HLSLでのScanLine Shader（走査線）実装

## はじめに
今回はScanLine shaderの実装について記事にしたいと思います。

ScanLineはよく見る表現で、次のようなものです。
![Scan_Line_Shader_-_SampleScene_-_Windows__Mac__Linux_-_Unity_2021_2_1f1_Personal__DX11__2022-03-27_23-31-18_AdobeCreativeCloudExpress.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191216/ba1f7c58-6e49-dcfa-ac77-5114ca3ca1d0.gif)
ブラウン管のテレビのような雰囲気を醸し出すことができます。

プログラムは[GitHub](https://github.com/JunNishimura/ScanLineShader)にて公開しています。

## 実装
実装は大きく3ステップに分ける事ができます。

### ステップ1: 波
上の画像を見ると周期的に波打っている事が分かるので、sin, cosを使います。
白黒じゃつまらないので、rgbのgのみずらします。
```hlsl
float s = sin(i.uv.y * 1000);
float c = cos(i.uv.y * 1000);
col += float3(c, s, c) * _OpacityScanLine;
```

### ステップ2: ノイズ
絵全体にノイズを走らせます。

各画素に対して乱数を割り当てます。
ランダム関数はThe Book of Shadersにて掲載されていたものを用いました。
```hlsl
float random (float2 st) {
    return frac(sin(dot(st.xy, float2(12.9898,78.233)))*43758.5453123);
}
...
float r = random(i.uv*_Time);
col += float3(r, r, r) * _OpacityNoise;
```

### ステップ3: フラッシュ
絵全体をチカチカ光らせます。

これは単純にsinを使って周期的に光らせるだけです。
```hlsl
float flash = sin(_FlickeringSpeed*_Time);
col += float3(flash, flash, flash) * _FlickeringStrength;
```

### コード全文
最後にコード全文載せます。
```hlsl
Shader "Custom/ScanLine"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _OpacityScanLine ("Opacity ScanLine", Range(0, 2)) = 0.8
        _OpacityNoise ("Opacity Noise", Range(0, 1)) = 0.1
        _FlickeringSpeed ("Flickering Speed", Range(0, 1000)) = 600
        _FlickeringStrength ("Flickering Strength", Range(0, 0.1)) = 0.02
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            float random (float2 st) {
                return frac(sin(dot(st.xy, float2(12.9898,78.233)))*43758.5453123);
            }

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            half _OpacityScanLine;
            half _OpacityNoise;
            half _FlickeringSpeed;
            half _FlickeringStrength; 

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 img = tex2D(_MainTex, i.uv);
                float3 col = float3(0, 0, 0);
                float s = sin(i.uv.y * 1000);
                float c = cos(i.uv.y * 1000);
                col += float3(c, s, c) * _OpacityScanLine;

                float r = random(i.uv*_Time);
                col += float3(r, r, r) * _OpacityNoise;

                float flash = sin(_FlickeringSpeed*_Time);
                col += float3(flash, flash, flash) * _FlickeringStrength;
                return img * float4(col, 1.0);
            }
            ENDCG
        }
    }
}

```


## 終わりに
shader楽しいです。

## 参考資料
- [The Book of Shaders - random](https://thebookofshaders.com/10/?lan=jp)
- [Scanline - post process](https://www.shadertoy.com/view/3dBSRD)