はるのゲーム開発メモ

自分の勉強の成果などをメモ代わりにします

UnityのShader勉強8 GrabPass

今回はGrabPassをおさらいします。

GrabPassとは、すでに描画されている画面ををテクスチャとして扱えるというものです。(まちがっているかも...)

すでに描画されている背景をゆがませたりといった面白い表現が可能になります。

参照:

docs.unity3d.com

 

tsumikiseisaku.com

 

勉強段階で知った機能なのでまだ実践では使っていません。ですので軽く試しておわりにしようと思います。

 

コード

Shader "Unlit/GrabPass"
{
	Properties{
		_Texture("Texture",2D)="white"{}
		_Float("float",float) = 1
		_Color("Color",Color) = (1,1,1,1)
		_Range("Range",Range(-10,10))=0.5
		_Vector("ector",Vector)=(0,0,0,0)

	}
	SubShader
	{
		Tags{
			"RenderType"="Transparent"
			"Queue"="Transparent"
		}
		GrabPass{}
		Pass
		{
			Blend SrcAlpha OneMinusSrcAlpha
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			sampler2D _GrabTexture;

			sampler2D _Texture;
			float4 _Texture_ST;
			
			fixed _Float;
			fixed4 _Color;
			fixed _Range;
			fixed4 _Vector;

			
			struct appdata
			{
				float4 vertex : POSITION;
				float2 texcoord : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.texcoord, _Texture);
				
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 tex = tex2D(_GrabTexture, i.uv);
				return tex;
			}
			ENDCG
		}
	}
}

 

GrabPassの使い方ですが、まずはPassの前に

GrabPass{}

と記述します。

そして

sampler2D _GrabTexture;

と宣言します。

これでテクスチャの取得がおわりました。

 

あとは

frag関数のテクスチャを_GrabTextureとしてやります。

fixed4 tex = tex2D(_GrabTexture, i.uv);

 

 

適当に作成したSpriteにこのシェーダーを適用したマテリアルをアタッチします。

(枠を作るために適当な画像をSpriteに入れておく必要があります。)

 

結果

f:id:Haru_android:20171221131153p:plain

 

わかりずらいですが、カメラでみている状態のようなテクスチャが貼られました。

 

次に、スクリーンのポジションを利用してテクスチャを貼ってみます。

コードを以下のように書き換えます。

Shader "Unlit/GrabPass"
{
	Properties{
		_Texture("Texture",2D)="white"{}
		_Float("float",float) = 1
		_Color("Color",Color) = (1,1,1,1)
		_Range("Range",Range(-10,10))=0.5
		_Vector("ector",Vector)=(0,0,0,0)

	}
	SubShader
	{
		Tags{
			"RenderType"="Transparent"
			"Queue"="Transparent"
		}
		GrabPass{}
		Pass
		{
			Blend SrcAlpha OneMinusSrcAlpha
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			sampler2D _GrabTexture;

			sampler2D _Texture;
			float4 _Texture_ST;
			
			fixed _Float;
			fixed4 _Color;
			fixed _Range;
			fixed4 _Vector;

			
			struct appdata
			{
				float4 vertex : POSITION;
				float2 texcoord : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float2 uv : TEXCOORD0;
				float4 screenPos : TEXCOORD1;
			};

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				//o.screenPos = ComputeScreenPos(o.vertex);
				o.screenPos = ComputeGrabScreenPos(o.vertex);
				o.uv = TRANSFORM_TEX(v.texcoord, _Texture);
				
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				half2 grabUV = (i.screenPos.xy / i.screenPos.w);
				fixed4 tex = tex2D(_GrabTexture, grabUV);
				return tex;
			}
			ENDCG
		}
	}
}

 

 

取得したスクリーンのポジションを保持するためにv2f関数に

float4 screenPos : TEXCOORD1;

と追加します。

 

次にvert関数にてスクリーンのポジションを取得します。

//o.screenPos = ComputeScreenPos(o.vertex); 
o.screenPos = ComputeGrabScreenPos(o.vertex);

最初はComputeScreenPos(o.vertex)を利用していたのですが、UIに適用したところ反転してしまったため調べたところ、環境によって反転してしまったりする(?)というので、それに対応したComputeGrabScreenPos(o.vertex)を利用しました。

 

この二つはどちらも"UnityCG.cginc"にある関数で、

ComputeGrabScreenPos()はUV座標によって自動で反転してくれるようです。

 

こちらを適用すると、

f:id:Haru_android:20171221132450p:plain

こうなります。

なにも表示されていないように見えますが、すでに描画されているものをスクリーンのポジションに合わせてテクスチャを貼っています。

 

わかりずらいので、frag関数のtexに_Colorを乗算してみます。

fixed4 tex = tex2D(_GrabTexture, grabUV)*_Color;

 

f:id:Haru_android:20171221132748p:plain

実際にテクスチャが貼られているのがわかりました。

結果としてみると透明にしてるだけじゃないかと思いますが、テクスチャとして扱えるため、様々な表現ができます。

 

例えば、

frag関数のtex

fixed4 tex = tex2D(_GrabTexture, grabUV);
tex~1-tex;

 としてみると

f:id:Haru_android:20171221133303p:plain

テクスチャの色が反転しました。

 

ぼかしたり

fixed4 frag (v2f i) : SV_Target
{
	half2 grabUV = (i.screenPos.xy / i.screenPos.w);
	fixed4 tex = tex2D(_GrabTexture, grabUV);
	tex += tex2D(_GrabTexture, float2(grabUV.x+_Range,grabUV.y));
	tex += tex2D(_GrabTexture, float2(grabUV.x - _Range, grabUV.y));
	tex += tex2D(_GrabTexture, float2(grabUV.x , grabUV.y + _Range));
	tex += tex2D(_GrabTexture, float2(grabUV.x , grabUV.y - _Range));
	return tex/5;
}

・_Rangeの値は0.01ぐらい

・ImageをCanvas全体に広げる

 

f:id:Haru_android:20171221142828p:plain

簡単ですが中央と上下左右にずらしたものを加算して足したぶんを割っています。

 

 

 

おわりに

お手軽に画面にエフェクトをかけることができるのがわかってもらえるとうれしいです。

次は、RenderTextureをおさらいします。