本篇文章是比较宏观范围的技术思路,而非很细节的算法。有些东西讲的不一定正确,是我尽最大可能理解的这些知识点,欢迎指正或者讨论。
B站的这部视频是本篇文章最大的灵感来源。
游戏为什么需要这个技术方向
十年之前,不需要很逼真,只需要达到游戏性和“让马里奥可爱”就行。
近十年这段期间越来越注重游戏的真实场景打造。现在,要更加真实,几何细节需要更加丰富。但真实渲染已经触及到了恐怖谷效应。什么时候能跳出这个恐怖谷,还需要时间。
怎么样让游戏画面变得更加的真实,不仅是模型、人物动作等表现,光的科学是重中之重。
(游戏画面好不好看,取决于场景是不是足够亮)
光的渲染启蒙-渲染方程
对于一个平面,反射光和入射光的关系是下面这一个公式:
BRDF看作是各种材质的量化统一,比如Lambert光照模型和Phong光照模型。目前流行的PBR模型就是基于物理的模型,会使得计算出来的效果非常写实。
那么BRDF可以由我们来决定,入射光又该怎么确定呢?都知道空间内一个点,都会被半球空间内的所有方向的入射光打过来,而且随便跟踪一个入射光到它的上一个反弹点,依然会面临同样的半球面无数个入射光的问题。
(右侧为半球面的入射光积分)。
考虑到有会自发光的物体,于是加了一项,得出渲染方程:
问题就是对于这个方程怎么求。
光线追踪
用离散方式对渲染方程做处理。
先不去考虑间接光的无穷项,只想着渲染出来哪怕一次间接光,该怎么处理?光线追踪。
从摄像机每个像素点射出多根不同方向的射线并跟踪它们的运动、放射轨迹,直到它们到达光源或者耗费完弹射次数(注意肯定不是无穷次反弹),于是就可以得到这个像素点对应的光线数据汇总。
为什么不从光源每个方向出发,而要从摄像机的每个像素点出发?
既然知道了原理,就可以渲染出近乎光照真实的图像。
问题在于什么?
空间换时间-距离场
光线追踪技术难的点之一在于,如果高效迅速地求出一条射线和哪个平面相交。
传统方法就是,求这个射线和整个场景里的所有平面是否有交点,同时,求出最近的那个交点。
所有平面,所有交点,每条射线,这些参数合在一起终究还是一个大的离谱的数字。
怎么优化?动态规划。
如今有一个距离场的概念,就是以某个特定距离作为单位,记录空间中所有的点到距离它周围最近平面的距离。
每个点都存有一个计数值即离最近的平面的步进,当光线运动到其中一个点时,根据这个点的计数来决定下次它应该走多少步进。
当然,可能会遇到光线和某个平面一直平行的情况,所以需要设置步数的上限;同时,也可能会遇到最终收敛到非常小的值的情况,也可以设置一个最小步进值,当小于等于这个最小步进值的时候等同于相交即可。
无限次反弹?表面缓存+微分迭代
无穷项。光线有无数次反弹。
按照上面的公式,每个点的入射光能量可以是直接光照加上多次间接光照。边际效应在这个公式里尤为明显,每多一次间接光反弹,它的优化力度会相较上一次少一些。第一次的间接光对场景的优化尤为明显。
那我们能不能就只渲染一次间接反射就行?可以,老显卡的ray tracing功能就只是提供一次间接反弹。但是优化嘛,肯定要做得更加精细。解决方案就是:复用/迭代。
我们先给场景里的每个面元设置一个表面缓存(光辉缓存),在第0帧,首先计算一次它被直接光和其他面元第一次间接光影响所得的受光信息。
第1帧,我们此时有了场景内所有面元受到其他面元第一次间接光的信息,通过这些缓存输出,作为这一帧的光源输入,获得受到第二次间接光的信息。第2、3帧继续如此,就可以无限重复,在仅需1s的时间,就可以计算出30次间接光照所得的画面光照表现。
思考一下,这样没有其他问题吗?如果画面里的物体会动,会不会造成一些影响?光源会动,又该怎么办?这里更多的是一种无穷项的解题思路,绝大部分情况下的物体运动是很缓慢并且是不影响场景中绝大部分其他面元的光照信息的。当然针对这个方面的问题有其他优化方案可做。
Lumen
lumen用到了上述这些优化方案,但远不止这些。剩下还有很多算法思路和优化技巧,像shadow map这种。当然还会根据离摄像机是否接近来调整优化方案。