-
2019-04-17 22:46:29
参考博文【《Real-Time Rendering 3rd》 提炼总结】(八) 第九章 · 全局光照:光线追踪、路径追踪与GI技术进化编年史
前言
本章知识概览:
- 全局光照的基本概念
- 全局光照的算法主要流派
- 全局光照技术进化编年史
- 光线追踪 Ray Tracing
- 路径追踪 Path Tracing
- 光线追踪、路径追踪、光线投射的区别
- 环境光遮蔽 Ambient Occlusion
一、全局光照
全局光照(Global Illumination,简称 GI), 或被称为间接光照(Indirect Illumination)。使用全局光照能够有效地增强场景的真实感。
全 局 光 照 = 直 接 光 照 ( D i r e c t L i g h t ) + 间 接 光 照 ( I n d i r e c t L i g h t ) 全局光照 = 直接光照(Direct Light) + 间接光照(Indirect Light) 全局光照=直接光照(DirectLight)+间接光照(IndirectLight)尽管实际应用中只有漫反射全局照明的模拟算法被称为全局照明算法,但理论上说由于反射、折射、阴影的模拟都需要考虑光源对物体的直接作用和物体之间的相互作用,因此也属于全局光照的范畴。
二、全局光照的主要算法流派
主要流派:
- Ray tracing 光线追踪
- Path tracing 路径追踪
- Photon mapping 光子映射
- Point Based Global Illumination 基于点的全局光照
- Radiosity 辐射度
- Metropolis light transport 梅特波利斯光照传输
- Spherical harmonic lighting 球谐光照
- Ambient occlusion 环境光遮蔽
- Voxel-based Global Illumination 基于体素的全局光照
- Light Propagation Volumes Global Illumination
- Deferred Radiance Transfer Global Illumination
- Deep G-Buffer based Global Illumination
其中,光线追踪又分为递归式光线追踪、分布式光线追踪和蒙特卡洛光线追踪等;路径追踪又分为蒙特卡洛路径追踪、双向路径追踪、能量再分配路径追踪等。
三、全局光照技术进化编年史
3.1 光线投射 Ray Casting
从每个像素射出一条射线(每像素从眼睛投射射线到场景中),找到挡住射线路径的最近物体,视平面上每个像素的颜色取决于可见光表面产生的亮度。
3.2 光线追踪 Ray Tracing
在光线投射基础上加入光与物体表面的交互,让光线在物体表面沿着反射、折射以及散射方式继续传播,直到与光源相交。也称为经典光线追踪方法、递归式光线追踪方法或者Whitted-style光线追踪方法。
每像素从眼睛投射射线到场景中,找到与该光线相交的最近物体的交点。(1)若该点处的表面是散射面,计算光源直接照射该点产生的颜色;(2)若该点处的表面是镜面或折射面,则继续向反射或折射方向追踪另一条光线。如此递归,知道光线射出场景或到达预设的最大递归深度。
3.3 分布式光线追踪 Distributed Ray Tracing
将蒙特卡洛方法引入光线追踪领域,将经典光线追踪算法扩展为分布式光线追踪算法,也称为随机光线追踪算法,可以模拟金属光泽、软阴影、景深和运动模糊等效果。
【补充】蒙特卡洛方法,参照蒙特卡洛光线追踪
3.3.1 反向光线追踪
传统正向光线追踪的基本思想:从光源出发,正向追踪每一条光线在场景中的传递过程。正向追踪计算了大量对当前屏幕颜色不产生贡献的信息,极大影响了效率。因此引入反向光线追踪。
反向光线追踪的基本思想:从相机(而不是光源)出发,向投影屏幕发出光线,然后追踪该光线的传递过程。若该光线经过若干次反射、折射打到光源上,则认为该光线是有用的并递归计算其颜色,否则丢弃该光线。实际上,该过程是真实光线投影的逆过程,同样也会产生浪费,并且只适用于静态渲染。3.3.2 蒙特卡洛光线追踪
理论基础:
传统的逆向光线追踪算法表面属性的单一且不考虑漫反射。改进:认为一个表面的属性可以是混合的,有20%的概率发生反射,30%的概率发生折射,50%的概率发生漫反射。然后通过多次计算光线跟踪,每次按照概率决定光线的反射属性,这样在就把漫反射也考虑进去了。
逆向光线追踪的改进方法:本质就是通过大量的随机采样来模拟半球积分。这种方法在光照细节上可以产生真实度很高的图像,但是图像质量有比较严重的走样,而且效率极其低下。蒙特卡罗光线追踪本身也是一种逆向光线跟踪。其本质是通过概率理论,把半球积分方程进行近似简化,使之可以通过少量相对重要的采样来模拟积分。相对于普通光线追踪,蒙特卡罗光线追踪引入了更复杂的漫反射模型,从而增加了需要跟踪的光线数量。但是又通过采样算法减少了需要跟踪光线,所以其核心效率取决于采样模型。
3.4 渲染方程 The Rendering Equation
渲染方程描述了场景中光能传输达到稳定状态以后,物体表面某个点在某个方向上的辐射率(Radiance)与入射辐射亮度等的关系。渲染方程根据光的物理学原理,以及能量守恒定律,完美地描述了光能在场景中的传播。很多真实感渲染技术都是对它的一个近似。
3.5 路径追踪 Path Tracing
路径追踪方法是第一个无偏(Unbiased)的渲染方法。路径追踪的基本思想是从视点发出一条光线,光线与物体表面相交时根据表面的材质属性继续采样一个方向,发出另一条光线,如此迭代,直到光线打到光源上(或逃逸出场景),然后用蒙特卡洛的方法,计算其贡献,作为像素的颜色值。
路径追踪会避开贡献小的路径,而在贡献大的路径附近做更多局部的探索。3.6 双向路径追踪 Bidirectional Path Tracing
双向路径追踪(Bidirectional Path Tracing)的基本思想是同时从视点、光源打出射线,经过若干次反弹后,将视点子路径( eye path) 和光源子路径( light path) 上的顶点连接起来(连接时需要测试可见性),以快速生成很多路径。这种方法能够产生一些传统路径追踪难以采样到的光路,所以能够很有效地降低噪声。
3.7 梅特波利斯光照传输 Metropolis Light Transport
与双向路径追踪相比, MLT方法 更加鲁棒,能处理各种复杂的场景。比如说整个场景只通过门缝透进来的间接光照亮,此时传统的路径追踪方法因为难以采样到透过门缝的这样的特殊路径而产生非常大的噪声。
四、光线追踪 Ray Tracing
光线追踪跟踪从眼睛发出的光线而不是光源发出的光线,所得结果类似于光线投射与扫描线渲染方法的结果,但是具有更好的光学效果。
光线追踪的最大缺点是性能问题,独立看待每条光线,每次均需要重新计算,计算量非常巨大。传统光线追踪产生的并不一定是真实效果图,只有在非常近似或者完全实现渲染方程的时候才能实现真正的真实效果图像。
伪代码:for each pixel of the screen { Final color = 0; Ray = { starting point, direction }; Repeat { for each object in the scene { determine closest ray object/intersection; } if intersection exists { for each light in the scene { if the light is not in shadow of anotherobject { addthis light contribution to computed color; } } } Final color = Final color + computed color * previous reflectionfactor; reflection factor = reflection factor * surface reflectionproperty; increment depth; } until reflection factor is 0 or maximumdepth is reached }
五、路径追踪 Path Tracing
从视点发出一条光线,光线与物体表面相交时根据表面的材质属性继续采样一个方向,发出另一条光线,如此迭代,直到光线打到光源上(或逃逸出场景),然后用蒙特卡洛方法,计算光线的贡献,作为像素的颜色值。而使用蒙特卡洛方法对积分的求解是无偏的,只要时间足够长,最终图像能收敛到一个正确的结果。
路 径 追 踪 = 光 线 追 踪 + 蒙 特 卡 洛 方 法 路径追踪 = 光线追踪+ 蒙特卡洛方法 路径追踪=光线追踪+蒙特卡洛方法六、Ray Casting ,Ray Tracing,Path Tracing区别
- 光线投射 Ray Tracing:实际上是一个框架,符合该框架的都叫Ray Tracing。从视点发射光线,与物体相交就根据规则反射、折射或吸收。遇到光源或者走太远就停住。一般来说运算量不小。
- 光线追踪 Ray Casting:是Ray Tracing的第一步,发射光线,与物体相交。
- 路径追踪 Path Tracing:是Ray Tracing + 蒙特卡洛法。在相交后会选一个随机方向继续跟踪,并根据BRDF计算颜色。
七、环境光遮蔽 Ambient Occlusion
环境光遮蔽(Ambient Occlusion,简称AO)是全局光照明的一种近似替代品,可以产生重要的视觉明暗效果,通过描绘物体之间由于遮挡而产生的阴影, 能够更好地捕捉到场景中的细节,可以解决漏光,阴影漂浮等问题,改善场景中角落、锯齿、裂缝等细小物体阴影不清晰等问题,增强场景的深度和立体感。
后记
本章内容的重点是光线投射、光线追踪和路径追踪,介绍了全局光照技术的发展历程,涉及的算法简单介绍了其原理,算法变形和细节没有过多介绍,时间问题我也没有过多探索,通过本章的学习我对全局光照技术有了大体的认识,具体算法细节留到需要用的时候再做深入探索。
更多相关内容 -
全局光照技术.pdf(手机拍照版)
2019-05-28 16:31:18《全局光照技术:从离线到实时渲染》是一本聚焦于渲染领域的计算机图形学图书,它同时包含了离线和实时渲染的内容,探讨了渲染中最常用的约十种全局光照技术的概念,原理以及相互之间的联系,并以这些全局光照技术为... -
多光源实时全局光照算法的实现 (2011年)
2021-05-14 14:39:15针对全局光照尤其是多光源的全局光照的复杂性使其一直局限于离线应用的问题。对能量大的能代表多个光源的主光源,采用立即辐射度的方法,跟踪其光线产生虚拟点光源(VPLS:virtual Point Lights),这些VPLS再照亮场景... -
基于体素锥追踪的全局光照算法
2021-02-22 05:19:18提出一种全局光照的计算方法,使用体素结构存储简化场景的光照信息,并通过体素锥追踪方法来收集光照信息,计算间接光照。在计算过程中,每帧只更新动态场景的体素结构,既可以支持动态光源,又避免了更新静态场景的时间... -
基于级联体素纹理的VCT全局光照算法
2021-04-30 13:28:48针对大规模场景的全局光照渲染往往因为计算量过大而无法满足实时性要求的问题进行了研究,提出一种高性能的体素圆锥追踪(voxel cone tracking,VCT)全局光照算法。算法包括:a)在体素化阶段,提出一种新型的场景... -
全局光照算法:IBL
2022-02-28 13:10:59基于图像的光照(Image based lighting, IBL)是一类光照技术的集合。其光源不是可分解的直接光源,而是将周围环境整体视为一个大光源。现代渲染引擎中使用的IBL有四种常见类型: 远程光探头,用于捕捉"无限远"处的...1. 介绍
基于图像的光照(
Image based lighting
, IBL)是一类光照技术的集合。其光源不是可分解的直接光源,而是将周围环境整体视为一个大光源。现代渲染引擎中使用的IBL有四种常见类型:- 远程光探头,用于捕捉"无限远"处的光照信息,可以忽略视差。远程探头通常包括天空, 远处的景观特征或建筑物等。它们可以由渲染引擎捕捉, 也可以高动态范围图像的形式从相机获得.
- 局部光探头,用于从特定角度捕捉世界的某个区域。捕捉会投影到立方体或球体上, 具体取决于周围的几何体。局部探头比远程探头更精确,在为材质添加局部反射时特别有用.
- 平面反射,用于通过渲染镜像场景来捕捉反射。此技术只适用于平面,如建筑地板,道路和水。
- 屏幕空间反射,基于在深度缓冲区使用光线行进方法渲染的场景,来捕捉反射。SSR效果很好,但可能非常昂贵。
IBL
通常使用(取自现实世界或从3D场景生成的)环境立方体贴图 (Cubemap
) ,我们可以将立方体贴图的每个像素视为光源,在渲染方程中直接使用它。这种方式可以有效地捕捉环境的全局光照,使物体更好地融入环境。全景图也有其它形式的记录方法,例如
Spherical Map
(我们常见的HDRI Map
多用Spherical Map
表示),其对于地平线的分辨率要高于正上方的天空,比较适合室外这种天空啥都没有的环境。
理论上,物体的每一个细分的表面都应该对应着自己独有的一个半球体光照环境,而不是整个物体共享一个
Cubemap
,但是这样的话对于每个细分表面都算Cubemap
,性能开销还不如光线追踪——所以使用此技术的一个前提是:周围的物体足够远。1.1 积分方程的分解
先快速回顾一下基于Cook-Torrance BRDF的反射积分方程:
L o ( p , w o ) = ∫ Ω k d c π + k s D F G 4 ( w 0 ⋅ n ) ( w i ⋅ n ) L i ( p , w i ) n ⋅ w i d w i L_o(p,w_o)=\int_{\Omega}{k_d\frac{c}{\pi}+k_s\frac{DFG}{4(w_0\cdot n)(w_i\cdot n)}L_i(p,w_i)n\cdot w_i}dw_i Lo(p,wo)=∫Ωkdπc+ks4(w0⋅n)(wi⋅n)DFGLi(p,wi)n⋅widwi以上公式应该都是很熟了,每个符号就无需解释了
来自周围环境的入射光都可能具有一些辐射度,使得解决积分变得不那么简单。这为解决积分提出了两个要求:
- 给定任何方向向量 w i w_i wi ,我们需要一些方法来获取这个方向上场景的辐射度。
- 求解积分需要快速且实时
但实际上,第一点和第二点似乎是冲突的——因为如果真的在渲染时,考虑每个方向的辐射度,那就很难做到快速实时。为了以更有效的方式解决积分,我们需要对其大部分结果进行预计算。仔细研究反射方程,我们发现
BRDF
的漫反射 k d k_d kd 和镜面 k s k_s ks 项是相互独立的——可以将积分分成两部分:
L o ( p , w o ) = ∫ Ω k d c π L i ( p , w i ) n ⋅ w i d w i + ∫ Ω k s D F G 4 ( w 0 ⋅ n ) ( w i ⋅ n ) L i ( p , w i ) n ⋅ w i d w i L_o(p,w_o)=\int_{\Omega}{k_d\frac{c}{\pi}L_i(p,w_i)n\cdot w_i}dw_i+\int_{\Omega}{k_s\frac{DFG}{4(w_0\cdot n)(w_i\cdot n)}L_i(p,w_i)n\cdot w_i}dw_i Lo(p,wo)=∫ΩkdπcLi(p,wi)n⋅widwi+∫Ωks4(w0⋅n)(wi⋅n)DFGLi(p,wi)n⋅widwi这样,就可以分开研究漫反射和镜面反射。
2. Diffuse IBL
仔细观察漫反射积分,我们发现漫反射兰伯特项是一个常数项,不依赖于任何积分变量。基于此,我们可以将常数项移出漫反射积分:
L o ( p , w o ) = k d c π ∫ Ω L i ( p , w i ) n ⋅ w i d w i L_o(p,w_o)=k_d\frac{c}{\pi}\int_{\Omega}{L_i(p,w_i)n\cdot w_i}dw_i Lo(p,wo)=kdπc∫ΩLi(p,wi)n⋅widwi因为有上面的“周围物体足够远”的假设,这里
p
也和积分变量没什么关系了。但如果是室内场景,可以通过在场景中放置多个反射探针,来解决此问题——每个反射探针单独预计算其周围环境的辐照度图。这样,位置p
处的辐照度(以及辐射度)是:最近的几个反射探针之间的辐照度插值。这时候积分只和入射方向有关 了,而
Cubemap
本身就可以记录整个球面的入射角度,所以Cubemap
在这里很好的派上了用场。为了避免运行时做“积分”,我们可以在运行前预处理这张Cubemap
。
这个预计算的立方体贴图,在每个采样方向 w o w_o wo 上存储其积分结果,可以理解为:场景中所有能够击中面向 w o w_o wo 的表面的间接漫反射光的预计算总和。这样的立方体贴图被称为辐照度图
Irradiance Map
。Irradiance Map
:使用表面法线n
作为索引,存储的是辐照度值irradiance
,要求的分辨率极低。可以使用预过滤的高光环境贴图的最低等级Mip来存储。
Irradiance Map
直接和光照挂钩,所以这张图存的时候一定要存线性空间,并且支持分量大于1
的像素值。.hdr
或者.exr
格式的图像(radiance HDR)就可以做到这点。2.1 立方体贴图的卷积
如果只是使用上诉的
cubeMap
,那我们为了得到尽量正确的积分结果,还是要对半球的各个方向进行采样。为了避免这一点,我们需要对cubemap
进行预过滤,让我们可以在实时运行时,通过一次采样,得到所需的辐照度。既然半球的朝向决定了我们捕捉辐照度的位置,我们可以预先计算每个可能的半球朝向的辐照度,这些半球朝向涵盖了所有可能的出射方向 w o w_o wo。
真的对每一个可能方向去采样,理论上根本做不完,也没什么必要,所以这里肯定是用一种离散积分的方法,比如最简单的黎曼积分。为了避免对难处理的立体角求积分,我们使用球坐标 θ \theta θ 和 ϕ \phi ϕ 来代替立体角 w w w——这时候积分方程就变成了:
L o ( p , ϕ o , θ o ) = k d c π ∫ ϕ = 0 2 π ∫ θ = 0 1 2 π L i ( p , ϕ i , θ i ) c o s ( θ ) s i n ( θ ) d ϕ d θ L_o(p,ϕ_o,θ_o)=k_d\frac{c}{π}∫^{2π}_{ϕ=0}∫^{\frac{1}{2}π}_{θ=0}L_i(p,ϕ_i,θ_i)cos(θ)sin(θ)dϕdθ Lo(p,ϕo,θo)=kdπc∫ϕ=02π∫θ=021πLi(p,ϕi,θi)cos(θ)sin(θ)dϕdθ
当我们离散地对两个球坐标轴进行采样时,每个采样近似代表了半球上的一小块区域,如上图所示。注意,由于球的一般性质,当采样区域朝向中心顶部会聚时,天顶角 θ 变高,半球的离散采样区域变小。为了平衡较小的区域贡献度,我们使用 sinθ 来权衡区域贡献度,这就是多出来的 sin 的作用。
将上诉公式转换为黎曼和形式的离散版本:
一般情况下,连续形式可按照如下方法,转换成离散形式:
所以,参考离散版本,实际代码如下(这里存取的是辐照度
E
,注意!):vec3 irradiance = vec3(0.0); vec3 up = vec3(0.0, 1.0, 0.0); vec3 right = normalize(cross(up, normal)); up = normalize(cross(normal, right)); float sampleDelta = 0.025; float nrSamples = 0.0; for (float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta) { for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta) { // spherical to cartesian (in tangent space) vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); // tangent space to world space (for cubemap to use) vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta); nrSamples++; } } irradiance = PI * irradiance * (1.0 / float(nrSamples));
这段程序可以在运行图形应用程序之前就执行,然后存在硬盘上,程序执行直接读;也可以动态生成,然后存在显存上(和
Mipmap
的生成差不多)。2.2 应用
实际使用很简单:
vec3 kS = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); vec3 kD = 1.0 - kS; vec3 irradiance = texture(irradianceMap, N).rgb; vec3 diffuse = irradiance * albedo; // 间接光照的漫反射部分 vec3 ambient = (kD * diffuse) * ao;
为什么上诉代码中,是乘以
albedo
,而不是 a l b e d o / π albedo/\pi albedo/π,原因见上诉:离散版本的推导。2.3 关于菲涅尔项
由于环境光来自半球内围绕法线
N
的所有方向,因此没有一个确定的半向量,来计算菲涅耳效应。为了模拟菲涅耳效应,我们用法线和视线之间的夹角计算菲涅耳系数。然而,之前我们是以受粗糙度影响的微表面半向量作为菲涅耳公式的输入,但我们目前没有考虑任何粗糙度,表面的反射率总是会相对较高。间接光和直射光遵循相同的属性,因此我们期望较粗糙的表面在边缘反射较弱。由于我们没有考虑表面的粗糙度,间接菲涅耳反射在粗糙非金属表面上看起来有点过强。
可以通过在 Sébastien Lagarde 提出的 Fresnel-Schlick 方程中加入粗糙度项来缓解这个问题:
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) { return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); }
此时的结果如下:
自己也写过,照着learnOpenGL这个教程,但是找不到了,就直接照搬吧。
2.4 后续研究
考虑到运行时性能和效果,还可以做几点增强:
- 用
Irradiance Map
算出SH
,提升性能。全局可以使用一个SH
模拟天光照明(例如UE4
中的SkyLight
),也可以组建成网格,实现动态物体的 GI(Unity
中的Light Probe Group
,UE
中的ILC
)。An Efficient Representation for Irradiance Environment Maps - 使用
Hammersley
随机采样(球面上的均匀采样)实现Importance Sampling
,提升离线资产处理的性能。Hammersley Points on the Hemisphere
2.5 Irradiance Environment Maps
Irradiance Map 和 Light Map
当初第一次看RTR4时,看到十一章的第五节——
Diffuse Global Illumination
时,就特别疑惑,搞不清楚light map
和后文诸如H基技术的区别。特别是关于法线的描述。这里给出一个不知道对不对的理解:
-
light map
: 对于每一个物体的表面,以较低的分辨率预计算它的每个像素的光照情况,然后存储在纹理中。这个时候如果把墙旋转一个角度,那么如下所示的两张图都失效了。所以,light map
更多用于
-
Irradiance Map
:则是只预计算周围环境的光照情况。
SH基础
球谐函数在球域 S S S上定义了一个正交基。使用如下参数化:⬇️
s = ( x , y , z ) = ( sin θ cos ( ϕ ) , sin θ cos ϕ , cos ϕ ) s=(x,y,z)=(\sin{\theta}\cos(\phi),\sin{\theta}\cos{\phi},\cos{\phi}) s=(x,y,z)=(sinθcos(ϕ),sinθcosϕ,cosϕ)
基函数定义为:⬇️
Y l m ( θ , ϕ ) = K l m e i m ϕ P l ∣ m ∣ ( cos θ ) , l ∈ N , − l ≤ m ≤ l Y_l^m(\theta,\phi)=K_l^me^{im\phi}P_l^{|m|}(\cos{\theta}),l\in N,-l\leq m\leq l Ylm(θ,ϕ)=KlmeimϕPl∣m∣(cosθ),l∈N,−l≤m≤l
其中, P l m P_l^m Plm是相关的勒让德多项式, K l m K_l^m Klm是归一化常数:⬇️
K l m = ( 2 l + 1 ) 4 π ( l − ∣ m ∣ ) ! ( l + ∣ m ∣ ) ! K_l^m=\sqrt{\frac{(2l+1)}{4\pi}\frac{(l-|m|)!}{(l+|m|)!}} Klm=4π(2l+1)(l+∣m∣)!(l−∣m∣)!
上述定义形成了一个复基complex basis
,实值基是由简单的变换给出的:⬇️
l l l的低值(称为频带指数
band index
)表示球上的低频基函数,频带 l l l的基函数在 x , y , z x,y,z x,y,z上化为 l l l阶多项式。可以用简单的递推公式进行计算。因为SH基是正交的,所以定义在S上的标量函数 f f f可以通过如下积分⬇️,投影Projection
得到系数:⬇️
f l m = ∫ f ( s ) y l m ( s ) d s f_l^m=\int{f(s)y_l^m(s)\mathrm{d}s} flm=∫f(s)ylm(s)ds
这些系数提供了n阶重建Reconstruction
函数:⬇️
f ‾ ( s ) = ∑ l = 0 n − 1 ∑ m = − l l f l m y l m ( s ) \overline{f}(s)=\sum_{l=0}^{n-1}\sum_{m=-l}^{l}{f_l^my_l^m(s)} f(s)=l=0∑n−1m=−l∑lflmylm(s)
Properties: SH投影的一个重要性质是它的旋转不变性,例如:给定 g ( s ) = f ( Q ( s ) ) g(s)=f(Q(s)) g(s)=f(Q(s)), Q Q Q是S上的一个任意的旋转函数,则:⬇️(这类似于一维傅里叶变换的位移不变性)
g ‾ ( s ) = f ‾ ( Q ( s ) ) \overline{g}(s)=\overline{f}(Q(s)) g(s)=f(Q(s))
这个不变性意味着,当在一组旋转的样本处采样f时,SH投影不会产生任何锯齿。SH基的正交性提供了一个有用的性质,即给定任意两个球上的函数 a a a和 b b b,它们的投影满足:⭐️
换句话说,对带限函数的乘积进行积分,可将其简化为投影系数的点积和。
使用SH进行投影
如果我们使用
3
个频带的SH
系数,也就是说,我们只需要计算得到9
个 L l m L_{lm} Llm,而不是对每个像素进行积分。这些系数的计算方法如下:
L l m = ∫ θ = 0 π ∫ ϕ = 0 2 π L ( θ , ϕ ) Y l m ( θ , ϕ ) s i n θ d θ d ϕ L_{lm}=\int_{\theta=0}^{\pi}\int_{\phi=0}^{2\pi}L(\theta,\phi)Y_{lm}(\theta,\phi)sin\theta d\theta d\phi Llm=∫θ=0π∫ϕ=02πL(θ,ϕ)Ylm(θ,ϕ)sinθdθdϕ它的离散形式如下:
L l m = π N 1 ⋅ 2 π N 2 ∑ p i x e l s ( θ , ϕ ) e n v m a p [ p i x e l ] ⋅ Y l m ( θ , ϕ ) L_{lm}=\frac{\pi}{N1}\cdot \frac{2\pi}{N2}\sum_{pixels(\theta,\phi)}{envmap[pixel] \cdot Y_{lm}(\theta,\phi)} Llm=N1π⋅N22πpixels(θ,ϕ)∑envmap[pixel]⋅Ylm(θ,ϕ)
实际上,更加精确的公式,应该是实际考虑每个cubemap
上的像素所代表的矩形区域投影到单位球面的面积:
L l m = ∑ p i x e l s ( θ , ϕ ) e n v m a p [ p i x e l ] ⋅ Y l m ( θ , ϕ ) Δ w i L_{lm}=\sum_{pixels(\theta,\phi)}{envmap[pixel] \cdot Y_{lm}(\theta,\phi)\Delta{w_i}} Llm=pixels(θ,ϕ)∑envmap[pixel]⋅Ylm(θ,ϕ)Δwi最终我们得到
9
个SH系数(27
个float)。这里可以简单讲下为什么我们只需要这么点数据(27个float)就可以记录光照情况:最大的原因就是我们的假设——光照环境无限远,每个着色点 P i P_i Pi 对于光照贴图来说,都是一样的,这个时候, L ( p , θ , ϕ ) L(p,\theta,\phi) L(p,θ,ϕ) 就变成了 L ( θ , ϕ ) L(\theta,\phi) L(θ,ϕ),所以我们只需要在这个
cube map
(或者探针)的中心 P 0 P_0 P0,算一次投影结果,就可以推广到其他着色点。重建
我们重建的光照度量是辐照度
E
,而对于E
和L
的SH系数有如下关系:
E l m = A l L l m E_{lm}=A_lL_{lm} Elm=AlLlm
公式中的 A l A_{l} Al的定义如下:
这个 A l A_l Al 的物理意义是什么?考虑diffuse项的计算,光照积分里面是 L ⋅ c o s L\cdot cos L⋅cos,我们上面计算的只是 L L L,而没有余弦项。所以 A l A_l Al 应该是这个余弦项的SH系数 (根据SH基的正交性可以推断出)。目前还有一个问题是,我们是要投影 ∫ ( n ⋅ w ) d w \int (n\cdot w) dw ∫(n⋅w)dw,而这个积分哪怕基于环境光无限远的假设,除了 ( θ , ϕ ) (\theta,\phi) (θ,ϕ),我们还要考虑 n n n,这意味着我们需要 w i d t h ∗ h e i g h t ∗ N U M S H width * height * {NUM_{SH}} width∗height∗NUMSH个系数(其实就是三张纹理),这根本不节省带宽。考虑球谐函数的旋转不变性(法线的各不相同,本质就是旋转),我们可以对其进行简化,最终得到 A l A_l Al。具体推导可以见论文复现:A Non-Photorealistic Lighting Model For Automatic Technical Illustration
所以重建公式变成了:
E ( θ , ϕ ) = ∑ l , m A l L l m Y l m ( θ , ϕ ) E(\theta,\phi)=\sum_{l,m}{A_l}L_{lm}Y_{lm}(\theta,\phi) E(θ,ϕ)=l,m∑AlLlmYlm(θ,ϕ)
对于实际渲染,我们可以使用下列公式来计算E
:
由于我们只考虑 l ≤ 2 l\leq2 l≤2,辐照度就是一个(归一化)表面法线坐标的二次多项式。因此,对于 n t = ( x , y , z , 1 ) n^t=(x,y,z,1) nt=(x,y,z,1),我们可以有:
E ( n ) = n t M n E(n)=n^tMn E(n)=ntMn
M是一个对称的4x4矩阵。下面的方程对渲染特别有用,因为我们只需要一个矩阵-向量乘法和一个点乘法来计算E
:
总结
我们在
Diffuse IBL
的假设以及思想上,结合球谐函数,将环境贴图的低频光照信息投影到SH基
上,这样就可以极大节省带宽,因为我们不需要存储一张预过滤图了,而是存储几十个vector就行了(以 l = 2 l=2 l=2为例,我们只需要存储9个SH vector
系数即可)实时运行过程中,只需要以法线为索引,就可以快速重建辐照度
E
,下面是UE4的源码:// filament根据预缩放的SH重建辐照度的GLSL代码 vec3 irradianceSH(vec3 n) { // uniform vec3 sphericalHarmonics[9] // 我们只使用前两个波段以获得更好的性能 return //另外, 由于使用 Kml 进行了预缩放, SH系数可视为颜色, //特别地sphericalHarmonics[0]直接就是平均辐照度. sphericalHarmonics[0] + sphericalHarmonics[1] * (n.y) + sphericalHarmonics[2] * (n.z) + sphericalHarmonics[3] * (n.x) + sphericalHarmonics[4] * (n.y * n.x) + sphericalHarmonics[5] * (n.y * n.z) + sphericalHarmonics[6] * (3.0 * n.z * n.z - 1.0) + sphericalHarmonics[7] * (n.z * n.x) + sphericalHarmonics[8] * (n.x * n.x - n.y * n.y); }
3. Specular IBL
将重点关注反射方程的镜面部分:
L o ( p , w o ) = ∫ Ω k s D F G 4 ( w 0 ⋅ n ) ( w i ⋅ n ) L i ( p , w i ) n ⋅ w i d w i L_o(p,w_o)=\int_{\Omega}{k_s\frac{DFG}{4(w_0\cdot n)(w_i\cdot n)}L_i(p,w_i)n\cdot w_i}dw_i Lo(p,wo)=∫Ωks4(w0⋅n)(wi⋅n)DFGLi(p,wi)n⋅widwi
很明显,镜面部分要复杂的多,不仅受入射光方向影响,还受视角影响。如果试图解算所有入射光方向加所有可能的视角方向的积分,二者组合数会极其庞大,实时计算太昂贵。进行预计算?但是这里的积分依赖于 w i w_i wi和 w o w_o wo,我们无法用两个方向向量采样预计算的立方体图。
Epic Games
提出了一个解决方案,他们预计算镜面部分的卷积,为实时计算作了一些妥协,这种方案被称为分割求和近似法(split sum approximation
)——将预计算分成两个单独的部分求解,再将两部分组合起来,得到预计算结果。分割求和近似法将镜面反射积分拆成两个独立的积分:
L o ( p , w o ) = ∫ Ω L i ( p , w i ) d w i ∗ ∫ Ω f r ( p , w i , w o ) n ⋅ w i d w i L_o(p_,w_o)=\int_{\Omega}{L_i(p,w_i)dw_i}*\int_{\Omega}f_r(p,w_i,w_o)n\cdot w_idw_i Lo(p,wo)=∫ΩLi(p,wi)dwi∗∫Ωfr(p,wi,wo)n⋅widwi3.1 第一部分:光照部分
L o ( p , w o ) = ∫ Ω f r L i ( p , w i ) d w i L_o(p_,w_o)=\int_{\Omega_{f_r}}{L_i(p,w_i)dw_i} Lo(p,wo)=∫ΩfrLi(p,wi)dwi
卷积的第一部分被称为预滤波环境贴图,它类似于辐照度图,是预先计算的环境卷积贴图,但这次考虑了粗糙度。这部分看起来和上面
Diffuse IBL
非常接近,唯一不一样的是:它的积分域从整个半球,变为了BRDF
的覆盖范围,也就是Specular Lobe / BRDF Lobe
。于是积分域就和 Lobe “撑起来的胖瘦程度”有关了。而Lobe
和BRDF
项的Roughness
有直接关系——越粗糙,高光越分散(极端情况就是diffuse
了)。Roughness
是变量,因此需要得到一系列不同Roughness
所对应的Cubemap
。
diffuse IBL
中对粗糙度的考虑是菲涅尔项,但在求解积分的时候,并没有考虑,和这里是不同的。这里轮到
Mipmapping
来救场了:用不同的mipmaps
离散的表示不同的Roughness
,借助着Trilinear Filtering
三线性纹理过滤,来插值得到真正Roughness
所对应的光照强度。在实时渲染中,可以预处理原始环境贴图,得到的Mipmap
过的环境贴图被称为Pre-filtered Environment Map
(预处理环境贴图),如下图所示(来自LearnOpenGL
):
虽然积分中没有 w o w_o wo(视线向量)的身影,但采样的球面积分域和出射角有关,我们还没有考虑——一个lobe
除了胖瘦程度,还有朝向!但正如之前所说,我们已经和Diffuse IBL
一样有了法线N
作为索引,来采样这个预滤波环境贴图,不能在考虑第二个向量了,因此Epic Games
假设视角方向V
——也就是镜面反射方向R
——总是等于输出采样方向N
,以作进一步近似。翻译成代码如下:vec3 N = normalize(w_o); vec3 R = N; vec3 V = R;
显然,这种近似会导致:在视线几乎垂直于法线的掠射方向上,会无法获得很好的掠射镜面反射
3.2 光照部分的实现
一些基础技术,这里直接给出实现,具体原理请百度。
低差异序列:Hammersley 序列
float RadicalInverse_VdC(uint bits) { bits = (bits << 16u) | (bits >> 16u); bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); return float(bits) * 2.3283064365386963e-10; // / 0x100000000 } // ---------------------------------------------------------------------------- vec2 Hammersley(uint i, uint N) { return vec2(float(i)/float(N), RadicalInverse_VdC(i)); }
GGX重要性采样
有别于均匀或纯随机地(比如蒙特卡洛)在积分半球 Ω 产生采样向量,我们的采样会根据粗糙度,偏向微表面的半向量的宏观反射方向。采样过程将与我们之前看到的过程相似:
- 开始一个大循环,生成一个随机(低差异)序列值,用该序列值在切线空间中生成样本向量,
- 将样本向量变换到世界空间,并对场景的辐射度采样。
const uint SAMPLE_COUNT = 4096u; for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); }
此外,要构建采样向量,我们需要一些方法定向和偏移采样向量,以使其朝向特定粗糙度的镜面波瓣方向。我们可以如理论教程中所述使用
NDF
,并将GGX NDF
结合到 Epic Games 所述的球形采样向量的处理中:vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) { float a = roughness*roughness; float phi = 2.0 * PI * Xi.x; float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); float sinTheta = sqrt(1.0 - cosTheta*cosTheta); // from spherical coordinates to cartesian coordinates vec3 H; H.x = cos(phi) * sinTheta; H.y = sin(phi) * sinTheta; H.z = cosTheta; // from tangent-space vector to world-space sample vector vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); vec3 tangent = normalize(cross(up, N)); vec3 bitangent = cross(N, tangent); vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; return normalize(sampleVec); }
着色器
#version 330 core out vec4 FragColor; in vec3 localPos; uniform samplerCube environmentMap; uniform float roughness; const float PI = 3.14159265359; float RadicalInverse_VdC(uint bits); vec2 Hammersley(uint i, uint N); vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness); void main() { vec3 N = normalize(localPos); vec3 R = N; vec3 V = R; const uint SAMPLE_COUNT = 1024u; float totalWeight = 0.0; vec3 prefilteredColor = vec3(0.0); for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); vec3 H = ImportanceSampleGGX(Xi, N, roughness); vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(dot(N, L), 0.0); if(NdotL > 0.0) { prefilteredColor += texture(environmentMap, L).rgb * NdotL; totalWeight += NdotL; } } prefilteredColor = prefilteredColor / totalWeight; FragColor = vec4(prefilteredColor, 1.0); }
当然,也可以使用
Diffuse IBL
中均匀采样的方法,但这样的效率太低。3.3 第二部分:BRDF 部分
推导
L o ( p , w o ) = ∫ Ω f r ( p , w i , w o ) n ⋅ w i d w i L_o(p_,w_o)=\int_{\Omega}f_r(p,w_i,w_o)n\cdot w_idw_i Lo(p,wo)=∫Ωfr(p,wi,wo)n⋅widwi
这个方程要求我们在 n ⋅ ω o n\cdot ω_o n⋅ωo 、表面粗糙度、菲涅尔系数 F 0 F_0 F0 上计算BRDF方程的卷积。这等同于在纯白的环境光或者辐射度恒定为1.0的设置下,对镜面BRDF求积分。对3
个变量做卷积有点复杂,不过我们可以把 F 0 F_0 F0移出镜面BRDF方程:
F
为菲涅耳方程。将菲涅耳分母移到BRDF
下面可以得到如下等式:
用Fresnel-Schlick
近似公式替换右边的F
可以得到:
让我们用 α = ( 1 − w o ⋅ h ) 5 \alpha = (1-w_o\cdot h)^5 α=(1−wo⋅h)5,以便更轻松地求解 F 0 F_0 F0:
然后我们将菲涅耳函数F
分拆到两个积分里:
接下来,我们将 α \alpha α替换回其原始形式,从而得到最终分割求和的BRDF方程:
公式中的两个积分分别表示 F 0 F_0 F0的比例和偏差 。注意,这里的 f r f_r fr中不计算F
项。积分式子里面留下来了夹角( n n n 和 w o w_o wo)和粗糙度。我们将卷积后的结果存储在2D查找纹理(Look Up Texture,LUT
)中,这张纹理被称为 BRDF 积分贴图。着色器
BRDF卷积着色器在2D 平面上执行计算,直接使用其2D纹理坐标作为卷积输入(
NdotV
和roughness
)。代码与预滤波器的卷积代码大体相似,不同之处在于,它现在根据BRDF
的几何函数和Fresnel-Schlick
近似来处理采样向量:vec2 IntegrateBRDF(float NdotV, float roughness) { vec3 V; V.x = sqrt(1.0 - NdotV*NdotV); V.y = 0.0; V.z = NdotV; float A = 0.0; float B = 0.0; vec3 N = vec3(0.0, 0.0, 1.0); const uint SAMPLE_COUNT = 1024u; for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); vec3 H = ImportanceSampleGGX(Xi, N, roughness); vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(L.z, 0.0); float NdotH = max(H.z, 0.0); float VdotH = max(dot(V, H), 0.0); if(NdotL > 0.0) { float G = GeometrySmith(N, V, L, roughness); // 我们就是基于NDF进行重要性采样的 // 所以这里除了F,实际上也不需要计算D项。 // 所以fr只剩下了分母,和几何项G。 float G_Vis = (G * VdotH) / (NdotH * NdotV); float Fc = pow(1.0 - VdotH, 5.0); A += (1.0 - Fc) * G_Vis; B += Fc * G_Vis; } } A /= float(SAMPLE_COUNT); B /= float(SAMPLE_COUNT); return vec2(A, B); } // ---------------------------------------------------------------------------- void main() { vec2 integratedBRDF = IntegrateBRDF(TexCoords.x, TexCoords.y); FragColor = integratedBRDF; }
如你所见,BRDF卷积部分是从数学到代码的直接转换。我们将角度 θ \theta θ和粗糙度作为输入,以重要性采样产生采样向量,在整个几何体上结合BRDF的菲涅耳项对向量进行处理,然后输出每个样本上 F 0 F_0 F0的系数和偏差,最后取平均值。
关于几何项
与
IBL
一起使用时,BRDF的几何项略有不同,因为k
变量的含义稍有不同:
由于BRDF卷积是镜面IBL积分的一部分,因此我们要在
Schlick-GGX
几何函数中使用 k I B L k_{IBL} kIBL:float GeometrySchlickGGX(float NdotV, float roughness) { float a = roughness; float k = (a * a) / 2.0; float nom = NdotV; float denom = NdotV * (1.0 - k) + k; return nom / denom; } // ---------------------------------------------------------------------------- float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; }
请注意,虽然 k 还是从 a 计算出来的,但这里的 a 不是 roughness 的平方——如同最初对 a 的其他解释那样——在这里我们假装平方过了。我不确定这样处理是否与 Epic Games 或迪士尼原始论文不一致,但是直接将 roughness 赋给 a 得到的 BRDF 积分贴图与 Epic Games 的版本完全一致。
结果
3.4 实时运行阶段
也是公式到代码的直接复刻:
vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); vec3 kS = F; vec3 kD = 1.0 - kS; kD *= 1.0 - metallic; vec3 irradiance = texture(irradianceMap, N).rgb; vec3 diffuse = irradiance * albedo; const float MAX_REFLECTION_LOD = 4.0; vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; vec2 envBRDF = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg; vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); vec3 ambient = (kD * diffuse + specular) * ao;
请注意,
specular
没有乘以 k s k_s ks,因为已经乘过了菲涅耳系数。 现在,在一系列粗糙度和金属度各异的球上运行此代码:
参考
[1] LearnOpenGLCN
[2] Real Time Rendering 4th.
[3] Filament白皮书
[4] 学姐的笔记
[5] Games202 -
基于PRT的大规模场景全局光照技术
2021-02-23 14:20:55为了解决传统的基于光辐射传输预计算(PRT)的全局光照技术在大规模场景下渲染效率低下的问题,在对PRT光照算法的本质进行分析的基础上,提出了一种基于四叉树的自适应网格细分算法.根据光照方程中的可见性函数,只在需要... -
全局光照技术解析Global Illumination Explained
2021-07-08 08:57:54前言:Global Illumination全局光照技术是实时渲染的必然发展方向。我参考了一些研究成果,琢磨了一下,让更多的人可以理解这项“古老”的技术。 Front Line 虽然说,如今的GPU速度已经非常之快,并行浮点计算...解析全局光照Global Illumination Explained
前言:Global Illumination全局光照技术是实时渲染的必然发展方向。我参考了一些研究成果,琢磨了一下,让更多的人可以理解这项“古老”的技术。
Front Line
虽然说,如今的GPU速度已经非常之快,并行浮点计算能力已经相当的强大,但是由于很多人依旧使用传统的Phong局部光照模型对场景进行Shading处理,所以成像效果上还远远不能够与离线渲染的图像相比。如让我们目瞪口呆的Unreal Engine3,技术规格依旧是48bit Color Phong HDR。虽然人们已经开始普遍的使用Shading Language,但是光栅化、对屏幕空间差值的操作依旧没有变化。这一点不同于Pixar RenderMan。重要的原因就是,无法精确的从物理上模拟光线在场景中的交互过程。对于PC机平台来说,由于硬件性能的限制,实时光线跟踪还是不现实的事情,但是我相信未来人们一定可以在PC机平台上置身于可以和电影画面媲美的场景中交互、游戏。
经典全局光照模型的公式如下:
场景的每一个物体,既有可能是发光体(萤火虫、火把),也有可能是接受光线的物体(举火把的人)。意义等同于,物体的每个点都可能会互相影响。让我们想象,有一个模型S由许多个三角形组成,以每个顶点Vertex为中心,以这点的向量Normal为法向量,构造一个半径为1的半球体Normalized Hemisphere。这样的做法类似于Ambient Occlusion的步骤。当我们从y发出一条光线以ωi的方向入射的时候,函数h返回光线与模型的第一个交点。这里可以进行并行优化。θi是指ωi和Normal的夹角,cosθi将射入的辐射光线投影到Normalized Hemisphere上。模型上每一个点y的辐射度L等于,这个点本身的辐射度Le与反射的辐射度之和。可以使用在半球上取一些点采样,进行测试,或者使用更精确不过更加耗时的Monte Carlo采样法。我们也可以简单的把上面的积分式写成这样的简单式子:
fr是BRDF函数,表现了模型表面的颜色光泽等特性。一般来说这个函数的计算依赖于辐射的入射方向ωi,辐射的反射方向ωr,以及y。为了简化BRDF的表达,我们将公式简化如下,这样表面将只存在各向同性漫反射:
pd(y)是相应位置的漫反射贴图数值。我个人觉得大家如果不知道如何实现BRDF,可以参考GPU Gems1上的那篇《Spatial BRDFs》一文。
现在我们有了4个量,(S, fr, Le,Ψ),最后一个是什么呢?这是处理微平面反射向量集合的函数,可以从理论上分析得到。此时,再试图分解这个公式已经没有多少意义,不如直接用一个近似模型简化:
我们直接使用这个公式得到,穿过像素Pmn光的平均辐射度值 —— 我们知道“摄象机”上有一个个像素,把yf想象为一个针孔,ωyf := P – yf作为方向,XPmn是像素特征函数,δ是Kronecker Delta函数。(是什么东西……)
Alexander Keller在他的《Instant Radiosity》中展现了一种算法,目的是为了快速计算光照特性实现实时渲染。想象场景由有限多的虚拟光源组成。对于像素Pmn,我们计算通过这个像素的平均辐射度:
最后一项TmnL决定了最少有一条辐射光线通过像素Pmn。当场景中有M个光源的时候,我们可以简单的进行累加:
对于最右边的求和式,
Li是辐射度,Pi是第i个光源的位置。Tmn的数值可以用普通图形卡计算得到,直接计算当场景中只有一个光源存在时每个像素的亮度数值。这个算法的最大好处就是在性能和质量上可以寻求平衡,通过控制采样点数和光源数目,也就是控制多通道渲染的次数。Behind the Enemy
Monte Carlo方法的大名想必大家都听说过。它需要一个随机数发生器,而且这个发生器对统计结果得质量来说非常重要。这里省略了详细的推导过程,大家可以看原文[1]。
首先想象从光源发出N个粒子,从光源开始,赋予初始的辐射强度Le,射向场景,p-bar≈||Tfd||约等于场景的平均反射率。这些虚拟的光子不断地反射折射,不断地点亮,直到自己强度为0。伪代码如下:
其中,入射方向可以使用下列公式计算得出:
这种方法的思路被用于目前大部分的Monte Carlo光线跟踪渲染引擎中,比如赫赫有名的Mental Ray,而且可以同时使用GPU与CPU进行加速渲染,缺点是多次累计填充率极高。
Continue Fighting
可以看我的那篇斯图加特大学光线投射引擎的分析文章。
Conclusion
事实上,正如[2]中所说,实时光线跟踪还需要克服下面几点难点:
Parallesim并行:无论是何种方法,为了在有限计算能力下得到最大的性能提升,并行处理都是必须的。这一点在GPU上得到充分表现,而CPU上的并行计算依赖于有经验的设计和出色的算法。
Efficiency效率:我们需要在画质和速度中追求一个平衡点,找到最适合的采样数目,避免额外的而对画质的提升没有帮助的计算。
Independence From Geometry独立于几何体:事实上这才是真正我们需要关注的,毫无疑问,体渲染光线投射在实现上更加的有效,可是它只能处理Volumn Texture,体积大处理速度慢来源少;传统的模型体积小来源广泛,优化分割算法成熟,但是却更适合光栅化处理,速度更快,效果也不差。而且真实的场景中,Raytracer应该无论是Volumn Textures和Indexed Models都可以一视同仁的处理。
我相信会有越来越多的程序依赖或者逐渐采用全局光照技术,从GPU Gems1/2中的静态/动态遮挡算法的进化,到NVIDIA OpenGL SDK 10里Render To 3D Texture的展示,还有采用Volumn Rendering技术的新一代游戏的出现,全局光照技术将应用的越拉越广泛。
相关技术文档下载
[1]Alexander Keller Instant Radiosity
[2]Wald et al. Realtime Ray Tracing and its use for Interactive Global Illumination
以后我会详细的收集相关技术文章给大家打包下载
有几本书我很感兴趣,不知道什么地方有的下载,麻烦直到的朋友告诉我一声
Programming Mental Ray和Realtime Rendering -
全局光照(简述)
2020-05-11 22:05:23全局光照(Global Illumination,简称 GI), 作为图形学中比较酷的概念之一,是指既考虑场景中来自光源的直接光照,又考虑经过场景中其他物体反射后的间接光照的一种渲染技术。 即可以理解为:全局光照 = 直接光照...一、什么是全局光照
全局光照(Global Illumination,简称 GI), 作为图形学中比较酷的概念之一,是指既考虑场景中来自光源的直接光照,又考虑经过场景中其他物体反射后的间接光照的一种渲染技术。
即可以理解为:全局光照 = 直接光照(Direct Light) + 间接光照(Indirect Light)
图1 Direct illumination (原图连接)
图2 Global illumination = Direct illumination +Indirect illumination虽说实际应用中只有漫反射全局照明的模拟算法被称为全局照明算法,但其实理论上说反射、折射、阴影都属于全局光照的范畴,因为模拟它们的时候不仅仅要考虑光源对物体的直接作用还要考虑物体与物体之间的相互作用。也是因为,镜面反射、折射、阴影一般不需要进行复杂的光照方程求解,也不需要进行迭代的计算。因此,这些部分的算法已经十分高效,甚至可以做到实时。不同于镜面反射,光的漫反射表面反弹时的方向是近似“随机”,因此不能用简单的光线跟踪得到反射的结果,往往需要利用多种方法进行多次迭代,直到光能分布达到一个基本平衡的状态。
实际应用中只有漫反射全局照明的模拟算法被称为全局照明算法
PBR中的BRDF主要模拟的是镜面高光,漫反射通常使用Lambert着色方程模拟二、全局光照的主要算法流派
经过几十年的发展,全局光照现今已有多种实现方向,常见的全局光照主要流派列举如下:
- Ray tracing 光线追踪 [1968]
- Path tracing 路径追踪 [1986]
- Photon mapping 光子映射
- Point Based Global Illumination 基于点的全局光照
- Radiosity 辐射度
- Metropolis light transport 梅特波利斯光照传输
- Spherical harmonic lighting 球谐光照
- Ambient occlusion 环境光遮蔽
- Voxel-based Global Illumination 基于体素的全局光照
- Light Propagation Volumes Global Illumination
- Deferred Radiance Transfer Global Illumination
- Deep G-Buffer based Global Illumination
- 等。
三、渲染方程 The Rendering Equation [1986]
在前人的研究基础上,Kajiya于1986年进一步建立了渲染方程的理论,并使用它来解释光能传输的产生的各种现象。这一方程描述了场景中光能传输达到稳定状态以后,物体表面某个点在某个方向上的辐射率(Radiance)与入射辐射亮度等的关系。
可以将渲染方程理解为全局光照算法的基础,Kajiya在1986年第一次将渲染方程引入图形学后,随后出现的很多全局光照的算法,都是以渲染方程为基础,对其进行简化的求解,以达到优化性能的目的。渲染方程根据光的物理学原理,以及能量守恒定律,完美地描述了光能在场景中的传播。很多真实感渲染技术都是对它的一个近似。渲染方程在数学上的表示如下:
图 渲染方程描述了从x点沿某一方向看的光放射的总额。四、光线投射 Ray Casting [1968]
光线投射(Ray Casting),作为光线追踪算法中的第一步,其理念起源于1968年,由Arthur Appel在一篇名为《 Some techniques for shading machine rendering of solids》的文章中提出。其具体思路是从每一个像素射出一条射线,然后找到最接近的物体挡住射线的路径,而视平面上每个像素的颜色取决于从可见光表面产生的亮度。
五、光线追踪 Ray Tracing [1979]
1979年,Turner Whitted在光线投射的基础上,加入光与物体表面的交互,让光线在物体表面沿着反射,折射以及散射方式上继续传播,直到与光源相交。这一方法后来也被称为经典光线跟踪方法、递归式光线追踪(Recursive Ray Tracing)方法,或 Whitted-style 光线跟踪方法。
光线追踪方法主要思想是从视点向成像平面上的像素发射光线,找到与该光线相交的最近物体的交点,如果该点处的表面是散射面,则计算光源直接照射该点产生的颜色;如果该点处表面是镜面或折射面,则继续向反射或折射方向跟踪另一条光线,如此递归下去,直到光线逃逸出场景或达到设定的最大递归深度。
一般的光线追踪算法有两大缺点,一是表面属性比较单一,很难去丰富光接触到物体表面以后发生的多种光学效应,二是忽略了漫反射。我们可以通过模型修正的方式来缓解,首先,物体的表面属性是混合的,比如它有一部分是反射,一部分折射,一部分漫反射。我们这么理解各个部分:当光线接触该表面以后,有一定概率发生反射,折射和漫反射。发生这些光学效应的概率我们可以事先定义好。然后我们多次计算光线跟踪,每次按照概率决定光线的反射属性,就能把各种光学效应都考虑进去。
以下这张图示可以很好的说明光线追踪方法的思路:
图 Ray Tracing Illustration First Bounce
图 典型效果图光线追踪的伪代码:
for each pixel of the screen { Final color = 0; Ray = { starting point, direction }; Repeat { for each object in the scene { determine closest ray object/intersection; } if intersection exists { for each light inthe scene { if the light is not in shadow of anotherobject { addthis light contribution to computed color; } } } Final color = Final color + computed color * previous reflectionfactor; reflection factor = reflection factor * surface reflectionproperty; increment depth; } until reflection factor is 0 or maximumdepth is reached }
六、路径追踪 Path Tracing [1986]
Kajiya也于1986年提出了路径追踪算法的理念,开创了基于蒙特卡洛的全局光照这一领域。根据渲染方程, Kajiya 提出的路径追踪方法是第一个无偏(Unbiased)的渲染方法。路径追踪的基本思想是从视点发出一条光线,光线与物体表面相交时根据表面的材质属性继续采样一个方向,发出另一条光线,如此迭代,直到光线打到光源上(或逃逸出场景),然后用蒙特卡洛的方法,计算其贡献,作为像素的颜色值。
路径追踪 = 光线追踪+ 蒙特卡洛方法
具体算法流程:(1)从视点出发,到成像平面上的每一个像素向场景发出一条虚拟的光线。 (2)当光线与物体相交时按照概率确定光学效应,即是折射,反射还是散射(漫反射)。 (3)根据不同的光学效应继续跟踪和计算,直到得到最终结果。如果光学效应为漫反射,随机选择一个反射方向进行跟踪。 (4)重复前面的过程,把每次渲染出来的图像像素叠加混合,直到渲染出的结果达到满意程度。
【转】MonteCarloPathTracing的参考代码链接 https://github.com/ybbbbt/MonteCarloPathTracing
很香,真实可用,环境也配好了
图 蒙特卡洛法多次迭代的渲染图
图 基于路径追踪渲染的效果图六、光线投射(Ray Casting ),光线追踪(Ray Tracing),路径追踪(Path Tracing)三者的的区别:
- Ray Tracing:这其实是个框架,而不是个方法。符合这个框架的都叫raytracing。这个框架就是从视点发射ray,与物体相交就根据规则反射、折射或吸收。遇到光源或者走太远就停住。一般来说运算量不小。
- Ray Casting:其实这个和volumetric可以脱钩。它就是ray tracing的第一步,发射光线,与物体相交。这个可以做的很快,在Doom 1里用它来做遮挡。
- Path Tracing:是ray tracing + 蒙特卡洛法。在相交后会选一个随机方向继续跟踪,并根据BRDF计算颜色。运算量也不小。还有一些小分类,比如Bidirectional path tracing。
参考文献:
1、【《Real-Time Rendering 3rd》 提炼总结】(八) 第九章 · 全局光照:光线追踪、路径追踪与GI技术进化编年史
2、光线投射,光线追踪与路径追踪的概念与区别
3、蒙特卡洛光线追踪
4、蒙特卡洛光线追踪 计算机图形学冬季作业 -
基于直接光照的全局光照模拟
2019-05-27 02:12:50NULL 博文链接:https://kennyluo.iteye.com/blog/599347 -
实时高清渲染:全局光照(Global Illumination)[2]---漫反射/高光全局光照
2022-04-27 19:33:11预先计算的辐照度值通常与漫反射颜色或反照率映射相乘,存储...将光照和表面颜色分开会消耗更少的内存。 除了最严格的硬件平台外,现在很少使用预先计算的辐照度。根据定义,辐照度是针对给定法线方向计算的,因此我们 -
高真实感全局光照算法优化研究
2021-07-30 08:59:08本文侧重于保证渲染质量同时尽可能地提高渲染速度,提出了多模式快速全局光照算法.该算法能针对不同的硬件配置采用不同的渲染策略,同时针对硬件能力最大程度的保证渲染质量以及交互性.本文做了如下工作: 1)介绍了全局... -
全局光照及相关算法的汇总介绍.ppt
2020-04-01 21:55:54MLT MLT方法 平均100变异路径/像素 可以实现刻蚀效果 MLT更奇妙的地方在于这样的变异路径可以更智能的方式去生成 双向路径跟踪 210路径/像素 光子贴图 针对蒙特卡洛光线跟踪提出的改进算法 目的 产生优化的取样方向 ... -
室内场景中适应光照变化的运动目标检测算法
2020-06-02 18:14:46为了进一步提高室内检测跟踪系统的有效性和稳定性,以经典的混合高斯模型为基础,结合了积分直方图方法,提出了一种室内场景中适应光照变化的运动目标检测算法。该方法通过当前帧和背景帧的全局积分直方图差分来确定... -
Unity实时全局光照和烘焙全局光照 Unity2019
2020-12-04 09:08:061、全局光照GI 全局照明是一个系统,用于模拟光从表面反射到其他表面(间接光)的方式,而不仅仅是直接从光源(直接光)照射到表面的光。 全局光照=直接光+间接光 没有光照的地方是一片死黑;可以想象现实生活中... -
Unity全局光照
2020-12-01 17:19:10Unity是从Unity5开始引入行业领先的实时全局光照技术Enlighten系统 全局光照是什么? 想象现实生活中的场景,一束光投进窗户,从而整个房间的阴暗角落也会被照亮,这就是间接光效的效果。 全局照明是Unity的一... -
【路径追踪】PathTracing 算法
2020-08-30 22:48:58路径追踪算法的实现 -
Unity学习日志_全局光照GI系统简介
2021-01-20 16:42:46全局光照,简称为GI,是一个用来模拟光的互动和反弹等复杂行为的算法。要精确的仿真全局光照非常的具有挑战性,付出的代价也很高,正因如此,现代游戏会在一定程度上预先处理这些计算,而非在游戏运行时实时计算。... -
一种基于全局和局部光照估计的Retinex图像增强算法
2016-01-12 22:19:18一种基于全局和局部光照估计的Retinex图像增强算法,通过分析全局和局部特征量减小图像增强时图像的失真 -
【图形学】体素全局光照GI
2020-05-22 22:30:48本文算法总共有四个阶段,分别为体素化、直接光照、各向异性过滤、间接光照,算法的各个阶段如图1所示。第一阶段是体素化阶段,将图元为三角形的场景转换为体素场景,计算单个体素的基础属性并存储。第二阶段是直 -
全局体渲染光照技术总结概括(截止到2014年)
2020-11-14 15:47:15交互式体光照技术的三个挑战: 体照明模型 交互式传输函数 应用于GPU的算法 科学可视化领域开发体光照模型的主要动机之一是提高视觉感知能力。我们将回顾和比较先进体积照明的现有技术,这些技术的适用性来自于... -
基于预计算的全局光照技术
2020-02-03 22:49:41基于预计算的全局光照技术 实时渲染,意味着渲染算法需要在1/30秒甚至更少的时间内生成当前场景在当前视角下的一张2D图像。尽管这看起来只是在时效上的要求不同,实时渲染和离线渲染采用的的确是两种完全不同的渲染... -
实时全局光照渲染研究
2021-07-30 08:59:07摘要:真实感绘制技术作为计算机图形学的一个重要组成部分,致力于使用计算机对现实世界进行准确模拟,将三维场景渲染成非常真实的图像....能实现真实光照效果的全局光照渲染技术吸引了国内外众多学者的关注和研... -
Cryengine 3新的全局光照算法简介
2015-10-26 12:08:38【文章摘要】Carsten Dachsbacher在其斯图加特大学的个人页面上发表了一篇与Crytek共同创作的论文《实时间接光照中的渐进光照传播Cascaded ...,其在文章总透露Crysis 2所采用的Cryengine 3引擎集成了此种光照算法... -
计算机图形学--全局光照(3D 空间:LPV,VXGI;屏幕空间:SSAO)
2022-01-06 19:06:49LPV做全局光照: 1.质量好 2.非常快 LPV的基本思想: 1.在任何一个shading point上,我如果可以立刻知道四面八方到达该点radiance,就可以很好的做全局光照。 2.在直线转播的过程中,radiance是不变的量 3.解决方法... -
[sig16]《使命召唤》中的全局光照技术
2016-10-30 18:11:05Treyarch是《使命召唤:黑色行动》系列的开发商,这里主要是使用irradiance volume系列的技术,但是在编辑器,数据收集组织方式以及一些出现瑕疵的地方做了更好的处理,达到更加成熟的水准。 -
Unity 5 中的全局光照技术详解
2016-10-21 17:47:06全局光照,简称GI,是一个用来模拟光的互动和反弹等复杂行为的算法,要精确的仿真全局光照非常有挑战性,付出的代价也高,正因为如此,现代游戏会先一定程度的预先处理这些计算,而非游戏执行时实时运算。... -
非均匀光照下的二维码识别算法优化研究
2021-01-26 18:29:43针对某污水膜生产企业使用的包含产品信息的QR码(二维码的一种)在非均匀光照下产生的阴影区域问题,提出一种基于大津法的改进算法。利用图像的局部区域阈值及...与经典全局Otsu算法相比,图像识别率提高了5倍左右。