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 从入门到盖棺(三)

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