广告牌技术,简单地说就是让一个平面始终朝向摄像机,感觉用的地方很多。最近在在玩全战三国,渣电脑开最低画质里面的树都是用了广告牌技术,一直都在随镜头旋转,不知道开了最高画质是不是也是这样子。
在这里我实现了一个一直朝向镜头的“滑稽”,代码其实就是《Unity Shader入门精要》里面的一个例子。
以下是效果图:
以下是代码
运算全在顶点着色器里面。其本质是构建旋转矩阵,我们需要三个基向量以及一个锚点,这个锚点是固定不变的。广告牌技术使用的基向量通常就是表面法线,指向上的方向,以及指向右的方向。
Shader "Summer/BillboardMat"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("Color",Color)=(1,1,1,1)
_VerticalBillboarding("Vertical Restraints", Range(0,1))=1
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent"
"IgnoreProjection"="True" "DisableBatching"="True"}
LOD 100
Pass
{
Tags{"LightMode"="ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed _VerticalBillboarding;
v2f vert (appdata_base v)
{
v2f o;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
float3 center=float3(0,0,0);
float3 viewer=mul(unity_WorldToObject,float4(_WorldSpaceCameraPos,1));
float3 normalDir=viewer-center;
normalDir.y*=_VerticalBillboarding;
normalDir=normalize(normalDir);
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));
float3 centerOffs=v.vertex-center;
float3 localPos=center+rightDir*centerOffs.x
+upDir*centerOffs.y+normalDir*centerOffs.z;
o.vertex=UnityObjectToClipPos(float4(localPos,1));
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb*=_Color.rgb;
return col;
}
ENDCG
}
}
}
float3 center=float3(0,0,0);
float3 viewer=mul(unity_WorldToObject,float4(_WorldSpaceCameraPos,1));
这里设定一个锚点(模型空间的原点),以及求视角位置。
所有的计算都是在模型空间下进行的。
float3 normalDir=viewer-center;
normalDir.y*=_VerticalBillboarding;
normalDir=normalize(normalDir);
这里根据观察位置以及锚点计算目标法线方向,而且根据_VerticalBillboarding属性控制垂直方向上的约束度
_VerticalBillboarding为1,法线固定为视角方向;为0,向上方向固定为(0,1,0)
float3 upDir=abs(normalDir.y)>0.999?float3(0,0,1):float3(0,1,0);
//abs 计算输入值的绝对值。
float3 rightDir=normalize(cross(upDir,normalDir));
upDir=normalize(cross(normalDir,rightDir));
然后是计算三个基向量,因为上方向还不是准确的,所以反过来上方向
float3 centerOffs=v.vertex-center;
float3 localPos=center+rightDir*centerOffs.x
+upDir*centerOffs.y+normalDir*centerOffs.z;
o.vertex=UnityObjectToClipPos(float4(localPos,1));
最后是根据原始位置相对于锚点的偏移量以及三个正交基矢量计算新的顶点位置
最后将顶点位置转换的裁剪空间
关于书上说用的是Quad而不能用Plane,因为这个代码是建立在一个竖直摆放的多边形的多边形的基础上的,我不太理解,便试了试,果然是不行,具体什么原因我也不知道。