这种效果是让一个平面能跟着视角进行旋转,从而使得该平面一直朝向着摄像机,形成一种立体的感觉.
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"
}