GAMES202

Some basic understandings

Q: 有关fragment shader中接收vertex shader穿过来的属性的问题:fragment shader是基于屏幕像素为基本单位操作的,而vertex shader是基于顶点操作。二者基于的对象不一样,那么在这里数据是怎样传输的呢?和插值有关吗?

A:在图形渲染管线中,Vertex Shader 以顶点为单位输出属性,但这些属性并不会直接传递给 Fragment Shader。渲染的核心几何单位是三角形,顶点输出的属性实际上被记录为三角形的顶点属性。在光栅化阶段,GPU 以三角形为单位生成 fragment,这里的fragment可以被理解为根据屏幕像素,把一个由线组成的三角形打散为碎片,每个碎片中的属性是根据三个顶点的属性进行插值得到(重心坐标插值,如 UV、法线等)。Fragment Shader 实际接收到的是每个 fragment ;对于不需要插值的离散数据(如 material ID),可使用 flat 修饰符使整个三角形内的 fragment 接收相同的值。

从这个角度上看,就能很容易的理解深度测试的机理:实际上对不同三角形上的fragment,是可能对应到同一个屏幕上的像素的。但实际绘制的只有一个,因此就需要对他们进行取舍。直观的,我们可以在fragment执行完毕之后做检测,根据z-test结果剔除深度比较大的fragment。当然,在fragment之前我们就已经能够得到有关fragment的信息,因此在这里就可以做z-test(Early-Z),使部分fragment根本不需要进入shader。

Early-Z 的本质是“用深度尽早淘汰不可见 fragment 以避免无意义的 Fragment Shader 执行”,它是现代 GPU 性能优化中最关键的机制之一,但会在 fragment 存在不确定性(如 discard、写深度)时退化为 Late-Z。


有关渲染方程的理解

在实时渲染的方程中,Lo = Li * BRDF * visibility。注意这个渲染方程实际上对应了3d空间里的一个点,但一般都是为了处理一个fragment的。

Li项(incident lighting 入射光线),也就是代表不同方向上对应光照的强度(辐照度 radiance)

Environment Lighting based on IBL 是一个工程上近似,防止逐像素积分的做法。本质上是:假设光源在无穷远,通过采样一个cubeMap或者一个球来得到Li项。同时通过预计算处理,加速积分。

在 IBL 中,原始环境贴图表示的是方向相关的入射辐射度 LiL_i。为了避免在 fragment 中对渲染方程进行实时积分,实时渲染并不直接使用该贴图,而是通过预计算将积分拆分:对 diffuse 部分直接存储 Li 与 cosine 项卷积后的积分结果;对 specular 部分则将环境相关的积分结果存入预滤波环境贴图,将材质相关的积分结果存入 BRDF 查找表,从而以查表方式近似完整积分。当然在这个过程中,默认含有visibilty = 1的假设保证这一项不影响积分加速,同时通过拆分保证把环境项和材质项解耦。

BRDF:BRDF 描述的是:描述入射方向 ωi\omega_i 的能量,有多少会被反射到出射方向 ωo\omega_o

这一项是和物体的材质属性相关,在物理上很复杂,但在Cook–Torrance microfacet BRDF就表现为metallic,roughness这些参数,也就是一个高度参数化的物理近似模型。

visibility是决定光源可见性的一项。考虑到不是所有方向的光源都是完全可见的,因此这一项和遮挡有关的项也组成实时渲染的一部分。


Q:shadow mapping的问题

Z标准问题

  1. 透视投影的projection matrix会把三角形往远平面推,导致这里的Z值和实际世界空间的距离不一样。因此在fragment shader使用shadow map的时候,需要统一使用Z或者世界空间距离。

