动态2D角色光照

发表于2015-09-22
评论0 5.5k浏览

动态2D角色光照


本文原文英文,由Oliver Franzke发表在Gamastra,原文链接http://www.gamasutra.com/blogs/OliverFranzke/20140828/224326/Dynamic_2D_Character_Lighting.php,游戏上有篇机翻结果实在比英文还难看懂,于是决定自己翻译,如有错漏冀望指正!

另外,不要问我为什么没有图片,因为作者他用了不可访问的链接!原文也显示不出来!


动态2D角色光照

 

光照可以极大地提高提高游戏的视觉质量已经不是什么秘密了。在这篇博客中,我将介绍几种非常容易实现而且不需要额外资源(比如法向图)的动态2D角色光照实现技术。我已经成功地在几款游戏中应用了这些技术,如《猴岛:特别版》(Monkey Island: Special Edition)、《明朗》(Lucidity)和最近的《破碎时光》(Broken Age)

为什么角色光照在2D游戏中很重要?通常美术会把光照(和阴影)直接画进场景原画中,而由于世界通常是静止的,这么做也没什么问题。但事实上去实时计算一张背景图片的光照是非常难的,因为没有计算需要的空间信息(比如Z轴深度、法线方向等)。另一方面角色因为在世界中穿梭而出现在有各种不同光照环境的位置上,所以通常把光源的影响直接画到精灵(或纹理)上是不太可能的。因此唯一的解决方案就是在运行时计算角色光照。

下面的视频显示了Shay(《破碎时光》的主要角色之一)在有和没有光照时的表现。我希望你看了之后会认同光照对Shay融入(美丽的)世界有所帮助。

 


视频链接点开前保证可以访问youtube

 

环境光照(Ambient Lighting

环境光照的主要想法是让一个角色看起来像是游戏世界的一部分而不是浮在它的表面。举个例子,假如一个玩家从一个阴影区域移动到一个完全照亮的区域,角色的视觉表现应该从暗变亮。

 

B_D2DCL_Ambient_Gradient(丢失的图片) 

 

幸运的是这可以通过对精灵(或纹理)染色而轻松地实现。使用一个渐变染色就能分离控制上下半身的颜色,这样就可以模拟地面和角色间的环境光遮盖了环境渐变染色是《破碎时光》光照系统的骨干,应用到了每一个场景每一个角色中。下面的图片展示了Shay的环境渐变是如何随着他的位置变化而变化的。

 

B_D2DCL_Ambient_Gradient_Sampling(丢失的图片) 

 

有一些不同的方法可以在角色身体上插值渐变色。最简单的做法是用包围盒,在这种情况下角色头部的顶点从渐变的顶部采样而足部则使用渐变的底部。这种方法在基于精灵的角色上表现突出,但没法支持对渐变形状的丰富控制。

 

B_D2DCL_Normals(丢失的图片) 

 

《破碎时光》应用了另一种策略,计算从包围盒中心到每个顶点的向量作为法线。之后环境渐变色就可以基于结果法线的Y轴来采样了。这项技术不止在蒙皮几何体上表现出色,可以通过改变中心点的偏移可以改变渐变色在填充全身时渐变得有多快。我在GDC欧洲的演讲“《破碎时光》的可扩展性之路”(Broken Ages Approach to Scalability包含了关于这种方法的更多信息,如果你想知道更多,欢迎查阅。

 

 

局部光照(Local Lighting

局部光照的目标是展示附近光源的影响。换句话说它回答了角色相对于光在什么位置的问题。以下《猴岛:特别版》的视频展示了营火的局部光照是如何映在2个角色上的。请尽量在全屏高分辨率的情况下观看,因为效果还是比较微妙的。如果你仔细看,你会发现营火只照耀了角色面向它的那一部分。


视频链接点开前保证可以访问youtube


有几种方法可以实现这个效果。《破碎时光》的角色有手动编辑的法向图用来计算反射光。在绘制的法线提供了巨大的美术自由度的同时,显然也需要花费大量的时间去制作。《猴岛:特别版》中,我们因为开发计划时间紧迫没能走上这条道路,所以我们需要一个不需要额外资源且只需要最低限度调优的技术。我们的解决方案使用了一个包含屏幕空间近距离光源照亮范围的光照图。下图展示了一个场景的光照图。

为了计算光照图,我们首先把角色的透明遮盖(alpha mask)渲染到一个屏幕外渲染对象(见图A),然后向下采样并用高斯核函数模糊(图B)。结果图可以理解成是一个包含了角色的近似色块的柔和高度图。有了这个我们现在就可以计算这些近似色块的法线作为高度图的坡度了。有了法线信息,我们就可以通过把从所有临近光源得到的反射光累加起来,最终计算出光照图。在《猴岛:特别版》中我们用了一个非常简单的朗伯光照模型(又被称为“n· l”),不过更复杂的公式也可以容易地替代它。

生成的结果数据可以用不同的方法和场景结合。最简单的方案就是把光照图用附加融合的方式直接贴到缓存上。我们在《猴岛:特别版》中用了这个办法,表现很不错,不过有个缺点就是反射光在亮的像素(比如GuybrushT恤)上几乎看不到。

另一个混合策略是在角色的shader上采样光照图的亮度信息。这种方法光照可以以不同形式集成进精灵(或纹理)的纹素(texel)中,这样角色可以有效的获得丰富的反射。这种情况下光照图实际上作为了一个光照预处理步骤的结果

 

光源(Light Sources

有件事情我到现在还没有提到,那就是光源是如何编辑的。为了能够使角色光照与画进环境的亮度匹配,把世界的不同部分用特定参数关联起来应该不难。使用基于圆周的函数代表光照在环境光和局部光中都好用。每个角色的渐变染色可以通过基于到光源距离的加权平均来计算。在我的经验中,使用只在附近没有光源时才生效全局光也很不错。下图展示了基于圆周的环境渐变染色融合函数。光照图可以用画光源近似形状(比如四边形或者圆)到渲染目标的方式实现。每个输出碎片的颜色和强度是通过计算你选择的光照模型得出的。在《猴岛:特别版》中我们简单地通过以下方法计算反射光:

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)

 

使用基于圆周的函数有额外好处,使光源产生动画可以用改变它的转换(如位置、大小)或者参数(比如颜色、亮度)来简单实现。一个跳动的营火可以在两种状态中用一个(漂亮的)噪声函数插值就能简单获得。把光源绑定到角色手上就能有效地制造一个酷的火炬。

 

总结

动态角色光照帮助游戏世界和其居民看起来更可信和有趣。幸运的是实现一个产生漂亮效果的光照系统非常简单,我希望我的博客能给你一些启发和实践建议。感谢阅读,最后一个走的请把灯关了!:-)

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引