【Unity】アニメ調の髪のハイライトを実現する案【Shader】

はじめに

久々のセルシェーディングネタでひらめいたのでメモしておきます。

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

↓シリーズ物

前回

アニメ調の髪の毛のハイライト

これまでやってきた中で髪の毛のハイライトだけが納得いかなかったので、テコ入れしました。

アニメのハイライトは光源無視の雰囲気的な描画が多いと思います。レイトレーシングや物理演算的には苦手な分野です。ので光源無視のカメラ依存のハイライトを模索してみました。

こんなんできました

ハイライトを表示するためVを求める

今回作成したのはこのようなマスクです。

ユニティちゃんのUVマップに沿ってベイクしたもの

ハイライト部分は不透明で、描画しない部分はアルファ0が入っています。
グラデーションは 0~255 の赤成分で、実際にレンダリングしたときの高さ情報を入れてます。
Blender でベイクしたもので、結構めんどいので 後日別途作り方を投稿します。

↓(2020/05/27追記)ベイクの過程を投稿しました。

で、このテクスチャーをそのまま表示すると以下のような画像になります。

 このようにハイライトの出る部分だけ下に向かって1~0のマスクがかかっているのがわかります。
 0~1が入れば ハイライト用の UVのVに利用できます。

参考にここまでの Pass

Pass
{
	Name "CROWN"

	CGPROGRAM
	#pragma vertex vert
	#pragma fragment frag

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

	sampler2D _CrownMask;
	float4 _CrownMask_ST;
	float4 _CrownMask_TexelSize;

	v2f vert( appdata v)
	{
		v2f o = (v2f)0;
		o.vertex = UnityObjectToClipPos( v.vertex);
		o.uv = float2( v.uv * _CrownMask_ST.xy + _CrownMask_ST.zw);
		return o;
	}

	half4 frag( v2f i) : SV_Target
	{
		float4 map = tex2D( _CrownMask, i.uv);
		clip( map.a - 0.5);
		return half4( map.r, 0, 0, 1);

	}
	ENDCG
}//Pass

_CrownMask を見て 不透明なら R を出力しています。

ハイライトを表示するためUを求める

Uはカメラ視線と頂点法線から算出します。

U の算出方法は カメラの Right方向 左手の法則の 中指ベクトルと 面法線の内積から求めます

float3 right = UNITY_MATRIX_V._11_12_13;
float u = dot( i.normalV, right);
u = ( u + 1.0) * 0.5;

ViewMatrix にはカメラの
Rightベクトル
Upベクトル
Forwardベクトル
の順に入っているので 1行目 Rightベクトルを利用して 面法線と内積を求め
-1~1 を 0~1 に変換します。

カメラから左向きの面は 0
カメラから右向きの面は 1
正面は 0.5 となり綺麗に 0~1 に収まります。これをUとして利用すれば テクスチャーが貼れるということです。

Uのみ青で出力した結果

算出した結果をRGB で出力するとこのようになります。

この値を利用してテクスチャを貼り付けます。

見やすくするために、今回はこのようなテスクチャを表示してみます。

法線により歪んでいますが テクスチャーの 「3」と「4」が中心に出ていて「2」と「5」がサイドになります。これを目安にハイライト用のテクスチャーを書きます。

Pass
{
	Name "CROWN"
	Tags
	{
		"Overlay"="Transparent"
		"ForceNoShadowCasting " = "True"
		"IgnoreProjector" = "True"
	}
	Blend SrcAlpha OneMinusSrcAlpha // 昔ながらの透明 

	CGPROGRAM
	#pragma vertex vert
	#pragma fragment frag

	struct appdata
	{
		float4 vertex : POSITION;
		float4 uv : TEXCOORD0;
		float3 normal : NORMAL;
	};
	struct v2f
	{
		float4 vertex : SV_POSITION;
		float2 uv : TEXCOORD0;
		float3 normalV : TEXCOORD2;
		float3 normal : NORMAL;
	};

	sampler2D _CrownMask;
	float4 _CrownMask_ST;
	float4 _CrownMask_TexelSize;

	v2f vert( appdata v)
	{
		v2f o = (v2f)0;
		o.vertex = UnityObjectToClipPos( v.vertex);
		o.uv = float2( v.uv * _CrownMask_ST.xy + _CrownMask_ST.zw);
		o.normalV = mul( (float3x3)unity_ObjectToWorld, v.vertex);
		o.normalV.y = 0;
		o.normalV = normalize( o.normalV);
		return o;
	}

	sampler2D _CrownTex;

	half4 frag( v2f i) : SV_Target
	{
		float4 map = tex2D( _CrownMask, i.uv);
		clip( map.a - 0.5);

		float3 right = UNITY_MATRIX_V._11_12_13;
		float u = dot( i.normalV, right);
		u = ( u + 1.0) * 0.5;
		u = u*0.5 + 0.25;

		float v = map.r;
		float4 hilight_color = tex2D( _CrownTex, float2( u, v));
		return hilight_color;
	}
	ENDCG
}//Pass
u = u*0.5 + 0.25

の部分はテクスチャーの位置を調整して、テクスチャーの中心が正面に来るようにしています。

パターン1

このようなハイライトテクスチャを使うと

アニメでよく見かける ハイライトの用に見えます。

パターン2

点光源っぽいテクスチャで

あとから書き足したようなハイライトになります。

3 Comments

Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です