Shadow acne

  1. Shadow acne 的本质在于 shadow map 使用单个离散深度(一般为这个区域内的最小深度)来近似表示光源视角下的一块连续几何区域,而该区域内真实表面的深度是连续变化的。当 fragment 从相机视角重投影到光源空间并与 shadow map 中的深度进行比较时,由于两次光栅化与数值精度差异,同一表面上的不同 fragment 可能得到略大于或略小于该离散深度值,从而导致部分 fragment 被错误地判定为被自身遮挡,形成自遮挡。
  2. 可以想象,假如光线的方向近似于平面平行,那么一个阴影像素对应的实际空间范围将会非常巨大,在这种情况下,会出现极其严重的问题。反之,垂直的情况下问题较小,因为相对来说一个像素的覆盖范围变小了。
  3. 解决方式:bias容差,容忍小范围内的自遮挡问题。这个bias可能根据光源角度等参数变化,比如更斜的光的bias可能就更大。问题:detached shadow(不接触的阴影,又叫peter panning):源自bias问题。bias如果比较大,可能误伤某些离得确实很近的物体,导致阴影和物体断开。也可以叫阴影悬浮。工业界的处理一般就是找一个合理的bias。

4. aliasing走样,源于shadow map的分辨率有限,因此会出现不可避免的锯齿边缘。


Q:什么是PCF(percentage closer filtering)?什么是PCSS(percentage closer soft shadow)?

从实践的角度来讲,PCF一般在fragment shader里面用到。此时已经有了shadow Map和采样点,但我们不是直接从这里取出一个非0即1的visibility,而是采样周围的很多点,然后平均这个visibility,得到一个0-1的中间值(假设在边缘附近)。

注意这既不是后处理(直接处理渲染完毕图像上的阴影边界),也不是改shadow Map(这个图代表深度,本身的物理意义使其不能被直接修改)。

这项技术最开始被用于实现AA(anti-aliasing 抗锯齿),但后续发现随着filter core的增加,阴影的边界变得越来越“软”,因此这项技术就成为了软阴影的实现方式。

接下来的一个关键问题是:filter core的大小选择。显然,core越大就越软。那么现实中的软阴影大小由什么决定呢:首先,是光源本身的大小,光源越大,造成的半影区面积显然也越大;其次,是blocker距离Receiver的距离(或者从下图来讲,是个相对距离),这个距离越大,半影区的面积也越大。

这就是PCSS,也就是PCF在软阴影问题上的应用算法:

The complete algorithm of PCSS

  • Step 1: Blocker search
    (getting the average blocker depth in a certain region)
  • Step 2: Penumbra estimation
    (use the average blocker depth to determine filter size)
  • Step 3: Percentage Closer Filtering

问题:这个certain region本身又是什么呢?

他的物理意义是:在一个shadow Map的区域范围内,可能存在blocker遮挡从shading point到光源面的连线。这个范围取决于光源大小,receiver深度和light深度。

注意,大部分的深度存储(比如shadow Map)都是0-1,但这里存的默认不是线性深度,而是经过了projection压缩之后的非线性深度。在距离远平面越近的地方,精度就越差。在实战中需要注意这个问题。


Q:VSSM(variance shadow mapping)是什么?为什么出现?

在PCSS种,需要两次进行类似卷积的多重采样。step1中需要采样得到average blocker depth,step3中需要采样得到最终的visibility。显然这两步采样的消耗非常大。

一个常见的想法是,我们使用稀疏采样的方式,减少sample的数量。但这里就会导致另一些问题,比如单张图上的noise和连续渲染中,noise导致的fliker(抖动)问题。因此稀疏采样一般都配合图像降噪工作出现。这就是另一个大块的话题,denoisy去噪。

那么VSSM对这个问题提出了怎样的解决方式呢?

首先可以直观的想到,如果框选区域内的深度概率分布近似符合正态分布,由于第三步实际上在做的任务,类似寻找shading point深度在整体深度中的百分比,因此如果知道了这个区域内的深度概率分布的均值方差,就可以对这里进行快速计算。

均值(mean):直观的想到可以利用hardware mipmap(快速,近似,范围查询)的形式来存储一个固定区域(这个由于硬件支持的原因,实际非常快),但由于mipmap只能是正方形,需要通过另外的数据结构summed area tables(类似一个二维前缀和矩阵)来做任意矩形。

方差(variance):方差 = 平方的期望 – 期望的平方,期望平方易得,而平方期望需要另外存一张类shadow map的图,里面存放的值是深度的平方。也就是空间换时间。如果深度本身不是特别高精度,可以写在一个float8里面(RGBA里的一个通道),那么平方可以只占用另外一个通道。

此时我们已经知道了mean和variance,此时考虑到计算正态分布的复杂性,VSSM抛弃了正态分布假设,提出了另外的一个近似,即通过切比雪夫不等式来近似(把不等当成约等来看)

