(本是2023年9月12日的日记,由于投稿了我再进一步细说)
前情回顾
先来回顾一下游戏项目的开发进度(已知的可以略过):
目前已经部署了包括伯耐利M4和AA-12等自动霰弹枪,其射速非常快。选用了爱发电上的banner图展示一下AA-12:
按照常识上讲,照明枪的照明弹应该是14g左右口径,但是由于游戏设计,我们选择跟攻击性霰弹枪一起,使用12g口径(目的是提升霰弹整体枪种的应用范围和适用性)。
然后一顿操作猛如虎之后下来,遇到制作照明弹效果,马上就遇到了巨大的问题....
0.问题的出现
照明弹光照存在以下特点:
· 相对于主光照低优先级但必不可少
· 同屏存在数量可能很巨大
· 照射范围略大甚至巨大
· 需要阴影或部分需要阴影
· 与粒子系统伴随出现
其中第二、三点最为致命。
毕竟按正常道理,正常玩家只需要射1~2发照明弹提供场地照明即可。但是本着程序设计人员必须把用户想成猴子的经典理论。总免不了有特殊癖好玩家直接对着天空射烟花。
.....然后你总不能告诉玩家说,你5秒内只能射一次....吧?(板砖警告)
但是满足玩家这个癖好,则会发生以下交通事故:
完全无优化的效果,FPS直降到30左右,才...9发照明弹
本着多想想就会头秃,再想多几天就会葛屁的觉悟,赶紧问了一下小伙伴们:
然后猛然惊喜发现:这好像是个业界天花板技术问题~(我去...)
结果很明显:似乎大范围大剂量点光源就没在多少个A级游戏里面被开放式的允许过。果然。这就是天花板的感觉....
但是项目已经做了...
照明弹通用12g霰弹的口径也已经定了...
双持AA-12美妙的十几发秒射速连喷摆在那里...
噗...只能霸王被弓硬上。
以下是主要技术节点的结果与结论的演示。
1.光的合并
我的游戏中,爆炸对小型植物会产生烧炙火焰。由于小型火焰也带无阴影光照,所以也属于额外非重要点光源,可以跟照明弹问题并列处理使用同一个优化脚本。
首先我们看看单纯多个无阴影光照的开销
如果我们把多个无阴影光照合并之后会发生
明显经脚本优化后FPS明显有优势,而优化脚本的开销可以忽略。
也就是说,就算是我们不使用阴影,两位数以上光斑对游戏性能开销也是非常可观。(哥,我这是Deferred,如果你是Forward,我送你个刀自行了断吧xD)
不过问题还很多,因为当灭失时,光斑会产生抽搐感或者强烈抖动,于是我们取所有合并光的平均位置。
但是由于光到耗尽时会有渐变暗和灭失,所以还是存在小抖动和不自然。
这里面就涉及一丁点开销问题。我们按两个光之间的强弱比例使用Lerp线性插值求得整体加权平均位置
效果非常爽滑:
2.阴影控制
由于阴影开销巨大,下意识程序员们都会把它们关了。但是这就产生了一个尴尬问题。
比如说,你在房间外面射了一颗照明弹,房间外面是亮的,房间里面,是暗的(正常)
然后来了一另个玩家(猴子),给你射了十几发过来,结果由于阴影限制,后面的无阴影光照直接就的把房间里给照亮了...
所以再三思索下,我们限制在距离镜头最近的前n个光照允许阴影。
不过如果我们直接把镜头位置跟点光源中心位置比较,似乎会产生不合理现象。毕竟光源是有一个范围的,他的阴影产生也会有一个范围,但是我们的锥形视界,明显靠前。所以具体算法参见下图:
我们为镜头选择一个check point。位置大概镜头正前方0.3~0.5光照range的位置。因为按照unity里光照的实际操作经验和现实世界的光理论,光在超过一半range之后其光强衰减起码是三次方关系,也就是1/8,12.5%。所以我们默认在这个范围外的阴影及其强度不予优先考虑。所以与镜头距离的判断基点是check point,而非camera.position。
另外,基于前面第1节我们已经把光进行合并,所以我们只需要在合并后活跃的光照上进行这项判断
最终效果和FPS,15+发照明弹
最终效果的配置参数:
·允许n=2个阴影光照
·光射程10%范围内合并
事实上,我把燃烧时间改到50s,直接对着天空把一盒照明弹50梭子全怼了出去,fps也是差不多这样50上下纹丝不动。
所以秃头了这么多天,感觉还是值得的。我先去植一下发(笑)
3.其他可能存在的问题
1.当然,这种优化逻辑在比较窄或者复杂的地形或建筑内还是绝对有穿帮的可能,但是在那种环境下使用照明弹基本我们默认是猴子,可以拉出去直接击毙。
2.另外,关于光合并,会涉及一个合并后光强度的取值问题,由于我不想翻阅字典厚度般的unity光照内核资料,加上我到现在还用着5.6,我研究半天到了新版变了岂不是白给了。所以直接肉测了几个场景,取以下代码逻辑:
combineIntensity = intensityMax + (intensitySum-intensityMax>1f? Mathf.Sqrt(intensitySum-intensityMax) : intensitySum-intensityMax);
注释:intensityMax=合并群组里的最大光强;intensitySum=光强总和
暂无关于此日志的评论。