読者です 読者をやめる 読者になる 読者になる

Onoty3D

Unityに関するメモとか

クロマキーシェーダーを作ってみる

rocketnews24.com

朝からガチャピンがクロマキーで透明化した、というニュースが話題になっていたので、便乗してクロマキーシェーダー作りました。
記事の最後にコードを掲載します。

通常のクロマキーではブルーバック、グリーンバックが主ですが、任意の色が指定できるようにしました。
また透過対象色の近似度の閾値も任意に指定出来ます。

任意色指定
f:id:onoty3d:20160206140350g:plain

閾値指定
f:id:onoty3d:20160206140453g:plain

ガチャピンのようなもの。
f:id:onoty3d:20160206140531g:plain

WebCamTextureやRenderTexture用のShaderとして使うことで、実際のテレビのクロマキーの様に使うことが出来ると思います。
↓ちょっと前にFaceRigで試したやつ。

シェーダのコード

Shader "Onoty3D/ChromaKey" {
	Properties{
		_KeyColor("Key Color", Color) = (0,1,0)
		_Near("Near", Range(0, 2)) = 0.2
		_MainTex("Base (RGB) Trans (A)", 2D) = "white" {}
	}

	SubShader{
		Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
		LOD 200

		CGPROGRAM
			#pragma surface surf Lambert alpha

			sampler2D _MainTex;
			fixed3 _KeyColor;
			fixed _Near;

			struct Input {
				float2 uv_MainTex;
			};

			void surf(Input IN, inout SurfaceOutput o) {
				fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
				clip(distance(_KeyColor, c) - _Near);
				o.Albedo = c.rgb;
				o.Alpha = c.a;
			}
		ENDCG
	}

	Fallback "Transparent/Diffuse"
}

※2016/05/13 追記
色の近似度をRGB色空間の距離でとっていましたが、同色で明暗がある場合はHSV色空間のH(Hue)の近似で見たほうがいいことに気づいたので、そちらのバージョンも作ってみました。
下の図の場合、左が上記のRGBでの判定、右がHueでの判定となります。
f:id:onoty3d:20160513182839g:plain

Shader "Onoty3D/ChromaKeyHue" {
	Properties{
		_KeyColor("Key Color", Color) = (0,1,0)
		_Near("Near", Range(0, 0.5)) = 0.1
		_MainTex("Base (RGB) Trans (A)", 2D) = "white" {}
	}

	SubShader{
		Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }

		CGPROGRAM
			#pragma surface surf Lambert alpha

			sampler2D _MainTex;
			fixed3 _KeyColor;
			fixed _Near;

			struct Input {
				float2 uv_MainTex;
			};

			fixed GetHue(fixed3 rgb){
				fixed hue = 0;
				fixed minValue = min(rgb.r, min(rgb.g, rgb.b));
				fixed maxValue = max(rgb.r, max(rgb.g, rgb.b));
				fixed delta = maxValue - minValue;
				if (delta != 0){
					if (maxValue == rgb.r) {
						hue = (rgb.g - rgb.b) / delta;
					} else if (maxValue == rgb.g) {
						hue = 2.0 + (rgb.b - rgb.r) / delta;
					} else {
						hue = 4.0 + (rgb.r - rgb.g) / delta;
					}

					hue /= 6.0;

					if (hue < 0) {
						hue += 1.0;
					}
				}
				return hue;
			}

			void surf(Input IN, inout SurfaceOutput o) {
				fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
				fixed distance = GetHue(c.rgb) - GetHue(_KeyColor);
				if (distance > 0.5) {
					distance = 1.0 - distance;
				} else if (distance < -0.5) {
					distance = 1.0 + distance;
				} else {
					distance = abs(distance);
				}

				clip(distance - _Near);
				o.Albedo = c.rgb;
				o.Alpha = c.a;
			}

		ENDCG
	}

	Fallback "Transparent/Diffuse"
}