那么另一个问题:上面解决了step 3:PCF的近似,但step 1:blocker search(计算范围内平均遮挡物深度)仍没有解决,上面计算出来的均值是范围内所有深度平均,而不是遮挡物(z<t)深度平均。

我们假设这一块区域内的均值和方差是已知的(同step 3的解决方式一样)

对这个问题的解决方式涉及两个关键假设:

  1. 对x>t的比例,使用切比雪夫不等式估算出一个值
  2. 对于Zunocc(也就是非遮挡物的平均深度),直接使用t(一个hack,假设所有非遮挡物的深度都是shading point的深度,考虑到大部分阴影接受面都是平面,这样的近似也有一定的道理)。

可以看出VSSM是一个为了解决查询的速度无所不用其极的方法,做出了大量大胆的假设和近似,把原本大量的计算需求简化成平方和方差的查询计算。

但是,从业界的角度上看,由于降噪技术的进步,特别是空间和时间尺度上的方法(TAA),实际上目前更多仍然是使用稀疏采样的PCSS来做阴影。


Q:Moment shadow mapping是什么?为了解决什么问题?

VSSM的主要问题是:切比雪夫不等式的估计不是准确的。假设实际的结果和估计结果相差很大的话,就会出现阴影区overly dark或者overly lighting的问题。

常识:人们通常能接受一个阴影变得异常的“黑”,但不太能接受变的异常的“白”。这就是“Light leaking(漏光)”问题。

因此Moment shadow mapping就是为了解决“切比雪夫不等式对分布的描述不准”的问题。通过的方式是保留除了x和x平方之外的更高阶矩,从而能够更好的描述分布。当保留到四阶矩的时候,效果很接近PCF。

问题:由四阶矩重构原分布的计算过程,是非常复杂的,会导致perfomance问题。


Q:Distance field soft shadow是什么?好处?SDF(signed distance field)?

首先,distance function指的是:空间内任何一个点,到一个物体表面上的最小距离,记作这一点的distance function 的 value .

signed实际上是指,假设我们通过某种方式(比如规定物体的内部value为负值,外部value为正,就可以把unsigned变成signed,相当于使距离场有了“方向”, 也就是“有向距离场”)

一个例子:对于一个运动的物体边界,方便的进行线性插值得到中间的运动状态

距离场的应用:

  1. Ray marching (sphere tracing) to perform ray-SDF intersection
  • 光线追踪的主要任务是光与物体表面的求交。而SDF可以用来表示物体表面(区别于显式的物理表示)。
  • 这里面的核心思想是:对于一个点,它对应的distance function表示,在这个距离范围以内,绝对不存在一个需要考虑的物体表面点,也就是所谓的“safe”。在ray tracing里面,这相当于一个跳步优化。
  • 对于光线的起始点,查询到SDF值,然后就可以直接让光移动这么一大段距离,中间不需要做任何的查询。然后从下一个起点又可以重复这一个过程。这就是所谓的 ray marching(光线步进)。
  • 一般到SDF比较小(准备查询),或者移动距离已经很远(准备丢弃),就终止。

2. Use SDF to determine the (approx.) percentage of occlusion

  • the value of SDF -> a “safe” angle seen from the eye, 即不可能被遮挡,与visibility正相关
  • 因此从shading point出发向着light进行类似ray marching的工作,然后找到一个近似的safe angle(一般是几个里面最小的),然后通过近似三角函数(指把arcsinx近似成kx)来得到 visibility。用k来控制软硬程度。
  • pro:Fast(在光线追踪的体系下是很自然的),high quality
  • cons:SDF的生成(precomputation),存储(heavy storage),不能处理形变,artifact(走样/穿帮)

SDF表示物体表面的问题:纹理/uv问题,由于缺乏原生UV坐标,导致上色,拉伸等问题。


Q:Environment lighting是做什么?实现方式?

环境光照的基本假设:光照无限远,不考虑遮挡。因此两个场景中位于不同位置的物体,实际接收相同的环境光照。

cubemap vs. sphere: 工业界一般用cubeMap,虽然占用大一点,但渲染简单,适合旋转,图像质量高

ps:对于render equation来说,由法线定义正半球,Li * brdf * cosθ仅对正半球积分。

