小历史
今年(2016)siggraph上面,treyarch介绍了其类irradiance volume的全局光照技术。
不禁让我有点小感叹,这个技术还真是经久不衰,刚好是整整十年前的siggraph06上面,value介绍了其用于half-life2的source engine中的irradiance vlume。
(来!偶像出马!)
巧合的是,两个引擎source engine和cod系列的InfinityWard Engine(用于多个studio,infinity ward, sledge hammer, treyarch,或许后面有各自重写了吧)都脱身于大神卡马克的id系的GameEngine。
这个blog上面前前后后也记录了很多了,half life2, killzone, farcry3, battle field, 大量的早期cg。。。
这项技术的核心理念一直传承,但是实现细节到pipeline则一直变化。
干嘛的?
先进的游戏中的全局光照主要这三个:
- lightmap(UnityEngine, UnrealEngine, bungie各游戏,naughty dog各游戏。。。)
- irradiance volume(cod,farcry3(变种)等)
- light propagation volume(CryEngine, UnrealEngine, JustCause等)
- voxel con tracing(昙花一现,秀了一把之后回炉继续修炼去了)
Irradiance Volume是一个尤其自身优势的全局光照技术。
具体做法
它是一种离散的描述空间中光照信息的方案。
这是irradiance volume的一个debug visualization视图,其中大家可以看到,场景中以一定间距,放置了很多小盒子,这些小盒子仔细看的话,有明有暗,记录其所在位置的光照信息。
这些光照信息可以用来照亮场景,可以是静态的物件也可以是角色,进而带来很不错的全局光照(间接光照)的效果。
到今天的irradiance volume系的技术(包括大量变种),相对于我们大家熟知lightmap,可以有这样的优势:
- 可以非常快速的cook出来,在编辑器中达到近乎实时的速度
- PrecomputedRadianceTransfer版本,可以支持光照环境的变化(time of day等等)
- 支持角色的间接光照,当角色走进一面红色的墙壁的时候,身上可以泛出性感的微微红色的光
其核心思路包括:
- 空间分割–把空间分成规则的有限份,好以少量的数据表达出世界的样子
- 光照信息记录–记录每个空间分割中光照是什么样子的
- relighting–向场景中打光
空间分割
从早期的BSP到现在的voxel,空间分割在3D游戏中可以说是随处可见的东东了.
irradiance volume一般就是以一定的大小(比如2m的立方体)来把场景分割开,每一个小立方体中去记录光照信息(一般我们管这个收集光照信息的叫probe)。
这部分一般都在editor里去做,一般我们会探讨两个问题:
- 分割的太细,probe太多,烘培速度太慢怎么办
- 分割的不够细,产生漏光怎么办(一个cube穿墙了,导致另外一个暗屋子被照亮了)
两个做不好的话,就会导致实用性大幅度下降。
解决办法:
- editor会自动的,尽可能少的,在需要的地方,放置probe
- —比如farcry3是使用以一定间隔的ray向下trace,有静态物件就在这个表面生成probe
- 提供人为干预机制
- —美术可以自行添加删除和定制probe的产生方式,漏光了?那就使用不漏光的volume形状或者删掉相关的probe
- —-比如cod中,就提供了花样繁多的形状定制
光照信息收集
这里的光照信息收集,一般会探讨这几个问题:
- 收集什么样的光照信息
- 如何去存储,尤其是考虑到硬件的情况
- 信息收集的速度
光照信息
Irradiance
irradiance volume,那么这个光照信息当然就是irradiance了,具体的名词解释可以在
娜姐(Natalya Tatarchuk)的Irradiance Volumes for Games
细看,公式代码都全。
实际中cod的做法比较有代表性,就是在一个probe这里,做raytracing,收集光线的强度。
Radiance Transfer
另外一种方式是不是记录直接的光照结果,而是记录radiance transfer, 这是farcry3在几年前的做法,个人非常的喜欢,觉得比今年的cod的要更好。
radiance transfer可以理解为非常接近光照结果的东东了,只要加上周围光照,只需要很少的计算就能得到最后的光照结果,但是它有一个很大的优势,就可以可以支持周围光源的变化,也就是意味着可以支持time of day。
如何去存储
正常来讲,这里存储是指存用于照亮场景的irraidance信息了(即便是radiance transfer版本,也是要先算出irradiance,然后再用于relighting)
- rgb颜色(6个方向的ambient颜色,一般叫ambient cube)或者Spherical Harmonics系数,两者起的作用差不多。放到3d texture中,利用linear filter去高效的插值
- 但是实际游戏中(black rock, treyarch…),ambient cube使用的更加广泛些,black rock曾经做过这个比较,ambient cube介于1阶sh和2阶sh之间,表达力上并不弱,同时却更直接。
信息收集的速度
其实就是bake的速度,这里直接决定了pipeline的效率,lightmap最让人头痛的地方也是这个迭代效率,美术改一个参数,2个小时后才能看到效果。
这里一种方法是每个probe哪里去傻算,直接capture cube map,然后downsample,或者raytracing都可以,不过这个都会导致太慢。
cod这里就比较好了。
它是借用了reproject cube的概念,这本来是做反射时候常用的一个做法,就是在一个场景里capture一个cubemap同时带着光照和深度,然后反射的时候,根据深度来取最准确的颜色,这样保证parallax correct。
这样可以想象,两套连击:
- 更少的probe,少量的reproject cube就可以记录一个场景中大致的光照信息,然后应用于大量的irradiance volume的probe,
- 可cache信息:且其中一些tracing的位置信息可以cache
最后在好的电脑上,可以达到几秒钟就cook完(对比lightmap的几小时。。。)
向场景中打光
这里有两步:
- 如果有radiance transfer在,就要先根据光照环境生成irradiance,否则的话就是针对离线cook好的信息直接使用
- relighting:
- deferred renderer,就直接把光照信息放在3d texture中,根据G-Buffer来直接lighting就好
- forward renderer,一般是早期的做法,现在则是手游中可以考虑,就是在cpu端把对应物件周围的irradiance信息抽取出来,针对这个物体(比如一个玩家),作为一个ambient lighting,以几个shader constant的方式传到shader中去计算
reference