如何避免引擎项目里面的美术资源的“脏”

  厨师做菜,一般讲究色香味俱全。然而有时候一道美味的菜肴,吃下去之后却会引起肚子痛。这可能是因为菜里面有脏东西,比如说细菌什么的,肉眼看不出来,吃下去之后就发作了。
  游戏引擎的美术资源有时候也存在相类似的情况,很多美术工作者辛辛苦苦做完了美术资源,截图看很好,单纯放在工程里面预览也很好。但真正放进项目里面给程序调用,就错漏百出,或者不能用,或者严重影响了项目的性能和功能。
  我个人认为,这也是因为美术资源“脏”了,但从肉眼看不到,如何避免这些脏的情况,下面就拿显微镜把这些脏东西给放大看看。

一、文件存放习惯

  引擎项目里面的文件我一般也分成2类,一类是最终程序使用的文件,比如预设文件,或者场景文件;另外一类是正式文件依赖的原始文件,这里成为原始美术资源。
  对于正式使用的文件,一般程序有比较严格的要求,是不容易出错的,毕竟如果放得不对,就加载不到了。但对于原始美术资源,情况就比较多样。
  对于原始美术资源,不同项目,有不同的文件夹归类存放的习惯和标准。比较常见的有:
1、按照文件用途分,比如场景类资源放在一起,角色类的资源放在一起,特效类的资源放在一起,等。

2、按文件类型分,比如fbx文件放在一起,贴图文件放在一起,动画文件放在一起,等。

  我自己的习惯,是两者结合,先按照用途分,然后再在用途内部按照类型来分,然后shader必须不论用途全部放在同一个文件夹内。这样做的好处有很多,比如
1、想找到某个类别的文件很容量

2、不同用途类型的资源不同人上传,一般不会交叉文件夹,不容易乱

3、写工具批处理某个类别的资源比较方便

4、等

  如果原始美术资源文件存放的文件夹“脏”了,会引起什么后果呢?
  有些美术同事,并不是特别在意原始美术资源放在哪里,随便建个文件夹放进去,只要最后需要用的预设放在程序指定的文件夹,就认为可以了。实际上可能产生很多问题:
1、资源出问题了之后,想找到对应的文件,就只能逐个文件去看引用

2、有重复的文件不能很容易的找出来

3、想批量处理某些文件,找不到规律。

4、如果提交有误,比如某次svn提交出错了,很难根据文件夹的规律去找到出错的批次,只能逐个文件去查记录。

5、等
  遇到这种情况时,就要花大量的人力去查问题和整理了。

二、文件命名习惯

  从我多年的工作经验里看,美术工作者一般是不关心文件本身叫什么名字的,只关心内容。比如图片好不好看,动画做得是否流畅美观,模型是否有足够的细节,等等。很多时候,辛辛苦苦做了几个小时的美术资源,却因为随便命名了个文件名,就被程序员骂。估计很多美术工作者心里面都是不服气的。
  事实上,如果命名得不好,随便命个名,我认为就是命名“脏”了,会出现什么样的后果呢?
1、在比较规范的项目里面,美术资源都是填表来指定使用的。如果策划同事已经在表格里面填了一个资源名,美术在改动的时候,随便命名了另外一个名字,会导致加载不到资源,策划同事肯定会骂街。

2、提到了填表的问题,策划同事一天需要填大量的表格,如果文件名是有规律的,比如角色资源的文件名就叫character_xxxx,xxxx是对应角色的名字,那么策划同事在填的时候可以一目了然,不容易出错。反过来,如果文件命名完全没有规律,策划同事每填一个资源,都需要打开项目,去预览一下这个资源,才能填表,那么策划同事又会骂街。

3、美术资源一般都是需要反复的修改的,有些美术同事喜欢在修改之后,不舍得删掉原来的文件,怕某天需要还原回来,就在原始的文件名后面加一个2,或者加个new之类,久而久之,就会出现,xxx23456,或者xxxnewnewnew等文件名,美术同事在修改的时候,当然知道当时是为了什么修改的,但过来2个星期之后,估计就忘记得差不多了,然后出问题之后要找之前某个版本,就可能不知道那个文件才是需要的。其实我们都是用项目管理工具的,比如SVN之类,每次提交都写上备注,如果出问题的时候,随时可以还原到某个版本,根本就不需要害怕替换文件。

