ひきぷろのプログラミング日記

プログラミングの日記です。

(Unity) グレースケールフィルタを Unity に移植してみた

以前作ったグレースケールフィルタを Unity に移植しました。GPU で動かしたかったんですが、まずは CPU で実装してみました。

Visual Studio で実装したクラスは次のページにあります。hikipuro.hatenadiary.jp

GrayScaleFilter クラス

Texture2D を受け取って、加工するようにしています。

// GrayScaleFilter.cs
using UnityEngine;

public class GrayScaleFilter {
	// 引数で渡されたテクスチャ画像を、グレースケールに変換します
	public static Texture2D Apply(Texture2D texture) {
		if (texture == null) {
			return null;
		}

		// 全てのピクセルを抜き出して、UnityEngine.Color オブジェクトの配列形式にする
		Color[] pixels = texture.GetPixels();
		
		// 全てのピクセルをグレースケールに変換する
		for (int i = 0; i < pixels.Length; i++) {
			Color pixel = pixels[i];
			float color = (
				pixel.r * 0.29891f + 
				pixel.g * 0.58661f + 
				pixel.b * 0.11448f
			);
			pixel.r = color;
			pixel.g = color;
			pixel.b = color;
			pixels[i] = pixel;
		}
		
		// 新しいテクスチャを作成する
		Texture2D result = new Texture2D(texture.width, texture.height);
		result.SetPixels(pixels);
		result.Apply();
		return result;
	}
}
使い方

次のようなクラスを作って、画面上の GameObject に貼り付けてください。

// TextureTest.cs
using UnityEngine;
using System;
using System.Diagnostics;

public class TextureTest : MonoBehaviour {
	void Start () {
		// 開始する時に 1 度だけ実行する
		long time = Benchmark(() => {
			Texture2D texture = (Texture2D)renderer.material.mainTexture;
			Texture2D newTexture = GrayScaleFilter.Apply(texture);
			renderer.material.mainTexture = newTexture;
		}, 1);
		UnityEngine.Debug.Log("time: " + time);
	}
	
	// 引数で渡したメソッドの実行時間を計る
	private static long Benchmark(Action action, int interval) {
		GC.Collect();
		Stopwatch sw = Stopwatch.StartNew();
		for (int i = 0; i < interval; i++) {
			action.Invoke();
		}
		sw.Stop();
		return sw.ElapsedMilliseconds;
	}
}
Unity 初心者のかた向けの説明

上記のコードを実行しようとして、エラーが出てしまった人も確認してみてください。

1. Unity で使用する .NET Framework のバージョンを設定する
メニューから、 Edit -> Project Settings -> Player と辿って、 Optimization の Api Compatibility Level を .NET 2.0 に設定してください。

2. シーンに Quad か Plane を貼り付ける
テクスチャを貼るところを準備します。
Quad も Plane も似たようなオブジェクトですが、 Quad の方がポリゴン数が少ないです。メニューから、 GameObject -> 3D Object -> Quad で貼り付けます。貼り付けれたら、画面の前の方を向けて回転させて、カメラから真っ直ぐ見れるようにしてみてください。
そのままだと画面が暗いので、 Directional light も 1 つ追加しておいた方が良いかもしれません。

3. プロジェクトフォルダに任意の画像を持ってくる
プロジェクトフォルダに何か画像をコピーしておいてください。
Assets フォルダの中に入れると、 Unity 上で認識されます。

4. 画像のインポート設定をする
Unity の Project ビュー上で、先ほどインポートした画像を選んでください。
デフォルト状態だと、画像のピクセルデータがプログラムから読み取り不可に設定されているので、読み取り可能に設定しなおします。

  • Texture Type を Advanced に変更する
  • Read/Write Enabled のチェックを ON にする
  • Format を RGBA 32 bit に設定する
  • Apply ボタンを押す

5. マテリアルを作成する
シーン上の Quad に画像を設定するためには、マテリアルを作成する必要があります。Project ビューで右クリックして、 Create -> Material で、マテリアルを作成してください。

マテリアルが作成できたら、 Scene ビュー上に表示されている、先ほど作成した Quad にドラッグ&ドロップしてください。もしくは、Hierarchy ビューから Quud を選択して、 Inspector ビューの Mesh Renderer から Materials を展開して、 Element 0 のところにマテリアルをドラッグ&ドロップしても適用されます。

6. マテリアルに画像を設定する
Hierarchy ビューで Quad を選択した状態で、 Inspector ビューを見てみてください。
一番下に、 Main Color とか Base (RGB) とか書かれた欄があると思いますが (Unity 5 以降だとちょっと違うかもしれません。。一番下には出ていると思います)、そこらへんに手順 3. で Unity 内に持ってきた画像をドラッグ&ドロップしてください。 Quad の表面に画像が貼り付けられていれば OK です。

7. 実行して確認する
いったん、実行してみましょう。
Game ビューが表示されて、真ん中に画像が出ているでしょうか。
表示領域が小さい場合は、 Inspector ビューで Quad の Scale 値を変えてみたりして、大きさを調整してください。カメラを移動させても、大きさを調整することができます。まだこの段階では、グレースケールになっていません。

8. グレースケールにする
再度、 Hierarchy ビューで Quad を選択した状態で、 Inspector ビューを見てみてください。Quad の Inspector が表示されている状態のまま、 Project ビューから、先ほど作成した TextureTest クラスのソースコードのファイルを、 Inspector にドラッグしてください。貼り付けることができたら、 Inspector に Texture Test (Script) という項目が追加されます。

9. もう一度、実行して確認する
今度はグレースケールになっていると思います。

コードを貼り付ける という操作が、ちょっと戸惑うかもしれませんね。
Unity では、 MonoBehaviour というクラスを継承したクラスを作ると、画面上のオブジェクトに直接貼り付けることができるようになっています。

追記

シェーダーのプログラムで書きなおしてみました。

// GrayScaleShader.shader
Shader "Custom/GrayScaleShader" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
	}
	SubShader {
		CGPROGRAM
		#pragma surface surf Lambert
		struct Input {
			float2 uv_MainTex;
		};
		sampler2D _MainTex;
		void surf (Input IN, inout SurfaceOutput o) {
			float3 pixel = tex2D(_MainTex, IN.uv_MainTex);
			float color = (
				pixel.r * 0.29891 +
				pixel.g * 0.58661 +
				pixel.b * 0.11448
			);
			o.Albedo = color;
		}
		ENDCG
	}
	FallBack "Diffuse"
}

シェーダーについては、次のサイトを参考にさせて頂きました。

tips.hecomi.com

Qiita とかでも検索すると沢山出てくると思います。

シェーダーを適用する方法

Hierarchy ビューから Quad を選択した状態で、 Inspector ビューを見てみてください。一番下の方に、 Shader 選択用のドロップダウンリストがあると思います。そこから、 Custom -> GrayScaleShader を選ぶと、上で書いたシェーダーが適用されます。