Image-Based Lighting(IBL)

首先,IBL完全忽略阴影,因此visibility恒为1,渲染方程里面这一项忽略。

其次,对于BRDF项来说

  • 如果brdf是glossy(高光/镜面):说明support(支持域,这里可以就理解为积分范围)很小,意味着它只接收来自极窄方向范围的光。
  • 如果brdf是Diffuse(漫反射):表面非常光滑(Smooth),这说明它的brdf项在不同的光照方向上非常均匀,近似为常数。

如下的常用近似在什么情况下准确?g(x)的support比较小,或者比较smooth

因此可以使用这样的拆分把渲染方程拆成两项:

这个Li项可以很容易的被解释成:在一个范围内Li的均值。因此这一项可以被预先计算,类似于mipmap的形式,预先存储一批mipmap,比如1级mipmap就可以查询2*2的filter结果,2级查询4*4,想查5*5只需要对2级和3级的对应结果进行三线性插值就可以了。这个查询的过程本身成本很低。

补充:有关BRDF lobe,roughness以及IBL Li项的工程实现

在物理材质模型(如 GGX 分布)中,Roughness 直接控制了高光波瓣的“宽度”:

  • Roughness = 0:Lobe 变成一条线(完美镜面反射),积分范围是一个点。
  • Roughness = 1:Lobe 变成一个极宽的“胖气球”,积分范围接近整个半球

因此,我们对应的积分范围实际上是由roughness决定的。

能直观的想到一种思路:我们预先把卷积核的大小和roughness的对应表存储好,然后在fragment shader里面,根据Mipmap和三线性插值进行查询。

但这里有一个问题:这里的mipmap虽然也是2层级,但是并不能简单的做2*2平均的下采样,而是应该对一个roughness值对应的BRDF lobe进行蒙特卡洛积分,然后使用自定义的 Compute Shader(计算着色器) 手动写入 Mipmap 的一层。

显而易见的问题:Roughness和Level层级的映射是什么?

ue4的实现中,使用了简单的线性映射 LOD = Roughness*(MaxMipLevel – 1)

但由于Roughness实际在用的时候是要平方(α = Roughness^2),因此物理上的模糊程度(α)随实际roughness的增加是2次的,也符合我们希望在光滑区更细致的初衷(人们对光滑的东西更敏感)。

为什么还要用mipmap:因为硬件层面线性插值的支持,在fragment shader里面调用线性插值的时候,用mipmap比手动更快。

Q:Level 1 分辨率变小了,能存得下复杂的卷积结果吗?

A:当Roughness增加时,反射本身就会变得非常模糊。模糊意味着高频细节消失,剩下的都是低频信号。所以,分辨率的下降(硬件特性)恰好匹配了模糊程度的增加(物理特性)。

补充:有关插值和mipmap

  • 为什么要插值:因为uv是连续的,而texture是离散的。因此不是说采样点落到哪个像素就是什么颜色,而是根据这个采样点距离最近的四个像素中心对应的像素颜色,根据这个据中心距离进行双线性插值,得到采样颜色。
  • 也就是说,纹理的本质是一个离散的点图,每个像素实际上只对应一个点(中心),其他位置都是插值出来的。
  • mipmap的本质:是取四个像素的中心点进行线性插值,得到一个新的点。也就是说,它的本质是在加速一个“范围内均值”的查询。
  • 三线性插值:在双线性插值的基础上,增加了mipmap层级作为一个新的维度,已适应非2倍数的范围查询。

对brdf的再理解:

Fresnel 项,决定了“反射率”,一个非常复杂的函数,通常用Schlick近似,也就是R0 + (1-R0)*(1-cosθ)^5。可以看出这个近似只和入射角度和R0(这个东西就是metallic)有关,也就是决定高光能量的占比。

NDF项,主要和微表面分布有关,h是一个查询的值。通常使用beckmann分布来描述(假设微表面的高度分布遵循高斯分布),核心参数是roughness(也就是α)。注意我们刚刚提到的近似,这里的roughness实际上就是物理上的roughness,等于实际我们参数中roughness的平方(也就是算式算了一个参数的4次方),当然这都是实现问题。

工业界中对于NDF更经常使用GGX分布。

G项:能量守恒,相当于小弟,主要影响微表面相互遮挡的情况