三、文件重复

  在美术的工程项目里面,很容易出现重复文件,比如某张图片已经出现过很多次了,某个模型也出现过很多次了。
  出现这种情况的原因有很多,首先是上面说的文件夹存放习惯不好,没有同一类的文件存放在一起,所以出现了重复的文件也不知道。
  另外一种情况,是不同的人刚好都用到了相同的资源。比如一个闪光的特效,用到了一个叫做glow的序列帧图片。UI特效同事用到了,从别的项目拷贝了一份到项目;技能特效同事用到了,又拷贝了一份到项目;地编同事也用到了,又拷贝了一份到项目。这样一来,项目里面就存在了3份一模一样的资源了。
  图片重复其实还算好,毕竟只是可能浪费点资源容量和内存而已。但如果是同一个shader拷贝了多次进项目,就会出现问题了,尤其是这三个shader的shader名称一样,内容却不一样的时候。当我们用Shader.Find来查找时,都不知道实际用的是哪个shader了。
  这种文件重复的“脏”,是很难去避免的,要依靠团队里面统一良好的命名和存放习惯,还有资源使用的统一规划,再编写相应的工具去检查,才能比较好的去避免。

四、资源内容不合理

  这种“脏”的原因就根据不同资源类型不一样,可以分别说一下。

1、图片

本来图片是美术最单纯的一种美术资源形式,所见即所得,是比较难以出错的。但作为贴图了之后,就会产生一些问题。

1.分辨率问题。

  很多美术认为,只要出原图出得很大,那么显示的时候肯定就会很清晰。
  这其实是一个误区来的。
  首先,图片的分辨率要看引擎和设备的支持。比如出一张4096x4096的贴图,甚至更大一点的,有些手机设备是不支持的,所以把图片导入项目的时候,很可能是设置了最大分辨率是2048。这个时候美术出超过2048的贴图,除了会增大项目资源加载容量,增加项目检出时间以外,其实并没有什么好处的。
  然后,有个显示像素区域比例的问题,比如一张2048x2048的贴图,需要显示在屏幕上面一个100x100的区域,会产生什么效果?是不是特别特别的清晰?答案是否定的。由于100x100的区域显示不了2048x2048个像素,所以只能对这庞大的像素进行筛选,只显示其中的一部分内容。
  在3D游戏里面,这种现象就会表现成贴图闪烁,因为每一帧筛选的显示像素可能都不一样。为了解决这个问题,一般3D游戏都需要使用LOD等级,把原图转换出很多不同分辨率的图片,比如1024x1024、512x512、……32x32之类,然后根据实际显示的范围来选择需要显示哪一个等级的图片。所以图片出得远超于实际显示大小,除了增加包体容量,增加占用内存之外,其实并不会变清晰。
  出大分辨率图片唯一的好处是,如果是自由镜头的游戏,可以把摄像机拉近到非常贴近展示对象的时候,贴图也不模糊。但是不是每个游戏都有这样的需要,就要看自己的需要了。

2.压缩问题

  在不同的设备上,可以选择的图片压缩方式是不一样的,比如PC端的DXT格式,比如安卓的ETC2格式等。有些是要求4的倍数才能压缩,有些是要求2的次幂分辨率才能压缩。所以在做图的时候,我们都会习惯做2的次幂分辨率的图,比如如果出一张图分辨率是1024的,基本上所有设备都能进行压缩,但如果出一张贴图是1023的或者1025的,在有些设备里面是不能压缩的,就导致了包体容量和内存翻倍的问题。

3.图集分辨率的问题

  由于不是什么图片都能做成2的次幂的,特别是UI上用到图片。所以一般引擎都会把这些散图打包成图集来使用。比如Unity引擎就有这样的功能。而打包图集,由于要顾及到所有设备都能压缩,所以最终图集的大小肯定是需要2的次幂的。
  如果美术出一张图是1024x1024,那么图集的大小可以完整的1024x1024一张图。如果美术出的图是1025x1024,那么图集的大小将会变成2048x1024。美术的同事肯定会觉得很不能理解,我就出多了一个像素,怎么就变大了这么多,是不是冤枉了?事实上就是这样。

