效果
转动摄像机时,无论从哪个方向对着物体,会看到物体看起来总是正对着摄像机。
思路
为了使物体不会随着摄像机的角度的不同,而成像的面向不同。可以使用旋转矩阵,对物体实时进行旋转上的修正。即求得模型空间下,以视角方向做为法线方向,并计算出其他两个坐标方向,up与right,构成一组正交基。再使用它对原顶点进行转换。
实现
Shader "Custom/BillBoard" {
Properties{
_MainTex("MainTex",2D) = "white" {}
_Color("Color",Color) = (1,1,1,1)
_VerticalBillBoard ("Board", Range(0,1)) = 1
}
SubShader{
//半透明纹理,三个选项
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
//模型空间的顶点动画, 需要关闭合批
Tags { "DisableBatching"="True" }
Pass{
//前向渲染
Tags {"LightMode" = "ForwardBase"}
//半透,关闭深度写入
ZWrite Off
//需要双面渲染, 关闭裁剪背面
Cull Off
//混合 - 透明度混合模式
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _VerticalBillBoard;
struct a2v{
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
#include "UnityCG.cginc"
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
v2f vert (a2v v) {
v2f o;
float3 center = float3(0,0,0);
float3 view = mul( unity_WorldToObject, float4(_WorldSpaceCameraPos,1 ) ); //得到模型空间下的视野方向
float3 normalDir = view - center; //计算法线方向
normalDir.y = normalDir.y * _VerticalBillBoard ; //约束垂直方向的程度
normalDir = normalize(normalDir);//求得normal方向(即视野方向)
float3 upDir = abs( normalDir.y ) > 0.999 ? float3(0,0,1) : float3 (0,1,0) ; //为防止upDIr与normalDir 平行, (因为平行时,求叉积会错误), 先假定一个up方向
float3 rightDir = normalize( cross( upDir,normalDir ) ); //先计算右方向
upDir = normalize(cross(normalDir, rightDir)); //计算上方向
float3 centerOffs = v.vertex.xyz - center;
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;//根据计算得到的三个正交基,算出新的顶点位置
o.pos = UnityObjectToClipPos( float4(localPos, 1));//计算到裁剪空间中
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); //uv修正
return o;
};
fixed4 frag (v2f i): SV_Target{
fixed4 c = tex2D( _MainTex, i.uv );//纹理采样
c.rgb *= _Color.rgb;
return c;
};
ENDCG
}
}
FallBack "Transparent/VertexLit"
}