Unity デッサン風 ポストエフェクト

# 作ったもの
![drawing.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526299/09b01ac1-bd8d-6451-edd8-98716c55de68.gif)

https://github.com/Dumble009/Drawing

# アルゴリズムの概要

1. C# スクリプトで画面全体に広がるパーリンノイズのテクスチャを生成。
1. ポストエフェクトシェーダで各ピクセルの明度を計算。
1. 明度をいくつかの段階に分割して、各段階に応じてパーリンノイズのテクスチャに係数をかけて色をピックアップ。

もう知られているやり方だったらすみません

# ソースコード

## C#
```cs
using System.Collections;
using UnityEngine;

public class DrawingTextureGenerator : MonoBehaviour
{
	Texture2D m_texture;

	[SerializeField] int m_scale;
	[SerializeField] float m_power;
	[SerializeField] float m_cor = 1;
	[SerializeField] Material m_postEffectMat;
	[SerializeField] float m_scaleX = 1;
	[SerializeField] float m_scaleY = 1;

	private void Awake()
	{
		int w = Screen.width;
		int h = Screen.height;
		m_texture = new Texture2D(w, h, TextureFormat.ARGB32, false);
	}

	private void Start()
	{
		StartCoroutine(UpdateImage());
		m_postEffectMat.SetTexture("_DrawingTex", m_texture);
	}

	IEnumerator UpdateImage()
	{
		// 1. パーリンノイズテクスチャの生成
		while (true)
		{
			int w = Screen.width;
			int h = Screen.height;
			float t = Time.realtimeSinceStartup;
			for (int y = 0; y < h; y++)
			{
				for (int x = 0; x < w; x++)
				{
					float sample = Mathf.PerlinNoise((float)x / w * m_scale * m_scaleX + Random.value, (float)y / h * m_scale * m_scaleY);
					sample = Mathf.Pow(sample, m_power) * m_cor;
					m_texture.SetPixel(x, y, new Color(sample, sample, sample));

					if(Time.realtimeSinceStartup - t >= 1.0f / 60.0f)
					{
						yield return null;
						t = Time.realtimeSinceStartup;
					}
				}
			}

			m_texture.Apply();
		}
	}
}


```

## シェーダ

```
Shader "Hidden/Drawing"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _DrawingTex ("Drawing", 2D) = "white" {}
        _Threshold1 ("Threshold1", Float) = 0
        _Threshold2 ("Threshold2", Float) = 0
        _Threshold3 ("Threshold3", Float) = 0
        _Threshold4 ("Threshold4", Float) = 0

        _Magni1 ("Magni1", Float) = 0
        _Magni2 ("Magni2", Float) = 0
        _Magni3 ("Magni3", Float) = 0
        _Magni4 ("Magni4", Float) = 0

        _Xdiff ("Xdiff", Float) = 0
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            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;
            sampler2D _DrawingTex;
            float _Threshold1;
            float _Threshold2;
            float _Threshold3;
            float _Threshold4;
            float _Magni1;
            float _Magni2;
            float _Magni3;
            float _Magni4;

            float _Xdiff;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);

                // 2. ピクセルの明度を計算
                half brightness = dot(col, half3(0.299, 0.587, 0.114));
                
                // 3. 明度に応じてパーリンノイズのテクスチャに係数をかけて色をピックアップ
                fixed4 drawingCol = tex2D(_DrawingTex, float2(i.uv.x + _Xdiff * i.uv.y, i.uv.y));
                if(brightness >= 0.9){
                    drawingCol = 1;
                } else if (brightness >= _Threshold1){
                    drawingCol *= _Magni1;
                } else if (brightness >= _Threshold2){
                    drawingCol *= _Magni2;
                } else if (brightness >= _Threshold3){
                    drawingCol *= _Magni3;
                }else if(brightness >= _Threshold4){
                    drawingCol *= _Magni4;
                }
                return drawingCol;
            }
            ENDCG
        }
    }
}


```

パーリンノイズのテクスチャから色をピックアップする際に斜めにずらすことで、デッサン感を出しています。
また、パーリンノイズテクスチャの生成時に X 座標値をランダムにずらしています。こうした方がいい感じに見えたので。
冒頭の gif 画像のようにするパラメータは以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526299/b4cee06f-7fd9-7e04-0546-317938b56030.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526299/a89ae907-db02-d3aa-3195-a79442317294.png)

パーリンノイズの生成が重いので、実際にゲームで使う場合には一度作ったものを使いまわすと良いかと思います。