4.图片格式的问题

  不同引擎,不同平台可以使用的图片格式不一样的,比如Unity引擎,连psd格式都可以用。有些美术同事为了贪图方便,会把psd源文件直接上传到项目里面。最后效果也没有错,只是文件容量大,导入的时间长,导致大家打开项目的时候都浪费更多时间而已。
  然后有些格式,比如有些美术资源下载网站,从某些游戏里面破解出了dds的贴图格式。如果把这些贴图放在unity编辑器里面,在编辑器也能正常显示,但到了打包iOS平台的时候,就会显示了。

2、网格模型

  网格模型的“脏”问题也有多种

1.面数太多

  这是常见问题了,美术会觉得做的模型越精细越好,但程序和TA是觉得渲染压力大,不停的叫美术减面,形成了一个矛盾。
  我觉得真正有经验的游戏模型师,并不是那些可以不靠贴图就做一个非常真实的影视级别高模的人,而是在有限面数内,配合着各种贴图效果,得到一个综合效果不错的低模的人。

2.附加信息不对

  网格模型从肉眼看到的,就是点线面的信息。但其实他还有很多其他附加信息,在做不同效果的时候可能会用到,比如顶点颜色、法线、切线、顶点是否缝合等等。有可能有时候做到某些效果,才会法线原始的网格信息是不对的。

3.分割太多子网格

  如果在没有特殊需要的情况下,模型的网格最好合并在一起,这样方便程序的统一管理。当然有时候是有需要的,比如角色的脸部需要做表情,就需要把脸部分离出来。
  有时候美术不注意,会随意就分离出新的子网格,不同的网格其实本身是使用同一张贴图的,但由于赋予了不同的材质球,导致原来一个DrawCall能解决渲染的角色,变成了需要多个DrawCall才能渲染出来。当然,从美术的角度看,是没有问题的,因为1个DrawCall显示出来的角色,和10个DrawCall显示出来的角色从视觉上并没有什么区别……

4.碰撞体管理不当

  之前我受到一家展示公司的邀请,去帮他们解决一些技术问题。他们做的是一个使用Unity引擎的汽车的展示项目,遇到的问题是简单展示一个汽车模型,就非常的卡顿,基本上跑不起来。我看了一下,其实问题很简单,他们的汽车各个部分加起来有十多万面,每一个mesh对象都挂上了MeshCollider网格碰撞组件。这个组件应该是Unity引擎放模型进场景的时候自动生成的。从原理上说,虽然我们并没有用射线去检测这些碰撞体组件,但实际上从它们进入摄像机视野范围内后,这些网格碰撞就开始生效了。所以每一帧在计算十几万面的碰撞,就卡顿到爆了。直接把MeshCollider去掉之后,项目跑起来就流畅无比了。
  也许有些美术同事会说,我真的就需要碰撞检测,比如那个车门,我真的就要点击能开,怎么办?其实很简单,在上面套一个盒子,使用BoxCollider就行了,毕竟一个盒子才几个面,比起十几万面的检查,省了太多性能了。
  但这个碰撞的问题,一般美术人员都不会去注意的,所以也是经常出问题的。就算不是引起卡顿,但由于他会阻挡正常的碰撞检查,如果我们真的想在场景里面做一些碰撞功能,就会被一些没必要的碰撞给干扰了。

3、动画

  动画的“脏”问题有时候是很难发现的:

1.烘焙关键帧

  有时候动画师做出来的动画之后,发现角色的脚步有漂移或者滑步的现象,但又不知道怎样解决,就直接烘焙关键帧,把每一帧的每一个骨骼都打上所有关键帧。
  这样做了之后,从动画师的角度看,动画变得准确了,一点问题没有。从TA和程序员的角度看,这个动画的容量大了几十倍。
  真正要解决动画偏移或者滑步,应该是在适当的地方加上关键帧,然后调整过渡曲线,正面的去解决问题,而不能投机取巧的。不过这需要经验和技术,不是每个动画师都能做得到。

2.乱添加动画事件

  有时候在运行项目的时候,会看到一些警告报错,大概意思就是某动画事件没有接收的方法。这种情况一般就是由于在动画上面,被添加了一些莫名的动画事件。有时候问美术的同事,为什么你要添加这个动画事件,得到的回答是,我也不知道,可能是手抖了,可能是从别的项目里面拷贝过来的时候就带着事件。
  动画事件不是不能用,而是如果真的用了动画事件,程序员肯定要知道的,因为每个动画事件都是需要写程序方法去接收的。

