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

Projectorでデカールもどき

突然地味なUnityネタが沸いてくるD.N.A.のおぼえがきですいかがお過ごしでしょうか。

※今回の記事では利用例のテクスチャにハムコロ様STG素材を使用しています。

いきさつ

STGの試作を延々続けておりますが、地味に困ってるのが「Terrainの上の地上物の破壊跡の処理」です。
板ポリゴンにテクスチャを張っただけでは、Terrainにめり込んだりして見た目がよろしくありません。

f:id:dnasoftwares:20160221182338p:plain

そういうときはDecal(オブジェクトの表面に沿って別のテクスチャを張る)を使うのですが、
Decalを実現できるアセットというといくつかあるものの……

と、やることに対して$がかかりすぎる感じだったり微妙な物足りなさがあったり……と悩んでいたのですが、
良く考えればUnity標準機能のProjectorがあるじゃない、ということでProjectorでなんとかしてみようと試みたのでした。

シェーダーいじり

Standard Assetで入手できるProjector用のシェーダーは、

  • Multiply
  • Light

の2つだけです。MultiplyもLightもテクスチャの色をそのまま貼り付けられないので、これらを元にテクスチャの色をブレンドせず貼り付けられるシェーダーを書きます。以下、ProjectorLight.shaderを元に改造したコード。

リスト ProjectorDecal.shader

Shader "Projector/Decal" {
	Properties {
		_Color("Main Color", Color) = (1,1,1,1)
		_ShadowTex ("Cookie", 2D) = "gray" {}
	}
	Subshader {
		Tags {"Queue"="Transparent"}
		Pass {
			ZWrite Off
			ColorMask RGB
			Blend SrcAlpha OneMinusSrcAlpha //ブレンドを通常のアルファブレンドに
			Offset -1, -1

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fog
			#include "UnityCG.cginc"
			
			struct v2f {
				float4 uvShadow : TEXCOORD0;
				UNITY_FOG_COORDS(2)
				float4 pos : SV_POSITION;
			};
			
			float4x4 _Projector;
			float4x4 _ProjectorClip;
			
			v2f vert (float4 vertex : POSITION)
			{
				v2f o;
				o.pos = mul (UNITY_MATRIX_MVP, vertex);
				o.uvShadow = mul (_Projector, vertex);
				UNITY_TRANSFER_FOG(o,o.pos);
				return o;
			}

			fixed4 _Color;
			sampler2D _ShadowTex;
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
				texS.rgb *= _Color.rgb;
				// texS.a = 1.0-texS.a; //デカールの場合はアルファ値はそのままでいいので削除

				fixed4 res = texS;

				UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(1,1,1,1));
				return res;
			}
			ENDCG
		}
	}
}

いじったのは

  • FallOffの設定はいらないのでパラメータごとバッサリ削除。
  • Blendをアルファブレンドに。
  • アルファ値を反転する処理がいらないので削除(このままだと透明と不透明が反転してしまう)

の3点。これでうまくいきました。

配置してみる

実際にシーンに置いてみます。Projectorを配置して地面に向け、下記のようにプロパティを設定。

f:id:dnasoftwares:20160221182426p:plain

Orthographicを有効にして地面との距離によってデカールが変化しないようにします。Materialには先ほどのシェーダを指定したマテリアルを用意して割り当て。Cookieとして破壊跡の絵を用意。Ignore Layersで背景以外全部を指定し変なところに跡がつかないように。テクスチャの方もWrap Modeを「Clamp」にするのを忘れずに。

実際にはこんな感じになります。
f:id:dnasoftwares:20160221182953p:plain

ちゃんとTerrainに沿っていることがわかります。
f:id:dnasoftwares:20160221183002p:plain

まとめ

簡易的なデカール表現としてはこれでいいでしょう。ただ、板ポリならShader次第でなんでもありだった凝った表現ができないのと、ライティングを一切考慮していないままなので、当然ライティングに一切追従せず、使いどころは極めて限定されます。やっておいてなんですが基本は板ポリでしのぎつつ、必要なところだけProjectorにするといった方がいいでしょう。

あと、誰かTerrainにDecal張れるアセットご存じでしたら教えてください。(Asset Storeの説明文だとそのへんがわからないヤツが多くて突撃しづらい……)