画像生成からグラデーションマップを行うまで [Unity]



# 結果

![GradationMap.png](https://qiita-image-store.s3.amazonaws.com/0/113504/8472dbd4-5b55-1fb3-65ff-ab7ebe768531.png "GradationMap.png")

グラデーションの具合の調整をUnityの中でやるために，
グラデーション画像の生成をUnity側でやります．



# 1. Textureを生成する．

```csharp
public class GradationTexCreater{

	public GradationTexCreater(){
		
	}

	public Texture2D Create(Gradient gradient){
		return GradientToTexture (gradient);
	}

	/// <summary>
	/// グラデーションをそのまま画像にする．
	/// </summary>
	public static Texture2D GradientToTexture(Gradient gradient, int width = 256, int height = 1)
	{
		var texture = CreateTexture(width, height);
		for (int h = 0; h < texture.height; h++)
		{
			for (int w = 0; w < texture.width; w++)
			{
				float rate = (float)w / (float)texture.width;
				Color color = gradient.Evaluate(rate);
				texture.SetPixel(w, h, color);
			}
		}

		texture.Apply();
		return texture;
	}
	/// <summary>
	/// グラデーションを円形グラデーションのテクスチャにする．．
	/// </summary>
	public static Texture2D GradientToRadialGradationTex(Gradient gradient, int width = 256, int height = 1)
	{
		var texture = CreateTexture(width, height);
		for (int h = 0; h < texture.height; h++)
		{
			for (int w = 0; w < texture.width; w++)
			{
				float u = (float)w / (float)texture.width;
				float v = (float)h / (float)texture.height;
				//Color color = gradient.Evaluate(u);
				Color color = new RadialGradationColorCalclator(gradient).Evaluate(u, v);
				texture.SetPixel(w, h, color);
			}
		}

		texture.Apply();
		return texture;
	}

	/// <summary>
	/// 円形グラデーションのカラーを取得するためのクラス
	/// </summary>
	public class RadialGradationColorCalclator{
		public Color backGroundColor = Color.black;
		public float radius = 0.9f; // グラデーションの伸びる割
		public Gradient gradient;

		public RadialGradationColorCalclator(Gradient gradient){
			this.gradient = gradient;
		}

		// uvでカラーを取得 // 左下が0,0 to 1,1
		public Color Evaluate(float u, float v){
			Vector2 r = RemapMinus1To1 (u, v); // -1 -> 1
			float l = r.magnitude;
			if (l < radius) {
				float t = l / radius;
				return gradient.Evaluate (t);
			} else {
				return backGroundColor;
			}
		}

		Vector2 RemapMinus1To1(float u, float v){
			return new Vector2 (u - 0.5f, v - 0.5f) * 2.0f;
		}
	}


	/// <summary>
	/// グラデーション用の設定でTexture2Dを作成
	/// </summary>
	static Texture2D CreateTexture(int width, int height)
	{
		// Alpha使うのでRGB32にする. 使わないならRGB24
		Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, false);
		texture.wrapMode = TextureWrapMode.Clamp;
		return texture;
	}
}
```

Gradientから画像を生成します．
Gradientについては以下の記事に書きました．
https://qiita.com/Teach/items/3bc616d9f8115e80c31e

Texture2F作成の仕組みは以下です．
1. CreateTextureでwidthとheightを指定して生成
2. widthとheightの分だけFor分を回し，各pixelごとにSetPixelで色をつける．


フラグメントシェーダーに近い色の付け方です．

# 2. pngで保存する．

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;


#if UNITY_EDITOR
using UnityEditor;
#endif

public class GradationApplier : MonoBehaviour {

	public Gradient gradient;
	public string fileName = "gradation";

	[HideInInspector]
	public Texture2D texCache;
	public UnityEngine.UI.RawImage rawImage;
	public int height = 128;
	public int width = 128;
	public bool isRadial = false;
	public bool setGradationTexToMaterial = false;

	void OnValidate(){
		if (height < 0)
			height = 0;
		if (width < 0)
			width = 0;

		if(isRadial)
			texCache = GradationTexCreater.GradientToRadialGradationTex (gradient, width, height);
		else
			texCache = GradationTexCreater.GradientToTexture (gradient, width, height);
		if(rawImage !=null)
			rawImage.texture = texCache;
		if(texCache != null)
			texCache.name = fileName;

		if (setGradationTexToMaterial && texCache != null)
			this.GetComponent<SpriteRenderer> ().sharedMaterial.SetTexture ("_GradationTex", texCache);
	}
}

#if UNITY_EDITOR
[CustomEditor(typeof(GradationApplier))]
public class GradationApplierEditor : Editor{

	GradationApplier applier;

	void OnEnable(){
		applier = target as GradationApplier;
	}

	public override void OnInspectorGUI ()
	{
		DrawDefaultInspector ();
		if (GUILayout.Button ("Export")) {
			ExportTexture (applier.texCache);
		}
	}

	public static void ExportTexture(Texture2D tex){
		string path = GetSavePath (tex.name);
		Directory.CreateDirectory (Path.GetDirectoryName (path));
		File.WriteAllBytes(path, tex.EncodeToPNG());
		AssetDatabase.Refresh();
	}

	static string GetSavePath(string fileName){
		var path2 = string.Format ("Export/{0}.png", fileName);
		return Path.Combine (Application.dataPath, path2);
	}
}

#endif
```

ExportTextureで画像を保存します．
![スクリーンショット 2018-02-10 22.22.42.png](https://qiita-image-store.s3.amazonaws.com/0/113504/7f4fb396-61bf-5320-3652-08705613127f.png "スクリーンショット 2018-02-10 22.22.42.png")




# 3. シェーダーで合成する

```csharp
Shader "TSprites/Effect"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
        [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
        [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
        [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
        [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
        _GradationTex("Gradation Texture", 2D) = "white"{}
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0
            #pragma multi_compile_instancing
            #pragma multi_compile _ PIXELSNAP_ON
            #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
            #include "UnitySprites.cginc"

            sampler2D _GradationTex;
            float _val_u;

            struct appdata_ts
            {
            	float4 vertex : POSITION;
                float2 texcoord0 : TEXCOORD0;
                float4 color : COLOR;
            };

            struct v2f_ts
            {
            	float4 pos : SV_POSITION;
                float2 uv0 : TEXCOORD0;
                fixed4 color : COLOR;
                UNITY_VERTEX_OUTPUT_STEREO	
            };

            v2f_ts vert(appdata_ts IN){
            	v2f_ts OUT;
            	UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

            	OUT.pos = UnityObjectToClipPos(IN.vertex);
            	OUT.uv0 = IN.texcoord0;

            	#ifdef PIXELSNAP_ON
                OUT.pos = UnityPixelSnap (OUT.pos);
                #endif
                OUT.color = IN.color;
                return OUT;
            }

            fixed4 frag(v2f_ts IN) : SV_Target{
            	// Default
                  fixed4 mainColor = SampleSpriteTexture(IN.uv0);
                  // グラデーションの色を求める
                  float2 gradation_uv = float2(mainColor.r, 0);
                  fixed4 finalColor = tex2D(_GradationTex, gradation_uv);

                  finalColor *= IN.color;
                  float finalAlpha = mainColor.a * finalColor.a;
            	  return fixed4(finalColor.rgb, finalAlpha);
            }
        ENDCG
        }
    }
}

```

SpriteRenderer用のシェーダーです．

# まとめ
Photoshop等を使わずUnityだけで作れるという点で，スクリプトから画像を作るのは非常に役立つと思いました．

いろいろなことの自動化に応用できそうで，非常に勉強になりました．



# 参考
https://qiita.com/yoike/items/00e93824d3dcdc3b900f