UnityShader 從入門到蓋棺(四)

img

廣告版

今天講一下廣告板的實現。來來來,先砸效果圖。

final

廣告版的意思是,雖然我只是一塊板(一個平面),但是我始終對着觀察者(打廣告)。這個對着也有兩種方式。

  1. 整個平面垂直於觀察者的視線向量。常用於一些閃光特效。
  2. y向量始終垂直於地面。常用於像草這一類需要紮根在地面上的。

所以,理一下思路。我們要做的,其實就是把平面所有點基於平面的一個錨點做一個旋轉變換。使得旋轉後的平面符合我們預期的朝向。

介紹完概念,直接開始製作。

首先我們把我們的星星資源拖拽到場景中。然後將它的位置放在屋頂,並且進行一定的縮放。

trans

創建一個Unlit着色器,刪除全部內容。然後實現一個簡單的顯示一個2D精靈的Shader,沒什麼特別的。命名爲4_BillBorad。

Shader "Unity Shader/C4/4_BillBoard"
{
	Properties {
		_MainTex ("Main Tex", 2D) = "white" {}
		_Color ("Main Color", COLOR) = (1, 1, 1, 1)
	}
	SubShader 
	{

		Tags {
			"Queue" = "Transparent"
			"RenderType" = "Transparent"
			"DisableBatching" = "True"
		}

		Blend SrcAlpha OneMinusSrcAlpha
		ZWrite Off
		Cull Off
		
		Pass {

			Tags {
				"LightMode" = "ForwardBase" 
			}

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			struct a2v {
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};


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

			sampler2D _MainTex;
			float4 _Color;

			v2f vert(a2v i) {
				v2f o;
				o.pos = UnityObjectToClipPos(i.vertex);
				o.uv = i.uv;
				return o;
			}

			fixed4 frag(v2f i) : SV_Target {
				fixed4 c = tex2D(_MainTex, i.uv);
				c.rgb *= _Color.rgb;
				return c;
			}

			ENDCG
		}
	}

}

創建一個材質4_Billboard_up, 並且應用上對應shader。再把這個材質拖到我們的屋頂星星上。現在的效果圖如下。

init

因爲我們需要所以頂點做一個相同變換,使得最終朝向滿足我們的要求。

所以第一步,構造變換矩陣的向量基。

我們能得到的第一個向量是攝像機指向物體的向量。在頂點着色器中,定義一個物體的中心點,然後把攝像機位置轉換到物體空間座標系,利用座標相減得到forward向量(頂點的朝向向量)。

v2f o;

float3 centerPos = float3(0, 0, 0);
float3 objectSpaceCameraPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0));;

float3 forward = objectSpaceCameraPos - centerPos;

接着我們先定義頭頂向量爲(0, 1, 0), 然後根據叉乘得到右向量,再根據叉乘重新計算頭頂向量。讀者一定要自己搞清楚這裏面的原理,很簡單的。

forward = normalize(forward);

float3 upDir = float3(0, 1, 0);
float3 rightDir = normalize(cross(upDir, forward));
upDir = normalize(cross(forward, rightDir));

有了這個變換矩陣,我們就可以用它去左乘當前每個頂點與中心點的偏移向量。然後根據結果重新對當前頂點計算經過變換後的偏移位置。

float3 centerOffset = i.vertex.xyz - centerPos;
float3 localPos = centerPos + rightDir * centerOffset.x + upDir * centerOffset.y + forward * centerOffset.z;

o.pos = UnityObjectToClipPos(float4(localPos, 1.0));
// o.pos = UnityObjectToClipPos(i.vertex);
o.uv = i.uv;
return o;

然後現在再看下結果,就會發現我們屋頂的星星能朝着我們攝像機動了。

middle

上面的實現屬於我們說到的,頭頂向量永遠垂直於地面。廣告版還有一種就是forward向量永遠平行於實現向量。讀者應該已經發現,如果我們把normalize.y = 0這一段代碼刪掉,那麼星星就會永遠朝向攝像機。

我們先在天空上在擺上幾個星星。

然後添加一個開關變量控制是否永遠朝向攝像機。

_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Main Color", COLOR) = (1, 1, 1, 1)
[MaterialToggle] _FaceCamera ("Face Camera", FLOAT) = 0

如果朝向攝像機,那麼我們就保持forward向量的y分量。這裏我們再加個保險設置,避免forward向量和upDir向量重合。

float3 forward = objectSpaceCameraPos - centerPos;

forward.y *= _FaceCamera;
forward = normalize(forward);

// float3 upDir = float3(0, 1, 0);
float3 upDir = abs(forward.y) < 0.999 ? float3(0, 1, 0) : float3(0, 0, 1);
float3 rightDir = normalize(cross(upDir, forward));
upDir = normalize(cross(forward, rightDir));

然後我們再創建一個材質"4_Billboard_forward", 把勾選框勾選上,再把材質拖到天空中的星星上。然後我們就會發現天上的星星也在一直對着我們的攝像機拉!

final

結語

在本次筆者自己動手的時候遇到了幾個坑,總結一下幾個問題,大家一起去試一下吧!

  • 試下用下面的代碼去求攝像機物體空間位置,會有什麼不同。
float3 objectSpaceCameraPos = mul(unity_WorldToObject, _WorldSpaceCameraPos);
  • 試下把星星的縮放改成 5,5,1這種非均勻縮放,嘗試一下去解釋現象吧!
  • 刪掉"Queue" = "Transparent"看看。

好啦,這次分享就到這了。支持作者專屬二維碼走起來。

vxpay

鏈接

github地址:https://github.com/gjbian/Unity-Shader-Study

上一篇:UnityShader 從入門到蓋棺(三)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章