对于IBL的BRDF项,可以把Fresnel项显式的提出来,也就是完全把积分的结果转变为根据roughness和cosθ的函数。对于一个预计算来说,就是两张二维的表(如果我们考虑多通道,那实际上可能就是一个纹理的两个通道)


Split Sum:目前为止,我们需要的结构如下:

  1. Prefiltered Env Map (Cube Map):用于 Specular IBL(镜面反射)。实际上是一连串原图的mipmap,里面手动塞入了不同roughness(一般是线性映射,用的时候平方)对应brdf lobe卷积出的结果。
  2. BRDF LUT (2D Texture):用于修正 Specular IBL(解决菲涅尔和几何遮蔽积分)。一般是一个纹理的两个通道,R0(也可以是F0)项后面的一般叫Scale,另一项叫Bias

由于最后的结果在工程上是查表求和,因此这种方法叫做split-sum法

这种方法也是Unreal 4的PBR实现方案基础。

注意:这里更多是解决了镜面反射的效果。也就是让光滑平面低成本的反射周围环境。为了漫反射颜色,还需要球面谐波 SH。UE4用 3 阶球面谐波(Spherical Harmonics)来实现。

当然,怎样获得一个场景的environment Map又是另外一回事了。


Q:环境光照的shading有了,那么shadow怎么处理呢?

A:非常难

  • 因为环境光照约等于“many-light”问题,因此成本线性于光照数。
  • 从采样的角度上看,渲染方程中的vis项不可知,无法被提取出来(这里解释了AO为什么叫做“环境光遮蔽,因为它就是为了解决这里对于一个shading point来说,vis项无法提出的问题”)
  • 当然,AO的大前提是 constant environment lighting,也就是环境光不能是任意的,需要在各个方向上都等于一个常量。显然,这是一个不太严谨的近似。

工业界一边都是取环境光照中最亮的光源(one or more)做代表(比如太阳)来产生阴影。

那此时为了完成这个环境光照下的阴影问题,自然的引入了PRT(Prefiltered Radiance Transfer)


Q:PRT是什么?做什么?好处?代价是什么?

首先需要知道一些基本的数学概念:

  • Filtering和图片:对于图像来说,由于每个像素实际上是数值,因次衡量数值变化的剧烈程度也可以称之为频率。从这个角度,一张图片可以被转化为一张频谱。
  • 此时Low-Filtering相当于把高频变化的部分滤除,也就是去掉了细节,保留了部分相对模糊的部分。
  • 实际上卷积也可以达到类似的效果。直观的讲,卷积是在空间(Spatial Domain)上平滑了信号,在频域/时域(Frequency Domain)上看,相当于做了一个乘积(乘上卷积核对应的频谱)。
  • 空域卷积=频域乘积

核心数学工具:SH球谐函数(Spherical Harmonics)

  • 本质:一系列的二维基函数,变量定义为球面上的方向。
  • 为什么球面上的方向是二维:θ + φ (没有 r/模 的三维向量)
  • 因此,实际可视化的SH为空间中的一个物体(实际只有表面)

球谐函数有一个非常好的性质(这个性质源于球谐函数是一组标准正交基),即在求取某一项SH的系数的时候:

ci=Ωf(ω)yi(ω)dωc_i = \int_{\Omega} f(\omega) y_i(\omega) d\omega

我们可以直观的理解为,系数就是f在某一个SH上的投影,因此简单的点积(当然是连续积分)就可以得到这一项系数。

理论上这个解析和恢复是无限阶的,但通常我们恢复只会用有限阶。

这里有一个非常有趣的点:实际上这个过程,就相当于我们把高频(对应高阶SH)对应的信息给滤除了。也就是天然的低通滤波,非常适合用于漫反射这种非常低频的信息。


对于不考虑Shadow的shading part:

Li是一个任意的球面函数,但如果只考虑diffuse BRDF项的话,这一项是非常smooth(低频)的,研究证明,可以用三阶的球谐函数来近乎完美的近似diffuse BRDF项 。

对于一个product integral来说,Li和diffuse BRDF实际上在求结果的时候只取决于低频。既然diffuse BRDF本身就非常低频,那其实也不用使用非常高阶的SH来描述Li项了。研究证明,使用三阶的球谐函数表述Li,误差仅在1%左右。

