大家都知道,照明可以极大优化游戏的视觉质量。我将在本文描述易于执行并且不需要额外资产(如标准图)的不同动态2D角色照明技巧。我已在如《猴岛特别版》、《Lucidity》以及最近的《断剑》等多款游戏中成功使用这些技巧。
但角色照明在2D游戏中为何如此重要呢?通常情况下,美术人员会直接在场景美术中绘制光亮(以及阴影),这种做法并无不妥,因为其游戏世界是静止的。事实上,我们很难估计照明的用途,如实时背景图片(因为我们无法获知如标准方向等空间信息)。而角色在游戏世界中移动,并且在含不同照明条件的多个地点均具有可视性,所以一般不太可能在精灵(或纹理)中直接绘制出光源的效果。因此唯一的解决方法就是在运行时间中计算角色照明。
环境照明
环境照明背后的理念就是让角色看起来像是游戏世界的一部分而不是游离于其之上的物体。所以,当玩家从一个阴暗的地方转向一个完全照明的地方时,角色的视觉形象就会从暗转亮。
所幸我们可以通过给精灵(或纹理)染色来轻松实现这一点。使用渐变染色可以让我们独立控制或低或高部分的色彩,这就有利于效仿地面和角色之间的环境闭塞效果。
环境渐变染色是《断剑》照明系统的主心骨,运用于每个角色的每个场景。
我们可以用不同的方法为角色身体增添渐变色。最简单的方法就是使用边界框,即头顶使用渐变顶端,而脚部使用渐变的底部。这种方法极适用于基于精灵的角色,但并不太容易控制渐变的形状。
《断剑》运用了截然不同的方法,它计算了从边界框转化中心到每个顶点的矢量标准值。之后再根据所得标准值的Y坐标轴来抽取环境渐变。这种技巧不但极适用于剥皮几何体,它还可以通过更改中心的位移来决定身体添加渐变的速度。
局部照明
局部照明的目标是显示附近光源的影响。换句话说,它所要回答的问题就是角色与光照的位置关系。《猴岛特别版》中的一幕场景就呈现了篝火局部照明点亮了两位角色的效果。

monkey_island._special_edition(from gamebuy)
如果你凑近一点看,就会发现篝火只点亮了角色脸所朝向的那部分。我们可以用不同的方法实现这种效果。《断剑》中的角色手动创造了之后用于计算反射光的标准图。虽然绘制标准图让美术人员得到许多艺术创作自由,但显然也需要投入大量的创作时间。
在《猴岛特别版》中,我们由于开发计划的时间限制无法采用这种方法,所以我们需要采用一种无需额外资产,只要少量调整的技巧。我们的解决方案就是利用包含附近光源照明的屏幕空间光照图。
为了计算光照图,我们首先要在缩减采样和用高斯核模糊化之前将角色的alpha面具渲染到画面外的渲染目标。所得图像可以理解为一个包含二进制大对象近似角色的软化高度域。这样我们就可以像高度域坡度一样计算二进制大对象近似值的标准值。现在有了这个标准信息,我们就可以通过从所有附近光源积累反射照明来计算光照图。在《猴岛特别版》中,我们可以使用极为简单的Labertian照明模型,但也可以轻松使用一种更为复杂的模式。
其中所得照明数据可以用不同方式与场景相结合。最简单的解决方案就是使用额外混合在帧缓存的顶端位块传输光照图。我们在《猴岛特别版》中使用了这个方法并发现它甚为管用,但其劣势就在于反射光在明亮的像素中几乎不可见。
另一个影像合成战略就是从角色着色器的光照图中抽取照明。这样光照就可以用不同方式与精灵的纹素(或纹理)相结合,所以角色能够有效呈现多变的反射性。
光源
我还没有提到的一点就是光源的创造方式。它应该很容易以特定参数同游戏世界的不同部分相结合,以便匹配角色照明。
径向基函数一样的代表光源同时适用于环境和局部照明。每个角色的渐变染色都可以像基于与光源距离的加权平均值来计算。就我的经验来看,这也是展示一些全局照明的好方法。
光照图可以通过绘制像近似形状到渲染目标的光源来计算。每个输出片断的色彩和强度都是通过评估你所选择的光照模式来计算。在《猴岛特别版》中我们计算反射光的方法如下:
vec l = light_pos – fragment_world_pos
vec n = tex2D(normal_map, fragment_screen_pos)
scalar nDotL = saturate(dot(normalize(l), n))
scalar dist = length(l)
vec result = light_color * nDotL / (dist * dist)使用径向基函数的另一个好处就在于,它极易通过更改变形(如位置、比例)以及参数(如色彩、强度)来制作光源动画。你可以通过在两个状态间使用一个良好的噪声函数来实现闪烁的篝火这种效果。将这种光源置于角色手中就可以有效制作一个很酷的火炬。
总结
动态角色照明有助于制作游戏世界及其中居民,以便让它们看起来更具可信度和趣味性。所幸我们可以相当简便地执行能够产生良好视觉效果的照明系统,希望本文能够为你提供一些实用建议和启发。
