广告牌在实际游戏开发中使用非常广泛,可以用于草丛,云朵等,其实现的具体效果就是让摄像机在任意角度时,使用广告牌的物体都会面向摄像机,广告牌属于经典的顶点变换。
1. 创建相关场景,shader,材质球,和一个Quad,为了方便观察,我们将Quad沿x轴翻转50度并设置后位置,完成后效果如下:
2. 我们都知道如果想让一个物体面向摄像机的时候,需要表面法线和向上方向的垂直,但往往因物体角度的旋转导致两者并不垂直,此时我们需要固定一个方向来更正另一个方向
3. 打开shader文件,首先简单定义一下shder内属性和方法,其中_VerticalBillboarding字段用来固定一个视角
Shader "Youcai/test/BillBoard" {
Properties{
_MainTex("Main Tex", 2D) = "white"{}
_Color("Main Color", Color) = (1,1,1,1)
_VerticalBillboarding("Vertical Restraints", Range(0,1)) = 1
}
SubShader{
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 "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _VerticalBillboarding;
struct a2v
{
float4 vertex: POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos: SV_POSITION;
float2 uv: TEXCOORD0;
};
void vert(in a2v v, out v2f f)
{
}
fixed4 frag(v2f f) : SV_Target
{
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
4. 定义顶点着色器和片元着色器内容,其中顶点着色器内为核心部分
void vert(in a2v v, out v2f f)
{
//计算以模型空间下原点为锚点,根据顶点位置最后做偏移
float3 center = float3(0, 0, 0);
//将摄像机位置转为模型空间位置
float3 object_viewer = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
/*
1. 我们首先假定摄像机到锚点位置为表面法线(不理解可以看步骤2)
2. 通过控制法线的y轴值来确定固定的视角为法线还是向上方向
3. _VerticalBillboarding越接近1,normalDir值y值不偏移,则越强制偏向摄像机
*/
float3 normalDir = object_viewer - center;
normalDir.y = (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(normalDir, upDir));
//通过向右方向在求取正确的向上方向
upDir = normalize(cross(normalDir, rightDir));
//获取当前顶点距锚点偏移值
float3 centerOffs = v.vertex.xyz - center;
//求取正确顶点位置 向右对应x轴,向上对应y轴,法线对应z轴进行偏移
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
//片元传递正确uv和坐标
f.pos = UnityObjectToClipPos(float4(localPos, v.vertex.w));
f.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
}
fixed4 frag(v2f f) : SV_Target
{
fixed4 c = tex2D(_MainTex, f.uv);
c.rgb *= _Color.rgb;
return c;
}