导读
阅读完此文,你会了解:
1、建筑效果需要的UV支持
2、如何使用Shader Graph创建Shader
建筑Shader效果
建筑渲染是地图引擎中非常重要的一个方面。地图引擎中有多个图层,如水系,功能面,绿地,道路,建筑,POI 等。建筑图层是唯一一个有高度的图层,是最能体现地图空间感的一个图层。针对建筑,我们已经积累了很多酷炫的效果,来提升地图的整体表现力。

我们使用一些自定义的建筑 Shader 来实现不同的建筑渲染效果。下面介绍两个建筑效果。
定制的UV信息
在《地图建筑渲染》我们曾提到,我们对建筑进行了侧面和顶面的分离,并且在UV信息中写入了建筑的尺寸信息和一些随机值信息。如果 UV 中没有建筑的尺寸信息,我们可以用世界坐标或者循环贴图等信息实现一些简单效果。而有了尺寸信息,我们就可以根据建筑的物理尺寸实现不同的效果,更加灵活。
对于建筑侧面,我们使用三组 UV 来存储需要的信息。如下图:

对于建筑顶面,UV信息采用和和侧面顶部顶点一样的UV信息。
使用这三组UV,在 Shader 中每个像素都可以计算出自己在建筑某一条边的横向和纵向位置,从而可以制作很多和建筑物理尺寸相关的效果。
可视化构建着色器
过去,只有具备一定编程能力的人才能通过专门的的语言构建 Shader,对设计师非常不友好。而现在随着技术的发展,已经出现了很多可视化的 Shader 编辑器,大大简化了着色器创建工作,为设计师和其他不懂编程的团队成员打开了进入这一领域的大门。只需在图形界面中连接节点,就可以用所见即所得的方式编辑 Shader。
Shader Graph 就是这样一款工具,是 Unity 引擎内置的着色器编辑器。我们的客户端地图引擎使用了 Unity 引擎来开发,下面介绍下用这个工具搭建的两个效果。

构建一个透明边框风格的建筑Shader
目标
建筑整体半透明效果,根据高度填充渐变色,边缘高光。另外在高层建筑顶部加入额外的高光线。

整体效果我们可以分成下面三个模块来实现,组合实现最终效果。
1. 渐变填充面模块
首先,我们创建一个Unlit Mater节点,选择 Additive 的混合模式。我们的颜色将会和和目标颜色求和,实现半透明效果。

下一步我们来指定 Color 的输入。我们直接取模型的世界坐标的 y,在两个输入高度值之间做Smoothstep,然后作为两个输入的颜色值 Lerp 的参数,实现建筑在高度方向上的渐变色。


2. 边过滤模块
先过滤顶楼和顶楼下方的高亮线。UV2 的 y 值我们已经写入了建筑的总高度,用世界坐标的 y 和这个值求一个距离,我们就能得到距楼顶的距离。

取到距离,我们对这个距离取一个 -a 次方( a > 0 ),-a次方在 x 轴正方向有很好的下降曲线,取值随着高度下降迅速衰减,可以实现顶部线的高亮变化。a 值也作为一个输入值,调整参数可以控制高亮范围。

最终,把值 Clamp 为 0-1,防止过曝,这个值就是顶部曲线的值。

同样道理,我们设置一个输入值 2nd line pos 来给高度值做一个往下的偏移,同样的参数,计算出楼顶下方的高亮线的值。

对于顶部下方的高亮线,有些太矮的建筑显然是不适合加入的。我们加入一个高度的过滤,只有比这个高度高的建筑,才有第二条高亮线。通过一个输入值来确定。还是用 UV2 上存的建筑高度来完成这个处理。

把这两条线的值临时直接连入 Master 的 color 节点,观察此时的效果。

可以看到,第二条线显示是正常的,而顶部的整个面都高亮了,这显示不是我们想要的效果。顶部面所有的点的高度值都是建筑的最大高度导致整个顶面的顶点都被高亮了。所以我们要过滤掉顶部面的处理,高亮通过侧面就可以实现了。实现方法很简单,顶面法线是朝上的,通过判断法线方向实现过滤。

用这个值和之前的高光值做乘法,传入 Master 的 Color 节点,会发现边缘正常了。

每一条侧面的竖边我们也可以从 UV0 里面保存的宽度信息 UV1 里面的总宽度值计算出距边缘的距离,使用同样的方法,可以过滤出每一条竖边。


3. 边颜色模块
我们给边也填充一个基于高度的渐变色。给出两个输入颜色值,我们由世界坐标 y 值和 UV2 y 值保存的建筑总高度,通过插值,计算出每个像素的颜色。

4. 颜色混合
最终,我们使用前面三个模块的计算结果来做颜色混合。使用边过滤模块的值作为 Lerp 的参数,在面颜色和边颜色之间插值,就可以得到最终混合的颜色,传给 Master 节点的 Color,就可以得到我们最终的颜色了。


构建一个分层带动画效果的建筑 Shader
我们使用一个不透明的 Shader 来实现这个效果,首先,创建一个不透明的 Lit Master。

1. 渐变色填充模块
和第一个效果类似,我们使用一个渐变色来填充建筑底色。使用世界坐标的 Y 来做插值。

2. 划分楼层填充
为了方便控制,我们加入两个控制节点,bar center 和 bar half width来控制楼层高度和偏移。使用世界坐标的 y 值 和 UV2 上保存的建筑总高度求差取得距楼顶的距离,通过前面的控制变量进行 Remap,获得多个取值间隔为 1 区间。

然后使用 Posterize 使这些 0-1 的区间离散化。通过求模,分成两个部分。每 7 层,取一个特殊的颜色,其余的部分每 3 层,取一个颜色。

我们把这个颜色临时连入 Master 节点的 Emission,观察下渐变 + 楼层填充的效果。

3 .加入流动动画效果
动画效果,我们主要使用到 Noise 和 Time 节点实现。首先提供一个输入值,来指定 Noise 贴图。采样的 UV 需要通过一些计算得到。V 我们直接使用步骤 2 中得到的离散的楼层值就可以了。U 使用 UV2 中保存的随机值和 UV0 的 x 值做一些简单的数学运算得到。另外我们提供一个可以调整贴图的参数 Tiling And Offset 节点,可以更好的支持在运行时调整参数查看效果。

最后就是 Time 节点的部分,计算很简单。同样为了调整方便,我们提供了很多输入值来控制最终取值的变化范围。将这个值取一个 Sine,获得一个变化的曲线。然后截取到 0到1之间。

把这个节点临时连入 Master 节点的 Emission,观察一下效果。
4 .颜色混合
最终,我们使用第一步获得的底色作为 BaseColor,第二步和第三步获得的两个颜色相乘,作为动画的颜色,传入 Emission,就可以得到最终的效果了。

结束语
这一些列的分享,我们分享了3D地图如何从零DIY,包括数据源的解析计算、控制器的设计、底图的渲染、可视化图层的渲染、如何做定制化建筑模型、如何做定制化建筑特效。以地图引擎为基础,我们也做了地图时空分析工具和样式编辑器、城市编辑器。这个系列的分享到这里就告一段落了,剩下的我们来日方长!