查看原文
其他

Enlighten预处理的过程为你全面揭秘!

2016-08-10 范松明 Gad-腾讯游戏开发者平台
全局光照是相当复杂的。它不仅需要处理从光源照射到物体表面然后直接被反射进入相机的直接光,还需要处理场景内经过多次反射的间接光。同时,由于这些在实时渲染时过于复杂,所以必须被分解成不同的渲染路径的元素,然后在最终渲染时合并。
全局光照由以下三个主要元素组成

1 直接照明一般输入光照和阴影。
2 间接照明的反射光用来填充整个场景(我们要关注的内容)。
3 反射、高光和其他视图依赖的元素,我们统称为镜面反射。

这些在着色器中合并在一起构成最终的场景
直接光照的技术很成熟地应用在当今的游戏引擎中使用的算法和硬件上。来自场景的光,反射在一个高光的表面上,像一面镜子或者有光泽的金属,(它)可以被屏幕空间反射或者环境反射贴图充当的反射光探针很好地模拟出来。
余下的就是间接光,即在进入相机之前在场景中多次被漫反射面反射的光。它既是渲染中最大的开销之一,也是Enlighten在实时渲染中要解决的问题。
全局照明问题
渲染公式

本篇博客不会进行深入的技术研讨,但是渲染方程可以给出我们想要的正确结果。所以在回到实例之前,我们简要的回顾一下。



这个吓人的方程是一般场景照明的正确方法,并且几乎是所有渲染技术的基础。该方程表示表面上某点光(或者称之为辐射更合适)等于该点发出的光与所有反射进该点的光的积分乘以材质的反射率之和(这是一个无限的和)。它几乎不可能被实时计算出来。

然而,我们可以像所有优秀的工程师那样开发解决方案。假设我们有一个有限集合的元素和漫反射传输,事情就变得简单多了。
光能传递公式




1 Bi表示来自点i的光
2 Le表示直接从簇群i发出的光
3 ρi表示材质的属性
4 n 表示一个数量
5 Fij是形式参数(即i可以看到j的多少)
6 Lj表示来自簇群j的光(上一帧)

这个公式描述了一个独立的簇群(光照)的解决方案。我们试着去计算辐射度(Bi)。它等于该簇群所能看到的所有簇群的光能总和乘以它们的importance,(重要性),或者对场景的contribution(作用),它被理解为一种形式参数(Fij) 。换而言之,它(i)的广角越大,场景对它的影响越大。
n表示形式参数的数量,是用来控制在质量和效率上的权衡。把所有的这些光(来自簇群j的光)集合起来,我们可能发现它是一束很强的红色光,但是我们会给它乘以材质参数ρi。最后,我们简单地把直接从簇群i发出的光Le加在结果上,这样就完成了一个簇群(i)的光照计算。然后我们给每个簇群重复上面的方法,算出每个簇群的光照。这个过程完全不影响帧率,因为你可以在后台线程上运行它,可以分散负载,或者让不同的场景区域以不同的频度计算光照。
译者注:这部分原文解释起来有点啰嗦且不好理解,通俗一点的解释是:从一个表面i辐射出来的光(辐射度)Bi 等于该表面本身发射出的光 Le 加上从其他所有表面发射到该表面且被该表面反射的光,其中Lj表示来自簇群j的光,Fij表示表面j相对于表面i的形式参数,(ρi)表示表面i的材质属性,一般指反射率 。
这里是译者关于这部分的参考文章:案例分析:Radiosity 光能传递渲染器的多核优化 
预计算
那么Enlighten是如何工作的呢?Enlighten通过预计算场景中静态模型的可见度(即形式参数),然后就可以实时修改照明,即使在手机平台上。光源,环境光和材质属性(漫反射和表面反射)都可以在运行时动态修改。 和传统全局光照解决方案相似的是,Enlighten生成三种类型的输出:

1 光照贴图被生成为2D纹理
2 光照探针(light probe)被生成为球谐函数
3 反射接收器(Reflection captures)被生成为立方体贴图(cube maps)
 我们在之前的博客中已经详细描述了这些输出类型,让我们通过下面这个简单的例子,来探索Enlighten如何实现预计算的细节。
预处理阶段自动将场景分解成很多部分(或之前提到的簇群)。在这个场景中,有一堵墙,一个地板和一个立方体。如图4,我们分别将墙和地板分成4部分并将它们叠在场景中。
对每个单独的像素,Enlighten将光线投射到场景中并计算出那些簇群会影响到它或它的可见度。成千上万的射线从每一个独立的像素发出,来计算出哪些簇群首先被击中和光线到达簇群的密度。所有击中某个簇群的射线的总和的权重是该簇群对当前像素的形式参数。
在这个例子中,像素“X”将会被立方体的整个面(D)和墙的A,B,C的一部分影响。D仅仅遮住了这一点的一部分,所以这里我们给它的形式参数一个0.2的权重。尽管A,B,C可以被这个像素看到,但是它们的一部分被立方体遮住了,因此我们根据最终可见的表面积,分别给它们的形式参数取了一个更小的值,它们分别是0.05,0.1,0.05。
假设有一个明亮的聚光灯直接照在这个立方体红色的正面(D),唯一的光反射将会从这个面发出(也是红色的),因为没有光线照到墙上,所以没有来自墙的反射,我们这里用黑色来表示(而不是墙真正的颜色)。在运行时,Enlighten只需要概括出每个簇群储存的颜色值,并将其与相应的形式参数相乘。X的值就等于0.2乘以这个光值/辐射,结果是一个暗红的色调。
在另一个案例中,我们将立方体和墙的颜色改为胭脂红。如果我们扩大聚光灯的光圈,但不改变其发射到场景的光照强度,然后所有簇群(A,B,C和D)都会发生反弹,从而对像素X的颜色造成影响。X的值就等于0.4乘以这个光值/辐射,结果像之前一样是一个较深的红色色调。
反之,如果我们用三个不同的聚光灯照色墙的不同区域(簇群A,B和C),不照射立方体黑色的表面(D)。由于光在墙上反射,而立方体上没反射,那么像素X应该是灰色。然而,墙的绿色部分(簇群B)因为在X上有更大的可见范围,所以对x有更多的影响(因此有更高的形式参数0.1),同时它会反映出轻微的绿色色调的像素。
结论
在本质上,Enlighten预计算场景中静止的几何体,然后用这些数据模拟运行时的全局光照。在这个阶段上,Enlighten自动将场景分解成簇群,然后去计算它们之间的关系(形式参数)。如上面的例子所示,一旦我们知道了形式参数,我们只需要知道每个簇群的光照就可以计算一个像素的颜色,这就可以让我们实现实时的动态全局光照了。在运行时,间接光的传递量同样可以被控制。这就使得艺术家可以通过场景中间接光的反弹褪色来实现打开门和销毁一堵墙的效果并实时更新(它们)。

【版权声明】原文作者未做权利声明,视为共享知识产权进入公共领域,自动获得授权

近期热文

Unity资源商店里的免费资源,你一定要知道!

Glassdoor揭晓:在Valve、暴雪和Rockstar的开发者薪资是多少?



腾讯游戏开发者平台长按,识别二维码,加关注
经验分享丨项目实践项目孵化丨渠道发行做有梦想的游戏人

-GAME AND DREAM-


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存