技术面
技术面基本上就是对着我的项目经历一条条问的。
1. Lua 配置表优化
项目前期使用的是哈希表形式存储的表格数据,导致内存占用较大。优化的思路就是将数据以数组的形式存储,然后重写 index 元方法将传入 key 转换为索引来访问数据。具体可以看我的笔记:
2. 人物渲染
人物采用的是 Unity URP 的 PBR 渲染,根据项目需求做了一些参数和效果的扩展。头发部分采用的是双层 Kajiya-Kay 高光。皮肤部分会预先烘焙一张曲率贴图(主要是脸部),然后用曲率和 NdotL 采样预积分 LUT 得到皮肤颜色并叠加在 PBR 的漫反射色上。衣服部分主要是丝绸会特殊一点,需要用各向异性计算两层高光,一层横向一层纵向,类似于天刀那样的效果。
3. PBR 的理解
这个就没什么好说的了,各类文章和教程都有详细介绍 PBR 的内容。
4. 雨天潮湿效果
雨天潮湿主要是涉及到光滑度、法线强度、漫反射这三个部分。由于材质表面有雨水附着,因此适当提高光滑度、降低法线强度、降低漫反射就能够实现出湿润效果。如果想更进一步实现出积水效果,那么可以额外添加一张高度图(或者直接用粗糙度贴图代替,将有水坑的地方涂黑),并结合一个全局的潮湿度参数(区间最好是 0-1)来控制积水程度。具体的实现细节可以参考这篇文章:
5. 草地渲染
项目中用到的渲染方案是 GPU Instance,利用DrawMeshInstanced和 CullingGroup 做实例化草地的渲染以及剔除。之前我有尝试过 Indirect Draw,也就是用DrawMeshInstancedIndirect来绘制草地,在 Compute Shader 中执行剔除算法。不过由于我们的场景并不是很大,所以最终还是没采用这种做法。
由于是中小型场景,所以草地的位置直接是用自己写的草刷工具刷出来的,并没有用密度来实时生成草的位置。其实关于草地的渲染还有很多能讲的,比如草地区块的粗粒度划分、利用四叉树动态加载周围的草数据、Over Draw 问题等等,不过面试的时候只讲了这么多。
6. URP
URP 部分主要是问了一些扩展相关的问题,比如有没有修改 IBL 部分(因为 Unity 的 PBR 公式是拟合优化过的,不是像 UE4 那样去采样一张预积分的 LUT)。Shadowmask 的问题还着重问了我一下,但事实上 URP 10 已经官方支持了 Shadowmask,就算是用的低版本也只需要编写少量代码就能支持 Shadowmask。
7. GPU 粒子优化
粒子的优化其实是参考了 UWA 2020 上战双帕弥什的分享,主要的优化思路就是将粒子运动轨迹按照时间帧进行切分,将每一帧中所有的粒子数据保存成 Mesh 数据并用 Shader 进行还原。用 Mesh 存储数据的优点如下:
- Mesh 支持 Bounding Box,可以做视锥剔除。
- 可以直接挂在 Mesh Renderer 组件上进行渲染,播放一段粒子效果只需要按照顺序切换对应的 Mesh 即可。
- 这种做法对于低端设备的支持较好。
- 已经运动完的粒子并不会被烘焙到 Mesh 中。
- 渲染效率很高,且支持 SRP Batcher,相同的粒子数据只占一份。
之前和组内人员讨论时,他们觉得这种做法不如实时粒子模拟,理由是粒子数一旦提高就会占用大量内存。我个人觉得有许多优化本质上都是空间和时间的互换,具体采用哪种做法取决于游戏实际的需求。对于战双这种游戏类型来说,它需要在维持高帧率、适配低端机型的情况下渲染大量类似于火花的粒子,那么这套方案无疑就是更好的选择。如果游戏需要那种大型的粒子特效(比如粒子传送门),那么自然就可以考虑用 Compute Shader 或者多张 RT 做实时模拟。至于像是雨天这种重复度很高的粒子效果完全可以只烘焙一段 1s 的粒子轨迹,然后在场景中重复地进行摆放即可在保证效果的前提下完成高效渲染。总的来说,对于一套方案好不好需要结合实际应用场景来评价,而不是单方面做一些测试就下结论说不好用。
当然,这种做法也是会有透明排序问题的,只不过战双做的粒子效果都是类似于火花这种重复度很高的,基本上看不出来排序问题。
8. Unity 的 GC
Unity 的 GC 可以看看这个分享,讲的很详细:
9. C# 基础
C# 只问了几个很基本的问题:
- class 和 struct 区别
- Dictionary 的实现
- 虚函数实现
HR面
问了一些基本情况,比如为什么要离职之类的。
说起来我最开始投的是客户端岗位,但是可能是因为我简历上写了很多渲染有关的东西,因此面试的时候基本没怎么问客户端的内容,最后也是给的技术美术岗(我本来也是想做TA岗XD)。
#面经##社招##散爆网络#