3.首尾不添加关键帧

  有时候单独看某个动画,是很正常的能播放的,但几个动画一起播放之后,某些动画就会出现异常。比如说一个角色连续几个动画一起播之后,某个动画播放时候头部的位置就不对了。这是因为,在某个动画里面,头部没有打关键帧,导致当另一个动画改变了头部的位置之后,到了该动画需要播放的时候,头部的位置还保留在前一个动画的位置。

4、材质球

材质球可能会遇到这些问题:

1.使用了默认材质类型

  比如Unity引擎,在放进去Fbx文件的时候,一般来说都会自动创建一个材质球,让模型能正常显示。
  这个默认的材质球是使用了默认的Standard材质,这个Standard的Shader非常的复杂,占用内存非常的大,我一般是不允许美术直接用的。
  一般我的项目里面的要求,是把Mesh网格从Fbx里面复制出来,然后给每个模型新建对应的材质球,不使用默认创建的材质球。

2.多维子材质

  有时候模型在原始三维软件里面,比如3DsMax里面,制作的过程没有控制好,参数了多维子物体,并且使用了多维子材质。那么在导入到引擎的时候,会生成了多个不同的材质球来对应同一个模型的渲染。
  当然这种情况有时候是必须的,有些效果就是需要。但更多的时候,是不需要的,只会在相同的渲染效果下,白白增加了DrawCall。

五、项目内存放非必要文件

  有时候美术需要做很多和策划沟通的文件,比如一些效果的视频录屏,一些草图。或者一些源文件,比如一些PSD源文件、3Dsmax源文件等。
  这些文件,容量很大,甚至比真正的引擎内的美术资源要大。但这些文件,并不是需要在引擎里面直接应用的。
  有些美术同事,习惯把这些文件直接放到引擎项目里面就提交了,比如提交到svn上面的美术资源引擎项目里面。这些文件,由于并没有被使用在预设或者场景里面,所以对项目原则上是没有本质的影响的。但问题在于,只要把文件放进去项目里面,引擎就会加载。所以如果放了很多这种没必要的文件在项目里面,直接导致的后果就是,我们检出svn的时候,会非常慢,而打开引擎项目的时候,加载时间会非常的长。这样其实是在变相的降低了团队里面的所有人的工作效率。这个“脏”东西,虽然好像无伤大雅,但实际上是有很大危害的。
  正确的做法是,和引擎项目没有直接关系的东西,在引擎项目外另外建立文件夹来存放,这样需要的时候可以单独检出,也不会影响引擎的加载。

六、任意添加插件

  在美术资源制作的过程中,需要一些第三方的插件,比较常见的有刷网格地形的工具、编写shader的工具、还有制作特效的工具等。
  在美术同事想用某个插件做特定的效果时,我感觉最好是先咨询TA或者程序同事。这里面有很多方面的原因:
1、在项目里面,同一种类型功能的效果,应该选用固定的技术手段去实现,比如地形,是使用自带地形,还是网格地形,如果是网格地形,使用某个第三方插件,还是自己写刷地形的工具,都是需要技术选型的,而不是随便美术同事去选择的。
  每一个第三方的插件,由于他已经是写成了通用工具,它的功能肯定是多样的,但对于某个项目的针对性其实不强。TA要考虑性能,程序要考虑打包资源时的策略,这些都需要有统一的技术标准。
  我个人是比较倾向于尽量不用第三方插件,而自己写工具去实现这些效果的。这样可以更贴近自己的项目需求,抛去没必要的实现步骤和用不上的数据,只保留对自己有用的东西。不过有时候也会遇到有些自己不会实现的功能,那样也只能用第三方工具,这时候就要评估好目标工具的各种优缺点,这些思考的方面,往往是纯美术的同事没有能力去评估的。

2、第三方的插件,其实也是别人写出来的程序,有bug很正常。如果对插件没有足够的了解,单纯只是会用,那么遇到问题的时候,是没有办法去解决的。

