《Trifox》中的遮挡处理和溶解着色器技术
发表于2017-02-18
本文作者是来自Glowfish Interactive的开发者Brecht Lecluyse,目前正在开发一款独特而多彩的顶视角双摇杆动作冒险游戏《Trifox》,灵感源自经典的跳台游戏,玩家在《Trifox》中扮演一只技艺超群的狐狸去拯救被侵略的家园。今天将由Brecht Lecluyse为大家分享《Trifox》游戏项目中遇到的角色及障碍物遮挡处理以及溶解着色器相关的问题。
我的角色在哪里?
定义问题
在开发过程中,我们面临的首要挑战之一,就是在全3D的场景中让主角保持在头顶视角的镜头之内。也就是说,如果有东西挡住了主角,是选择避免这些遮挡,还是把遮挡物隐藏掉?假使选择隐藏的话,如何采用一个视觉上令人可以愉快接受的方式,来让隐藏的过程符合游戏风格?我们如何保持不会妨碍游戏体验的空间感?
对于那些镜头设置类似于《Trifox》的游戏而言,这是一个非常常见的问题。所以在开始实现自己的方案之前,我们先参考了一些已有的方案。
鸟瞰视角
第一个方案是禁止关卡中出现任何横亘玩家与摄像机之间的巨大障碍物。这意味着大部分区域必须非常广阔,而墙壁和障碍物需要尽可能保持低矮,或干脆完全避免使用它们,并且摄像机与玩家角色要有一个特定的距离,尽可能垂直向下看。这种形式非常适合于“街机”类游戏,或场景中所有物体都面向同一个方向的传统顶视角RPG。然而在我们的案例中,视角与角色的距离拉远会导致玩家与主角产生疏远感。同时,也会使场景显得更加不自然,从而使玩家难以感知我们的游戏性、沉浸感以及视觉风格。
鸟瞰视角例子:Arrow Head的《Helldivers》
切割/掀盖
下一个方案是对环境进行切割。您可以把场景想象为一个多层蛋糕,基于当前镜头关注的区域,玩家只能看到某一层的内容。
当主角站在建筑外部时,可以看到建筑的屋顶;而当他进入建筑内部时,整个屋顶以及高楼层就被全部隐藏,只显示当前楼层的墙壁和地板。大多数情况下不显示屋顶,这样能避免额外的设置工作。这种方法能够给玩家自然进出建筑物的感觉,让游戏能有更复杂的关卡设计和更强的空间感。虽然我们依旧需要将镜头定位在一个合适的距离并向下看,但由于障碍物可以被隐藏或显示,构造环境的方法可以更加自由。
为了进一步优化这个方法,我们隐藏墙壁和其他各种物体,决定它们是否遮挡我们的视野,是否允许更低的镜头角度及玩家角色视野更近。
掀盖式相机示例:Firaxis Games的《XCom:Enemy Unknown》
这种方法唯一的缺点是会让人感觉不太自然。虽然可以通过淡出和使用透明材质来减轻这种感觉,但总体来说,我们希望尽可能远离这些东西,以避免重绘性能问题和全屏透明覆盖。这种方法确实更加符合需求,但对于我们的游戏而言仍然不是非常理想。
自然演化
在考察了常规的解决方案后,我们仍旧感觉有些问题。这些方法在大多数游戏中都工作得很好,但在这个项目上,我们总感到似乎缺了点什么。
所以我们具体想要达成什么样的目标呢?
1、障碍物应当能在平滑而自然的过渡之后被隐藏。
2、关卡设计师应当能控制哪些东西能够被隐藏,于是我们就可 以保留一部分依然能够遮挡视野的物体,在环境中增加纵深感。
3、镜头和角色之间的距离发生变化时,系统应当运作如常。
4、遮挡处理应当在任何角度下都能工作。
5、障碍物被隐藏后,玩家需要依然能够感受到它的存在。
6、设置工作需要尽可能简化。
我们最终得出的方案整合了之前所有的方法,并增加了额外的障碍物隐藏风格。
当玩家向遮盖物移动的时候,遮盖物会逐渐变透明,确保视野不被遮挡,同时清楚地表现出此处确实有物体存在。这样,封闭空间场景依然可以保持封闭的感觉,并且墙面逐渐淡出不至于太显眼。这同时意味着我们可以让多个相交的大小形状不同的对象在一个统一的方式下消失,而不需要额外的设置。
那么我们如何达成这个目标?解决这个问题需要考虑如何来轻松地创建一个很棒的隐藏效果,可应用于各种游戏中的各种物体上。本文将阐述实现最终方案的过程,从深入研究一些常用的溶解着色器技术开始。
强大的着色器
基础的溶解着色器与世界空间UV
基于噪音纹理的裁切
最简单的溶解效果可以使用2D噪音纹理和裁剪的着色器技术来实现。这个裁剪函数会将所有大于等于0的值绘制到屏幕上,让所有小于0的值不可见,可以作为材质的开关。
上述的噪音纹理应用到Unity默认的立方体的示例如下。
下一步是通过基于噪音纹理处理让这个立方体逐渐消失。
回过头去看看clip着色器函数的工作原理和渐变纹理,这就很容易实现了。只需取出纹理中的灰度值,并减去一个0到1之间的值,这个值可以作为透明度百分比。
表面着色器的实现如下:
half gradient = tex2D(_MainTex, IN.uv_MainTex).r; clip(gradient- _DissolvePercentage); |
这就是创建溶解效果的基本技术。很简单吧?
现在如果直接尝试可能会得到一个略有不同的结果。噪音纹理可能看起来更暗,增加透明度参数的时候,您可能会注意到对象会在大概75%的透明度下完全消失,而不是100%。
这并不完全是我们期待的结果。纹理像素的值完全处于0和1之间,为什么最终结果会这样呢?
这是因为纹理导入引擎时,会进行一次伽马校正。这种情况下,我们需要将纹理当作线性数据纹理使用,也就是说我们需要将rgba的值当作数据而非颜色来使用。所以需要确保信息不被导入过程所改变。这可以通过调整纹理的导入设置来实现。
在“导入设置”窗口中,将纹理类型设为“Advanced”并启用“Bypass sRGB Sampling”标志。下面的动画展示了启用该设置前后的区别。在处理一些用到了纹理数据的更加复杂的着色器效果时,这个设置尤其重要。
这种方式对于一些可展开的网格非常适用,但如果是其它网格呢?如果有一些互相交叉的对象,能否创建一个效果,让它们表现得像是一个整体?
除非特地在溶解纹理上展开了每一个对象,否则相邻的对象在溶解时会出现很大的差异。而对于缩放过的对象,将需要针对每一个缩放级别各自应用一个材质来保持噪音细节平滑。很明显,这会导致巨量的设置工作,不符合需求。下一步:用程序化世界空间展开来替代手动展开。
世界空间UV
通常,纹理是依据网格在创建时定义的UV坐标映射到网格表面的。现在我们想要替换这些UV坐标,转而使用一个基于表面在场景中位置的坐标系。
通过添加Unity内置的着色器输入参数“worldPos”,我们就可以在着色器中访问到这个信息。
下面的代码是对一个在XY空间移动的四边形进行调整的结果。使用存储在“worldPos”变量中分别表示世界坐标系x和y值的红绿通道来替换存储在网格中展开的UV坐标值。
原有代码:
half gradient = tex2D(_MainTex, IN.uv_MainTex).r;
用世界空间坐标替换UV坐标后的代码:
half gradient = tex2D(_MainTex, IN.worldPos.rg).r;
这是一个常见的着色器技术,可以用于各种有趣的程序纹理贴图技术。例如,可以用它自动在表面低于某个特定高度的地方创建水浸效果。
再次把调整过后的着色器应用于立方体,这里需要一些额外步骤才能使世界空间纹理技术能够正常用于淡出效果。目前这个展开程序仅支持3D平面,这里的示例是XY平面,因为用到了x和y坐标来代替UV坐标。
可以通过一些向量运算计算出能够作用于所有表面的UV集,无论这些表面在空间中的位置和方向如何。最终实现一个纹理应用于网格后,它能在不同的方向、缩放以及位置下保持连贯。
最终应用于游戏资源的效果如下:
这足以应付大多数情况,但我们并未止步于此。从上图可以看出,使用这种噪音可能会产生令人不适的边缘溶解效果。此外,由于使用了纹理,在接近对象的时候会观察到明显的像素变化。我们同样也无法保证噪音纹理能够在很大的表面角度差和互相交错的物体之间都保持很好的连续性。
未完待续
以上就是第一部分的内容。第二部分(https://madewith.unity.com/stories/dissolving-the-world-part-2)将阐述如何处理由于使用噪音纹理而产生的问题,改善溶解效果的整体质量,如何在Trifox中使用这个简单的技术来创造所有有趣的视觉效果(包括动态镜头遮挡处理),以及一些很酷的游戏机制。