【Unity】【シェーダテクニック】ノイズを使って細かい線を作る

f:id:halya_11:20180906193647p:plain

ノイズテクスチャを使って細かい線を引いてみます。

ノイズテクスチャを作る

まずスクリプトでノイズテクスチャを作り、グローバルテクスチャとしてセットしておきます。

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

[ExecuteInEditMode]
public class Example : MonoBehaviour
{

    [SerializeField]
    private int _width = 128;
    [SerializeField]
    private int _height = 128;
    [SerializeField]
    private Texture2D _texture;

    public void CreateTexture()
    {
        if (_width <= 0 || _height <= 0)
        {
            return;
        }
        // ノイズテクスチャを生成
        _texture = new Texture2D(_width, _height, TextureFormat.ARGB32, false, true);
        for (int i = 0; i < _width; i++)
        {
            for (int j = 0; j < _height; j++)
            {
                var color = new Color();
                for (int k = 0; k < 4; k++)
                {
                    color[k] = Random.Range(0.0f, 1.0f);
                }
                _texture.SetPixel(i, j, color);
            }
        }
        _texture.Apply();

        // グローバルテクスチャとしてセットする
        Shader.SetGlobalTexture("_Noise", _texture);
    }

    private void Awake()
    {
        CreateTexture();
    }
}

#if UNITY_EDITOR
[CustomEditor(typeof(Example))]
public class ExampleEditor : Editor
{

    public override void OnInspectorGUI()
    {
        using (var s = new EditorGUI.ChangeCheckScope())
        {
            base.OnInspectorGUI();

            if (s.changed)
            {
                (target as Example).CreateTexture();
            }
        }
    }
}
#endif

こんな感じのテクスチャが生成されます。

f:id:halya_11:20180906193934p:plain

シェーダその1

テクスチャをサンプリングするシェーダを書いてみます。

Shader "Example"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        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;
            };

            sampler2D _Noise;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex    = UnityObjectToClipPos(v.vertex);
                o.uv        = v.uv;
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col  = tex2D(_Noise, i.uv).r;
                col.a       = 1;
                return col;
            }
            ENDCG
        }
    }
}

普通にサンプリングしているだけですが、ノイズテクスチャのサイズを10*1024にすると、下記のような結果となります。

f:id:halya_11:20180906193535p:plain

ピクセルに対してテクセルが少ないため、引き伸ばされてこのようになります。

シェーダその2

ただこれだと縦に白い線が入っているように見えるのでシェーダを修正します。
サンプリングするU座標をちょっとずつずらせば良さそうです。

Shader "Example"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        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;
            };

            sampler2D _Noise;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex    = UnityObjectToClipPos(v.vertex);
                o.uv        = v.uv;
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // vに応じてuにオフセットを掛ける
                fixed uOffset   = tex2D(_Noise, fixed2(0, i.uv.y)).g;
                i.uv.x          += uOffset;
                fixed4 col  = tex2D(_Noise, i.uv).r;
                col.a       = 1;
                return col;
            }
            ENDCG
        }
    }
}

結果はこのようになります。

f:id:halya_11:20180906193647p:plain