因此,我们可以只通过存一个vec9,来存储一个很”diffuse“,但够用的environment lighting,来计算漫反射环境光照。

在写到这里的时候,突然意识到一个一直以来的误解:BRDF一直是两项,diffuse项和specular项。cook-torrence模型和metallic/roughness属性都是属于specular项的,上面提到的split-sum技术也是为了解决specular项的问题。

但是其实diffuse项也是影响场景的关键因素,不能把这当成一个常量。


Q: PRT的思路:

Li * V * BRDF

假设BRDF是diffuse的:直接化成常数ρ提出来

Li可以化成SH的形式,把提出来的Bi(球谐函数分项)塞给V项,对于V来说约等于在某个方向上的投影,也就是一个数。这一项就可以预计算。

最后化成一个向量点积的形式。

代价:假设V不变,因此场景是不能动的。

好处:光照是可以切换的(只要都预计算出li系数项),而且由于SH函数优秀的旋转性质,可以比较容易的得到旋转光照之后的li系数项。

注意:由于这里是diffuse的,SH理论也可以3阶

另一种理解方式:直接把Li项和light transport项(也就是后面的一大堆,由于brdf diffuse, 可以只关注V)都拆开为球谐函数的形式。变成一个n平方的累积之后,由于SH交叉项为0,且对应项为1,因此此时退化为一个O(n)的点积,等价于上面的形式。

那么如果BRDF是glossy的,就会更复杂:

glossy物体的复杂性,体现在BRDF项不能被当作常数提取,它和观察方向(也就是出射光方向)是相关的。所谓高光/specular的语义也是这样,从不同的角度观察一个受同样入射光的物体,得到的结果不一样。

对于glossy的物体来说,Ti项就不能只是一个投影的向量(n维),而应该是投影的矩阵(n*n),存储来自不同方向上的light transport 在SH上的投影(SH系数)。


GI!(Global Illimination 全局光照)

在实时渲染中,我们需要解决的全局光照问题,实际上指的是“one bounce”问题,也就是所谓的”多一次弹射“作为间接光照的部分。

RSM(Reflective Shadow Map)

思考:实际上在对于一个shading point考虑光照的时候,它本身并不在意其接收到的来自某个方向的光,是直接来自光源的,还是来自其他物体反射的。也就是说:

全局光照,就是把所有受到光源直接照射的物体表面,当作新的光源处理(次级光源 secondary light source 相对于 primary light source)

需要处理的问题:

  • 1. 哪些表面会被直接照亮(即作为次级光源)
  • 2. 不同表面对shading point的贡献程度

对于问题1,我们的shadow map可以提供光源视角的图景,帮助解决“哪些是次级光源”的问题。但有一个关键的困难是,假设这个次级光源的radiance是出射方向有关的,那就非常麻烦了,因为在这里我们的观察角度不固定(shading point代替了camera的位置)。

所以这里做出了一个假设:所有的reflector 反射物都是diffuse的。即radiance视角无关。这样我们就可以把它当作一个简单的光源来处理。注意,此处并没有假设接受物(也就是shading point)也是diffuse的。

核心推导如下:q代表reflector p代表shading point。在这个问题里面,把次级光源的光照直接表述为brdf * 能量/面积的形式,这样就能在shading point计算的时候把单位面积消掉,达成只存取能量的优化。

由于这个公式的V项一眼难算,因此RSM是不考虑V项的

当然,对于任意的shading point考虑每一个shadow map上的点的贡献,在大多数情况下仍然是不可接受的。因此就需要做一些聪明的采样策略,找到哪些点可能对shading point有贡献。

在RSM里面,是把shading point的世界坐标投影到shadow map上,然后假设这个shadow map上离得近就是世界坐标近。这里有点像是PCSS对于shadow map找blocker和filtering的那个过程。然后开始调参!

通过这种做法,把对于单个shading point的次级光源数减少到了400个左右。

这里的Shadow map存储了Depth, world coordinate, normal, flux, etc这些信息

一般在游戏中,解决手电筒相关的间接光照会用RSM(可能因为电筒本身只照亮一小块区域,因此相当于间接光源的数量也有限制)。

Pros:非常好写,遵循shadow map的流程

