广告版
今天讲一下广告板的实现。来来来,先砸效果图。
广告版的意思是,虽然我只是一块板(一个平面),但是我始终对着观察者(打广告)。这个对着也有两种方式。
- 整个平面垂直于观察者的视线向量。常用于一些闪光特效。
- y向量始终垂直于地面。常用于像草这一类需要扎根在地面上的。
所以,理一下思路。我们要做的,其实就是把平面所有点基于平面的一个锚点做一个旋转变换。使得旋转后的平面符合我们预期的朝向。
介绍完概念,直接开始制作。
首先我们把我们的星星资源拖拽到场景中。然后将它的位置放在屋顶,并且进行一定的缩放。
创建一个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。再把这个材质拖到我们的屋顶星星上。现在的效果图如下。
因为我们需要所以顶点做一个相同变换,使得最终朝向满足我们的要求。
所以第一步,构造变换矩阵的向量基。
我们能得到的第一个向量是摄像机指向物体的向量。在顶点着色器中,定义一个物体的中心点,然后把摄像机位置转换到物体空间座标系,利用座标相减得到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;
然后现在再看下结果,就会发现我们屋顶的星星能朝着我们摄像机动了。
上面的实现属于我们说到的,头顶向量永远垂直于地面。广告版还有一种就是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", 把勾选框勾选上,再把材质拖到天空中的星星上。然后我们就会发现天上的星星也在一直对着我们的摄像机拉!
结语
在本次笔者自己动手的时候遇到了几个坑,总结一下几个问题,大家一起去试一下吧!
- 试下用下面的代码去求摄像机物体空间位置,会有什么不同。
float3 objectSpaceCameraPos = mul(unity_WorldToObject, _WorldSpaceCameraPos);
- 试下把星星的缩放改成 5,5,1这种非均匀缩放,尝试一下去解释现象吧!
- 删掉"Queue" = "Transparent"看看。
好啦,这次分享就到这了。支持作者专属二维码走起来。