【Unity】せっかくなのでプロジェクターでノーマルマップを投影してみた【Shader】

投稿者: | 2020年2月2日

ノーマルマップの仕組みもわかったので、プロジェクターで投影したらどうなるか試してみました。

こんなんできました

このような感じになりました。なんか違う。
深度バッファを書き換えてるのではなく、白気味でブレンドしているだけなので想像とは違うものになってしまった。

これはこれでなにかに使えるかもしれない。

プロジェクターのシェーダーコードになります。

Shader "Neko/Projector"
{
	Properties
	{
		[Normal]_NormalMap("Normal Map", 2D) = "bump" {}
	}//Properties

	SubShader
	{
		Pass
		{
			Tags
			{
				"RenderType" = "Opaque"
				"Queue" = "Transparent"
			}

			Blend OneMinusDstColor One // スクリーン / 比較(明)
//			Blend Zero SrcColor // 乗算
//			Blend One One // 加算
//			Blend OneMinusDstColor Zero // 反転

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 tangent : TANGENT;
				float4 uv : TEXCOORD0;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float4 uv : TEXCOORD0;
				float4 frustum : TEXCOORD1; // left right
				float depth : TEXCOORD2; // near far
				float3 lightDir : TEXCOORD3;
				float3 viewDir : TEXCOORD4;
			};

			float4x4 unity_Projector;
			float4x4 unity_ProjectorClip;

			sampler2D _NormalMap;
			float4 _NormalMap_ST;
			float4 _NormalMap_TexelSize;

			v2f vert( appdata v)
			{
				v2f o = (v2f)0;
				o.vertex =  UnityObjectToClipPos( v.vertex);
				o.uv = mul( unity_Projector, v.vertex);
				o.frustum = mul( unity_Projector, mul( UNITY_MATRIX_M, v.vertex));
				o.depth = mul( unity_ProjectorClip, v.vertex);

				float3 n = normalize( v.normal);
				float3 t = normalize( v.tangent.xyz);
				float3 b = cross( n, t) * v.tangent.w;
				float3x3 invM = float3x3( t, b, n);
				o.lightDir = mul( invM, ObjSpaceLightDir( v.vertex));
				o.viewDir = mul( invM, ObjSpaceViewDir( v.vertex));
				return o;
			}

			sampler2D _MainTex;

			fixed4 frag( v2f i) : SV_Target
			{
				// フラスタム カリング
				// left right top bottom チェック
				clip( 1-abs( (i.frustum.xy / i.frustum.w)*2-1));
				// near far チェック
				clip( 1-abs( i.depth*2-1));

				// テクスチャ外だったらカリング
				clip( 1-abs( (i.uv.xy / i.frustum.w)*2-1));

				float3 L = normalize( i.lightDir);
				float3 V = normalize( i.viewDir);
				float3 halfDir = normalize( L + V);

				float3 N = UnpackNormal( tex2Dproj( _NormalMap, i.uv));
				float3 diff = saturate( dot( N, L))/10;
				float spec = pow( max( 0, dot( N, halfDir)), 10);

				float3 color = diff + spec;
				return float4( color, 1);
			}
			ENDCG
		} // Pass
	}//SubShader
}//Shader

ピクセルシェーダーのカリング周りがだいぶシンプルになりました。

i.frustum.xy / i.frustum.w

この部分で 視錐台 の 幅と高さを算出して 0~1 の範囲外だと 台の外になります。
clip() で一度に ピクセルの破棄をしたいため 2倍して1を引いた値の 絶対値 が 0と1の範囲外であるかどうかをチェックしています。

unity_ProjectorClip は Near Far の計算に利用でき 0から1の範囲外であれば 破棄しています。

あとはライトをブレンドしているだけです。

もう少し凸凹感が出るかと思ったけど、要調整ですね。
加算でなく減算すれば凹んだ感じになるかな?

この作品はユニティちゃんライセンス条項の元に提供されています

関連記事

【Unity】せっかくなのでプロジェクターでノーマルマップを投影してみた【Shader】」への1件のフィードバック

  1. ピンバック: 【Unity】ペンキをぶっかけた人型を作ってみた | | ぶろねこ -Blog on NEKOTEAM-

コメントを残す