1、广告牌效果是什么
广告牌效果是一种图形技术,用于确保对象(通常是二维纹理面片 或 精灵(Sprite)图片)始终面向摄像机,同时在某些轴上保持固定的方向(一般分为全向广告牌和轴对齐广告牌)
在3D游戏中非常有用,它可以确保无论从哪个角度看、对象始终面向玩家,创造出一种始终可见的效果。
全向广告牌效果 是无论摄像机位置如何变化,对象在所有轴上始终面向摄像机。适用于 烟雾、火焰等需要从任何角度看都要正对摄像机的效果
轴对齐广告牌 是对象在一个特定轴上保持固定方向,而在其他轴上面向摄像机,适用于 树木、花草、人物等需要在特定轴上保持正确方向的效果
其中 垂直广告牌 就是一种特殊的轴对齐广告牌,对象在水平面(XZ平面)上旋转
但在垂直方向上始终保持不变
2、广告牌效果基本原理
想要实现广告牌效果,核心原理是旋转模型空间坐标系让其始终面向摄像机
想要达到这个目的,我们需要构建一个基于模型空间的新坐标系
坐标系由两个关键因素决定
- 原点(新坐标系中心点):基于模型空间的,可以自定义,但一般还是用(0,0,0)
- 三个轴向(X轴、Y轴、Z轴):通常情况下三个轴向由视角方向、垂直向上方向、右方向 构成
关键就是求出三个轴向(X,Y,Z)
- Z轴 = normal = 将摄像机位置转到模型空间 – 模型空间下新轴向空间中心点(一般还是0,0,0),old Up = 模型空间中Y轴(0,1,0)
- X轴 = right = Normal X OldUp (两个向量叉乘可以得到垂直于两向量所在平面的向量)
- Y轴 = new Up = Normal X Right
再定义一个新坐标系的中心点(相对于模型空间的),一般我们会将中心点Center定为(0,0,0)即原模型空间原点,那么此时我们只需要计算以下两步即可:
偏移位置 = 顶点坐标 – Center
新顶点位置 = Center + X轴 * 偏移位置.x + Y轴 * 偏移位置.y + Z轴 * 偏移位置.z
如果想要实现出 垂直广告牌效果,那只需要在计算轴向向量时进行修改即可,只需要在计算normal向量时,让其的y值变为0即可,相当于normal向量只在 xz 平面变化
3、实现
Shader "ShaderProj/8/Billboarding"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1, 1, 1, 1)
//用于控制垂直广告牌和全向广告牌的变化
_VerticalBillboarding("VerticalBillboarding", Range(0,1)) = 1
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" "DisableBatching"="True"}
Pass
{
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;
float _VerticalBillboarding;
v2f vert (appdata_base v)
{
v2f o;
float3 center = float3(0, 0, 0);
float3 cameraInObjectPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
float3 normalDir = cameraInObjectPos - center;
normalDir.y *= _VerticalBillboarding;
normalDir = normalize(normalDir);
//模型空间下的Y轴正方向 (0,1,0)作为它的 old up
//为了避免z轴和(0,1,0)重合 ,因为重合后再计算叉乘 可能会得到0向量
float3 upDir = normalDir.y > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
float3 rightDir = normalize(cross(upDir, normalDir));
upDir = normalize(cross(normalDir, rightDir));
float3 centerOffset = v.vertex.xyz - center;
float3 newVertexPos = center + rightDir * centerOffset.x + upDir * centerOffset.y + normalDir * centerOffset.z;
o.vertex = UnityObjectToClipPos(float4(newVertexPos, 1));
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float4 color = tex2D(_MainTex, i.uv);
color.rgb *= _Color.rgb;
return color;
}
ENDCG
}
}
}