Cons:

  • 有一个直接光源,就要多一张shadow map
  • 反射物到shading point的V做不了,真实感问题
  • diffuse 反射物, 世界坐标与shadow map的距离近似
  • sample rate/quality tradeoff

Light Propagation Volumes(LPV)

核心思路:把3d空间体素化为一个网格结构,然后对于每一个次级光源(注意这里需要先通过类似RSM的方式,得到所有的次级光源以及其对应的Radiance分布),通过“扩散”的方式传播光源

要避免一个思维误区:光是沿直线传播的。实际上在这里的光,被表示为一种辐射场的形式,也就是说,简单的通过一个向量点乘固定方向,就可以得到某一个方向的辐射分量。可以很简单的想到,不同方向上的分量重新加起来就是原始起点的场。

也因此,传播的过程和“光源起点”和“光线”没什么关系,主要是场在进行流动。在这里场表示为2阶SH。

问题:

  • 1. 如果有小于一个格子的面片,会有light leaking
  • 2. 无限制的细分网格,存储和扩散计算都有压力

VXGI(Voxel Global Illumination)

实现思路:依然把场景voxel化,然后对于每个小格子,记录其接受到的incident lighting lobe和其normal lobe,这样就可以计算对任何一个shading point来说,这个次级光源的光照贡献。

另一个重要思路是:对于每一个shading point来说,从camera出发发射一个camera ray,打到shading point表面反射。如果是glossy,则反射出一个圆锥;diffuse则反射出一个半球,这里解决为多个圆锥的拼合。理论上来说,考虑这个圆锥覆盖的,所有小格的光照贡献,就能够计算间接光照。

为了加速这个过程,在初始化的时候,会为场景的网格设置hierarchy。考虑到圆锥是上窄下宽的,因此初始的时候找最小层级的voxel,越往后可以越找高层级的voxel,类似RT的包围盒?

Pros:效果非常好,因为是逐像素处理的技术

Cons:慢,以及voxel化成本


GI in Screen Space

所谓”屏幕空间方法“,都是基于已经渲染出的图像结果的“post processing”!得到的信息仅仅是在camera的可见范围。可以想到,这些方法对GI的处理,必然是高度近似的。对立的概念是“image space”,也就是像RSM这种,从shadow map中获取信息的方法,和“world space”,LPV/VXGI


SSAO(Screen Space Ambient Occlusion)

Q:AO为了达到什么样的画面效果?为什么谈GI会突然转到AO?

A:AO是让物体和物体之间的接触部分变暗的技术(所谓的 contact shadow),是为了体现物体的相对关系,避免悬浮感,体现立体感。因为AO是很容易实现的技术,它可以被视为GI的一个近似(contact shadow本身就是全局光照造成的一种现象)

SSAO 的 key idea:
1. 在我们不知道对于一个shading point来说,它接收到的来自四周的间接光照的具体的值的情况下,其大胆的假设,所有方向的值为一个相等的常数(调参地狱?)

知识点回顾:在Blinn-phone 光照模型中,同样做出了这样的假设。最终导致结果项中出现了一个环境光照diffuse固定项,直接强硬的把场景提升一个固定的亮度。

2. 但SSAO毕竟是AO,它虽然假设常数,但考虑visibility,也就是,不是所有方向的这个常数,最终都产生了实际的光照效果(要乘上visibility)

3. SSAO还假设,所有物体的材质都是diffuse的。

如下面的公式推导,可以得到:对任何一个shading point,其能得到的间接光照的值,等于一个visibility对于半球的加权平均值,乘上一个精心调制的常数(光照常数 * diffuse brdf 常数)

为什么这里面带着cosθ一起积分?因为这两项的乘积有明确的意义:dω本身代表立体角,也就是单位球上的面积。它乘上cosθ实际上就是投影到单位圆上的面积(所以上面ka的分母是Π:圆面积)

Q:怎么去实时计算ka(也就是 visibility项的加权平均)?特别是在screen space?

首先:在这里计算visibility的时候,不能假设光是来自无限远距离(因为反射光肯定不是无限远)。因此在 visibility 去 trace 的过程中,肯定也是需要一个固定的距离,超过这个距离的就不予考虑(想象一个封闭的屋子,如果无限远,则visibility肯定为0).

这就产生了trade-off:到底这个“有效距离”是多少,要根据实际情况而定。