3、由于插件都是编辑器工具,有时候会写一些自己觉得方便的批处理,比如自动把某些资源的格式修改,自动创建一些文件等。这些工具类,就算我们平常不用这个工具,但在引擎加载的时候,也是会自动生效的。

  我就遇到过美术放了一个Unity的刷地形的第三方工具在项目里面。在每次打包AssetBundle或者加载场景的时候,都会自动设置它的笔刷图片的格式,严重的浪费时间,然后问了一下美术同事,他们说已经没有在用这个工具了,只是那样放在项目里面没删除而已。所以美术同事也不知道,他这种留着个没用的工具不删除,这种无伤大雅的操作,其实他已经浪费了我非常多的时间了。

七、乱用shader

  Shader里面产生“脏”东西,对于美术同事来说是最危险而且最不容易发现的一类东西。很多美术同事为了表现得自己水平比较高,都喜欢表现自己懂Shader。最常见是特效的同事,场景和角色模型同事也都经常会有这样的现象。
  Shader对于美术来说是非常重要的东西,比如在每一个特效里面,基本上都需要用到特殊的Shader来表现效果,比如叠加效果的,半透明效果的,序列帧播放的,扭曲空气效果的,UV偏移动画的,等等。可以说,如果没有了Shader的支持,特效同事就很难做出像样的Shader。再比如地编和角色模型的同事,什么PBR效果啦,二次元卡通角色效果啦,加上了之后,比单纯摆个网格模型上去的确是要好看很多。
  在面试特效、地编、角色模型这些岗位的时候,估计也没有一条规定是需要懂Shader编写的,所以美术同事们不懂Shader我觉得是很正常的。那么问题来了,既然美术同事都不懂Shader,那么他们的Shader又是从哪里来的呢?那么不外乎就是几个来源:
1、引擎自带的
2、网上下载的
3、以前的项目带过来的
  Shader在引擎编辑器里面是很难看出问题的。比如Unity引擎里面的Shader,一般来说在PC端的支持度是最高的,所以在编辑器里面看一般都不会有问题。但发布到不同的平台,比如安卓或者iOS平台,效果可能会完全不一样。
  然后,Shader的性能消耗,是大部分美术工作者都没有能力去评估的。Shader里面用了什么样的算法,只有懂的人才知道。包括了引擎自带的一些Shader效果,比如Unity自带的Standard材质,性能就非常不好。还有很多看着效果很绚丽的Shader,里面的计算也是非常复杂的。如果想实现相同的效果,很可能需要对某个Shader进行修改或者简化。
  从网上下载的Shader,或者从以前项目带过来的Shader,它的功能有时候并不是这么恰好和你现在的需求一样的,比如一个Shader里面提供了3 、4种运算,但你只是用到了其中一种。这个时候,你也能实现到你想要的效果,但另外几种你用不到的效果的性能,是白白消耗掉的。所以我一直不建议同一个shader里面实现很多种组合效果,而是比较推荐根据实际情况来写对应的Shader的。
  最后一个问题,同一个Shader有很多种变体。有些网上的Shader,或者用工具生成的Shader,里面写了很多宏,根据不同的情况,会进行不同的计算。在编辑器内,这些Shader都是跑得很正常的。但如果打包之后,引擎处于性能和容量的考虑,并不会把Shader里面的所有宏的分支情况打包,而只会打包当前用到的一些内容。所以打包之后,其实Shader根据不同的条件组合,是会生成很多不同的变体的。如果只打包了其中一部分变体,当用到另外一些条件分支时,这个Shader就不能正常的显示。如果把所有变体都打包,那么会产生很大的包体容量和内存占用。这也是我不建议同一个Shader里面写很多组合效果的原因,根据情况来写针对性的Shader,等于是自己在控制变体的生成,可控性更强。
  这个时候,就需要一个靠谱的TA同事,来统一的规划和管理Shader的编写了。

