什么是公告牌

基于视角确定纹理矩形朝向的技术就叫做Billboard(公告牌)

公告牌结合Alpha纹理和纹理动画能实现许多诸如草,烟,火,雾,爆炸,云等特殊效果

核心思想

基于视角指定物体的表面法向与向上向量,叉乘得到物体的向右向量,从而建立一个新的基于视角的坐标系,在物体空间(Obejct Space)根据新的旋转后的坐标系实施对物体的旋转。具体实施的话可以记录原物体在物体空间(Obejct Space)的各个顶点相对于物体空间原点(0,0,0)的各方向(x,y,z)位移,利用从世界空间(World Space)变换到物体空间的相机位置确定视线方向从而确定物体法向(物体表面法向将与视线方向相反且平行),并利用指定向上方向重建新的坐标系,将原物体顶点相对位移转移至新坐标系建立转向后新的位置,实现在物体空间下完成物体转向。

最后所得的旋转矩阵为 M = ( (向右),(向上),(物体法向) ) ,全程在物体空间下变换。

分类

  • Screen-Aligned Billboard 对齐屏幕的公告牌

最简单的是屏幕对齐的广告牌。这种形式与二维精灵是一样的,因为图像总是平行于屏幕,并且有一个恒定的向上向量。摄像机将物体呈现到与远近平面平行的视图平面上。对于这种类型的广告牌,所需的物体表面法线是视图平面法线的相反方向,其中视图平面的法线n指向远离视图位置的方向。向上的向量up来自相机本身,这定义了相机的向上方向。这两个向量已经垂直了,所以只需要「正确」的向右向量r就可以形成广告牌的旋转矩阵。因为n和up是相机的常数,所以将该旋转矩阵用于场景中所有的公告牌。常用与粒子系统或总是面向屏幕的文本。

  • World-Oriented Billboard 面向世界的公告牌

在屏幕对齐的广告牌中,当视角水平旋转时(摄像机以视线方向为轴旋转),公告牌将会在世界空间一起旋转从而保持在视图中的固定不变,然而公告牌这种行为有时候是违反世界规律的,如摄像机水平旋转,天空中的云本不应该会水平旋转,然而为了保持与屏幕对齐它会水平旋转甚至转至上下颠倒(若此时相机正在倒立观看的话)。这时候,就要将原先向上的向量由相机本身的向上向量变为世界空间下的向上向量,再用这个向上向量与物体法向(与视线方向相反且平行)叉乘求得向右向量,最后用物体法向与该向右向量叉乘求得物体最终准确的向上向量,完成新的坐标系的重建

最后所得的旋转矩阵为 M = ( r,u,n ) ,全程在物体空间下变换。

有时在FOV(file of view)较大时,由于投影矩阵的作用,离视轴较远(既靠近视图边缘)的真实物体(既非公告牌)会有些明显拉伸畸形,而由于公告牌始终平行视平面,无论处于视图哪个位置都不会有畸形或形变,看起来有时反物理,有点假。此时若想刻意表现这种畸形效果(这种畸形并不是坏处,有时能增强表现力),就要用到Viewpoint-oriented的方法来代替原先Screen-Aligned 的方法。此时,原先物体表面法向由原来的与视平面法向平行变成从物体中心指向相机位置。Viewpoint-oriented Billboard用和真实物体变换到视平面相同的变换方式来变换图片,所以最后公告牌会出现上述描绘真实物体那样的畸形。

  • Axial Billboard 轴向公告牌

有些公告牌并不直接面向观察者,而是绕著世界空间某个固定的轴旋转并尽可能的面向观察者,如渲染远处的树木,树是一个公告牌面片,树干朝向固定为世界坐标的向上方向,树面片以树干为轴旋转,若玩家俯视树就会发现树只是个垂直于地平面的面片,此时若为树增加一个横截面面片(无公告牌效果)与地平面平行,可以减少俯瞰时的违和感。

具体实现

在UnityShader中实现Billboard

代码参考《UnityShader入门精要(冯乐乐)》

//全部在顶点著色器下完成
v2f vert(a2v v)
{ float3 center = float3(0, 0, 0);
//物体空间原点
//将相机位置转换至物体空间并计算相对原点朝向,物体旋转后的法向将与之平行,这里实现的是Viewpoint-oriented Billboard
float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1));
float3 normalDir = viewer - center;
// _VerticalBillboarding为0到1,控制物体法线朝向向上的限制,实现Axial Billboard到World-Oriented Billboard的变换
normalDir.y =normalDir.y * _VerticalBillboarding;
normalDir = normalize(normalDir);
//若原物体法线已经朝向上,这up为z轴正方向,否者默认为y轴正方向
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
//利用初步的upDir计算righDir,并以此重建准确的upDir,达到新建转向后坐标系的目的
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));
}

注意事项

在unity的检视面板中物体的scale属性必须是等比例缩放,且rotation属性全为0,广告牌才能达到预期效果。这样物体空间的向上向量才会与世界空间的向上向量同向平行

否则将会出现特定视角出现物体形变或向上的固定方向不如预期的情况,如下

原因是在物体空间旋转后的物体受到了scale属性在原物体空间各坐标轴上的缩放影响,但可以利用这个技巧达到一些特殊效果,如特定视角缩放物体

正面看火把,有长方形的热扰动面片
上方看火把,热扰动面片变成正方形,匹配火焰形状

可以看到只有x和z方向呈正比例,所以从上往下俯瞰到的xz坐标轴呈现的是正方形,同理,从正面看是y轴方向稍长的长方形。


推荐阅读:
相关文章