更重要的问题是,在screen space不好做ray tracing 对此 SSAO做出了一个大胆的假设:

首先:对于一个shading point,向它周围的一个整球的范围内采样一批点。对于这些采样的点,可以根据深度图中的深度信息(会标记相机方向的最小深度),判断它是在物体内部还是在物体外部。当然,在图二这种情况下可能会出现误判,但整体不是很影响。(课上有一个石凳的例子说明了这种情况有时候会产生不自然的AO, 解决这个问题的方式叫HBAO,会考虑法线信息和深度衰减,而不是挡住都算)

其次考虑到只有法线方向上的那个半球的信息才实际对ao有效,而由于历史原因,SSAO那个时候还不能同时存储深度和法线,因此做了这样的判断:只有红点的个数过半,才考虑AO。红点不过半,visibility项显然是全1;红点过半,那把减去整体个数一半的红点个数再和绿点去比较,得到visibility项。图2是0.6,图三是0.2。

当然,这种做法只是平均,而不是和cosθ合在一起的加权平均。但工业界不太影响。


SSDO(Screen Space Directional Occlusion)

SSAO假设 incident light 为 constant,但这种做法实际上只能让接缝处变暗,并不能做到color bleeding的效果(两个diffuse的物体把自身的颜色互相传播)。

SSDO的思路类似path tracing:从shading point 发射出很多条光线,如果打到了obstacle,说明有来自这个方向上的反射光线;如果打不到,说明只有直接光照。

从这个角度上看,由于SSAO的constant假设,其实际上是用考虑直接光照的思路来考虑间接光照,假设间接光照都来自于环境光(较远的地方)。与SSDO正好相反,打不到说明有间接光照,打到了说明被遮挡住。而SSDO则考虑近处的反射。理论上,应该两个一块做(但好像没有应用实例)。

Pros:质量还不错,速度可以接受

Cons:

  • screen space丢失信息,camera看不到的面,根本就不知道存在,因此也就不贡献shading
  • 只能考虑一个小范围内的间接光照(由于其也使用类似SSAO的,拿半球内采点当作ray tracing的近似,因此其不能处理稍大范围内的反射光/间接光照)。

SSR( Screen Space Reflection / Ray tracing)

  • Raymarching: SSR shoots a ray from the camera, reflecting it off a surface, and then “marches” (traces) this ray across the screen’s depth buffer to find an intersection with another object.
  • Data Usage: It relies on the depth, normal, specular, and color buffers to compute the reflection.
  • Sampling: When a hit is detected, the color from that part of the screen is applied to the reflective surface

Hierarchical tracing:类似mipmap的计算方式,不过计算的不再是平均值,而是深度最浅值。可以很容易的想到,当ray不与high level的深度块相交的时候,它同时也不可能与这个high level下对应的low level相交。通过这种方式,可以跳过大量的计算块。

实际的计算中,采取一种“试探”的形式来完成这个求交的过程。大体上是:从最小的level开始步进,假设是一个像素块,然后步进2,4,8,直到相交。然后对于相交的那一块,通过逐级二分缩减的方式,最终找到对应的那个最低level的块。

SSR的问题同样也是screen space的问题:没有屏幕空间之外的信息,会导致反射细节的缺失。比如,实际反射计算只考虑深度图对应的那一层壳,因此就会有大量的没有被看到(但理论上应该被反射)的部分细节缺失。


PBR and PBR Materials

microfacet models and Disney principled BRDFs(both not PBR)

Microfacet BRDF

简单复习传统BRDF:
Fresnel:反射率,非金属的物体垂直基本为0,接近掠射时为1。特别metallic的物体垂直的时候就很高。大概反映了在某个角度下,材质反射能量的能力(可以认为0是完全不反射,1是完全不损耗)

一般使用Schlick近似一个从R0到1的一个变化轨迹(metal的R0接近1,非金属的接近0)

NDF(Normal Distribution Function)项:微表面法线分布,表示一种趋势。直观上,可以理解为材质是否smooth(法线是否集中)。理论上,如果平面是理想的,那么其法线的方向应该全部朝向半程向量h。

NDF项最终决定了物体是glossy(最极端的就是specular )的还是diffuse的。

NDF项的近似:Beckmann,GGX


暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