八、隐藏对象

  这是一个典型的鸵鸟心态问题。举一个例子,比如某个角色模型预设,一开始的需求是身上要带着一个粒子特效,后来策划同事说,那个特效不需要了,叫美术把它给删除了。然后美术同事,找到了粒子特效的挂点,把它给隐藏掉了。注意,他是隐藏掉了,并没有删掉。
  于是虽然这个粒子特效从那以后永远都不再出现了,但由于没有被删除,只是被隐藏,所以它也永远的被当做一个资源依赖,当需要加载这个角色的时候,这个粒子特效也会被加载,也会占用内存,只是没有显示出来而已。
  这种现象,在游戏场景、角色等方面都是经常遇到的。可能对于美术同事来说,所见即所得,只要我看不到的,它就是不存在的。但实际上,每一个对象它都是一个存储的对象,它由各种组件和数据组成,就算你看不到它,它也是客观存在的。
  如果怕以后这个粒子特效还需要用,可以单独把它保存成另一个预设,然后把它从角色身上彻底的删除。

九、资源丢失

  一般资源都是有依赖性的,比如一个角色模型,它依赖了一个网格模型,一个材质球,一个动画文件。然后材质球又依赖了几张贴图,诸如此类。
  在有些美术资源共用于多个模型的时候,我们有可能会因为想删除一个模型的资源,而错误的把共用的资源给删掉了,那么其他几个模型就会出现资源丢失的情况。
  所以前面说了,对于资源的管理,文件夹存放规则和命名规则都非常的重要,这样可以一定程度上知道某些资源的作用。然后,还需要配合的写一些资源检查的工具,来确定某些资源是否有在被使用,确定了没用之后,才能进行删除。这是一个很严谨的工作,不是自己觉得没有用了,就随手删掉的。

十、meta文件

  用过Unity引擎的人,应该都知道,项目工程里面每一个文件,都会对应有一个meta文件。但这个meta文件的作用,可能并不是每个人都知道。
  有些人的电脑里面,这个meta文件是默认被隐藏的。于是在提交项目的时候,比如提交SVN的时候,这些meta文件就会忘记提交了。
  meta文件的其中一个作用,是文件的身份证明,它里面有一个guid,可以理解成是资源在项目里面的唯一标识,差不多等于身份证的功能,如果你放一个资源到Unity项目里面,引擎发现这个资源没有guid,将会随机的生成一个给它,每次生成的都不一样。上面提到的一个材质球会依赖几张贴图,其实就是每张贴图都有一个guid,然后材质球记录了它需要哪几个guid的贴图。
如果你本地的资源meta文件不提交到SVN,比如刚才那几张贴图的meta不提交,那么别人更新SVN的时候,发现这几张贴图没有对应的meta文件,就会随机生成一个新的,分配一个新的guid。这个时候,由于你的材质球已经记录了你本地的贴图的guid,但别人的电脑上分配了和你不同的guid,那么别人电脑上的材质球就会找不到对应的贴图,表现就是贴图丢失了。
  上面说的是由于少了meta文件的“脏”东西,下面来说一个meta里面多了“脏”东西的情况。
  我曾经遇到这样的一个问题,我的项目都是根据自己定义的文件夹结构,去打包AssetBundle文件的。然后加载的策略也是按照这些文件夹路径去定制的。然而有一次,我发现打包出来的AssetBundle文件,出现了我完全没有见过的路径和命名方式,导致加载出问题。于是找了对应的文件,查询对应的SVN提交记录,发现是美术同事在上传资源的时候,上传的meta文件里面就带着一个奇怪的AssetBundleName。
  于是我询问美术同事,为什么要自己填一个AssetBundleName到资源里面。然后美术同事很无辜的说,他根本不知道那是什么,那个文件是他从别的项目里面拷贝过来的,别的项目都用得好好的,为什么只有我们项目有问题?这下我瞬间就懂了,美术同事把别的项目的资源AssetBundleName直接跟随这meta文件拷贝到我们的项目去用了。每个项目对于资源加载的策略不一样,这些没有什么对错或者通用性可言的,可惜美术同事不懂,连自己做错了什么都不知道。

最后

  说了不少内容,我认为这些情况,是在做游戏引擎美术资源的时候,很多人都会遇到的问题。我说的这些,都是我合作过的美术团队出现过的问题,有时候真的很无奈。但美术同事有他们自己的关注点,这些问题点,都是他们不会去关注,或者完全不懂的地方。如何能让美术同事更关注这些问题,让项目更顺利的开展下去,是一个值得思考的难题。

猜你喜欢

转载自blog.csdn.net/liweizhao/article/details/132747591
今日推荐