今天和大家分享一下在工作之中的一些感悟。本人于一年前从一名学生成为了一名引擎开发工作师。通过身份的转变也发现了一些处事的不同之处,在工作之前,无论是学习还是做学术研究往往只注重于局部而忽略了全局。然而局部最优并不等于全局最优。接下来通过一个案例的分享来阐述。
图1《泰坦陨落2》中的幻影模拟效果
为了实现美术对于图1中出现的人物角色的类似全息扫描的效果,对实现的方法进行了研究。其实这个效果的本质为平行的扫描线,不过这个扫描线之间的距离并不是均匀分布由角色上的。想要使其不均匀那就需要应用随机数,下面针对随机数的生成进行讨论。
我们在进行效果编写的时候,在Shader之中往往需要随机数,特别是需要实现类似信号传输不稳定效果的情况。实现这个目前特别简单粗暴的方法有两个:一是应用脚本生成随机数,二是应用噪声纹理来实现。但使用脚本会有性能问题,要尽量使用GPU直接生成随机数,而噪声纹理又会占用额外的纹理通道,该角色shader中纹理数量已经较多,而且项目显存占用也急需优化,所以在实现这个效果时候要尽量避免使用这两种方法。
图2 Shader实现随机数的两种方式
具体实现方法,目前冯女神和网络上其它地方已经有很多的分享,在这里我提出一种很粗略的实现方法。我们知道,时间贯穿着我们的世界,所有事物都有频率,在这样的思维之下,我们是不是可以通过对于频率域的一些定义而实现一些空间域上的效果。主要应用傅里叶级数来实现,可以通过多种级数的混合来实现一种近似的随机数(其实是伪随机中的伪随机)。以两种级数为例,如图3所示。
图3 两种傅里叶级数[1][2]
以锯齿波的结果作为自变量代入方波之中,最终就会得到随机的结果。不过在实际应用时,不用一定要按照正常的条条框框来进行,有的时候,出现BUG也可能会有意想不到的效果。这种随机方案在实现具体某个物体上的效果时,可以在整个物体上,也就是全局上发挥作用,随机的效果可以尽可能的避免突变的情况。具体如下:
在Shader之中可以这样写。通过这个过程计算出来的数,就是一种近似的随机数。其中的取值范围可以根据需要自行制定。
half SawWave = (2 / PI) * (-sin(_Time.y) + sin(2 * _Time.y) / 2 - sin(3 * _Time.y) / 3 + sin(4 * _Time.y) / 4);
half CubeWaveAns = (4 / PI) * (sin(SawWave) + sin(3 * SawWave) / 3 + sin(5 * SawWave) / 5 + sin(7 * SawWave) / 7);
下面对于上述三种方案进行性能分析。首先是控制变量,仅将全息效果中随机数生成部分进行替换。在渲染分辨率相同的情况下均提取6000帧的帧率。实验结果如图4所示:
图4 三种随机数生成方案的帧率比较
其中红色、黄色和蓝色的线分别代表应用脚本传递数据、应用噪声图和应用傅里叶级数的方法的渲染效率。可以发现,傅里叶级数的方法的帧率优于脚本传递的方法,劣于应用噪声图的方法,但可以减少纹理通道的占用和避免显存的开销,算是在时间与空间之间找到了一个平衡。
最后,对于全局状态的认知方面,在对新功能开发或对功能进行维护的时候,首先要厘清的问题是,用比较通俗的话来说就是“有啥,没啥,啥是啥,啥能干啥,啥和啥有啥关系,啥在啥时因为啥变成啥样,啥在啥时因为啥把啥变成啥样”。经过了这样的分析才会对于要做的事有清楚的认知。这也是在求学阶段老师对我的教导。本文旨在分享工作以来对于事物认知态度的变化,以及一点点技术的分享,仅供参考。
参考文献:
[1]方波傅里叶级数. https://en.wikipedia.org/wiki/File:Fourier_series_square_wave_circles_animation.gif
[2]锯齿波傅里叶级数.https://en.wikipedia.org/wiki/File:Fourier_series_sawtooth_wave_circles_animation.gif
文:Don
欢迎加入我们!
感兴趣的同学可以在官网投递简历:
内推码:NTAI1kh
引擎校招:搜狐畅游 - 校园招聘
引擎实习:搜狐畅游 - 校园招聘
TA校招:搜狐畅游 - 校园招聘
TA实习:搜狐畅游 - 校园招聘