這種效果是讓一個平面能跟着視角進行旋轉,從而使得該平面一直朝向着攝像機,形成一種立體的感覺.
billboard主要有兩種,第一種是固定向上方向的billboard,比如說草地,一個草地貼圖向上的方向一定是(0,1,0),unity自帶的草地就是用的這種方法的,這種固定向上方向的billboard最後形成的結果就是圖片繞着y軸進行旋轉,畢竟俯視的時候如果看到草地法線朝着視角肯定會露餡的。
第二種billboard就是固定法線的billboard,這種就是讓法線無時無刻都朝着攝像機,對於一般的例子效果是可以直接這樣做的。
這個shader最關鍵的部分就是旋轉,只要我們能構建出來一個旋轉之後的座標軸,就能得到旋轉後的座標。如下圖所示,可以假定一個上的方向,然後法線方向可以通過攝像機和頂點座標之差獲得,兩者叉乘,就可以獲得朝右的方向,然後把right和normal再進一步叉乘,就可以得到真正的UP方向了,這樣一來,我們就能得到最後的三個軸的方向。
具體實現的時候還要注意幾個點,
1.normalDir.y =normalDir.y * _VerticalBillboarding;
我們直接通過兩個極端情況來理解這個,_VerticalBillboarding爲1的時候,就是固定法線,因爲法線始終會是指向攝像機的。當_VerticalBillboarding爲0的時候,就是固定了向上的方向,這時候y是0,想象一下(x,0,z)作爲法線的平面,是不是就是繞着y軸旋轉的垂直的平面,向上的方向已經固定了。
2.假定的向上的方向的選取
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
實際上,並不是一定要選取(0,0,1)作爲假定的向上的方向的,我們只是把它作爲一個過程來計算三個軸的座標,當法線就是(0,0,1)的時候,叉乘就會出現錯誤,這時候需要重新選取假定的向上的方向了。
3.旋轉過程
這個旋轉過程並不是傳統的構造旋轉矩陣進行旋轉的過程,因爲旋轉只是旋轉,並沒有進行縮放,因此,遠來座標軸上的x,y,z值可以依然保留,直接乘以新的三個座標軸的方向向量就可以旋轉了。
參考圖片如下:
無論攝像機如何移動,那些星星都會始終面朝着攝像機。
完整代碼如下:
Shader "Unity Shaders Book/Chapter 11/Billboard" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1
}
SubShader {
// Need to disable batching because of the vertex animation
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed _VerticalBillboarding;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v) {
v2f o;
// Suppose the center in object space is fixed
float3 center = float3(0, 0, 0);
float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1));
float3 normalDir = viewer - center;
// If _VerticalBillboarding equals 1, we use the desired view dir as the normal dir
// Which means the normal dir is fixed
// Or if _VerticalBillboarding equals 0, the y of normal is 0
// Which means the up dir is fixed
normalDir.y =normalDir.y * _VerticalBillboarding;
normalDir = normalize(normalDir);
// Get the approximate up dir
// If normal dir is already towards up, then the up dir is towards front
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
float3 rightDir = normalize(cross(upDir, normalDir));
upDir = normalize(cross(normalDir, rightDir));
// Use the three vectors to rotate the quad
float3 centerOffs = v.vertex.xyz - center;
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
o.pos = mul(UNITY_MATRIX_MVP, float4(localPos, 1));
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 c = tex2D (_MainTex, i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}