欢迎来到这里

欢迎来到FocoL的展示架,如你所见本人将会在这里更新一些奇奇怪怪的东西,包括但不限于

图形学作业

虚幻4作品

游戏评测和体验

哲学哲♂学漫谈

自己写的小故事

键政暴论

以及其它的我没想到的东西

总之就是什么都会有,欢迎大家来玩顺便脑内喷我

评论功能和桌宠预计很快就会实装,到时候可以来调戏桌宠(谁想看大男人发牢骚

祝你武运昌隆,El Psy Congroo~~

屏幕空间中的全局光照

Screen Space Ambient Occlusion(SSAO)

大名鼎鼎的SSAO,游戏设置的常客,crytek出品,画质有保障(误)

屏幕空间指所有的信息均来自屏幕

SSAO有以下假设:

1.我们不知道间接光照是什么,但假设间接光照是一个常数。

熟悉吗?这实际上就是blin-phong模型的假设。

2.不是所有方向都能接收到全局光照

3.认为所有物体都diffuse

还记得我们第一节课说的近似相等吗?

那时候我们就说这个东西AO会用,现在是时候伏笔回收了。

说起来前面这项是不是错了啊?为什么上下都多了个$\cos{\theta_i}$?

那是因为$\cos{\theta_i}d\omega_i$本身是一个有明确定义的微元,如下

image-20210516174819240

所以这么拆也是对的

来看左边,分母实际上是定值$\pi$(算不出来的自行把$d\omega_i$拆成$d\theta d\phi$在正半球上二重积分),我们可以把这个式子记作Ambient项$k_A$,则有

再看左项,对于全局光照来说$L_i$是一个常数,对于diffuse的BRDF也是个常数,所以这一项简直是白给,化简后可得$L_i^{indir}(p)·\rho$

全局光照你随便定一个颜色就好,BRDF的albedo你也随便定一个颜色就好,所以右边突出一个随便定

重要的是$k_A$

在屏幕空间我们怎么求得visibility?

第一个假设:我们在着色点周围随机撒一些点,判断这些点哪些在物体里面,哪些在物体外面

第二个假设:只要一个点的深度大于深度图记录的最小深度,我们就认为它在物体里面

如图所示

image-20210516175925249

你可能觉得这难道不应该在半球上考虑吗?与法线方向相反的半球有什么用?

问得好,但是当时我们还不能在屏幕空间上获得物体的法线信息,所以这里我们再次认为:只有被遮挡的点来到了一半以上,才开始考虑AO的问题

我们就可以用这种方法近似visibility项

有采样就有噪声,有噪声就有降噪,懂得都懂

现在我们可以获得法线信息了,自然会指采样半球,这种改进方法叫做HBAO(Horizon based ambient occlusion),且这种方法只考虑一定范围内的遮挡关系,不会有SSAO里面一些奇奇怪怪的遮挡

Screen Space Directional Occlusion(SSDO)

SSDO是对于SSAO的提高,比起SSAO仅考虑环境光是常量,SSDO可以结合RSM考虑环境光

SSDO的实现和path tracing非常类似

1.对于着色点p,发射一根随机射线

2.如果射线没有打到障碍物,进行直接光照,反之进行间接光照

相比SSAO,SSDO做了完全相反的假设,SSAO认为物体被遮挡时无法接受到环境光照,而SSDO认为物体被遮挡是才接受环境光照,也就是说,SSAO假设环境光来自非常远的地方,SSDO假设环境光来自非常近的地方

但凡是渲染我们肯定要解渲染方程

把方程拆成可见和不可见两部分,则

我们关心第二部分

你用RSM也好,其它方法也好,一个点对着色点p的环境光贡献一定是可计算的,核心是怎么做遮挡的查询。

我们仍然不想做射线trace,太慢了,这里我们仍然使用HBAO的撒点方法。

SSDO是比较轻量级的算法,工业界应用广泛

但是它仍然无法逃离一个问题:只要是我看不见的,我什么都不知道,且只能处理小范围GI

SSR(Screen Space Reflection)

SSR同样是屏幕空间的全局光照技术,但是它是真的在屏幕空间做光线追踪。

SSR的关键在于反射,我们要想知道一个点的反射信息,我们先要知道它都能反射什么,一个基于现实的观察是:反射的东西大多都能在屏幕空间显示出来。

image-20210517155244867

基本的SSR便是基于屏幕空间已经渲染出来的东西做的镜面反射

具体是怎么做的?

首先我们可以从屏幕空间射出一道光,根据法线和深度信息,可以做出一次反射,反射出一根(视场景粗糙度等而定,可能有多根)我们关心这道反射之后的光会和场景交于哪个点。

最简单的考量就是我每次对于光线取一个步长往前查询,如果查询到深度比场景深度深,则发生了相交,如图

image-20210517160019036

问题是步长该有多大,甚至来说如果能动态决定就更好了

还真的有动态决定的方法。

还记得MIPMAP吗?我们先对场景深度做MIPMAP,不同的是MIPMAP上一个像素不再记录上一层几个像素的平均深度,而是深度的最小值。

这个加速结构就像path tracing里的层次结构一样,如果我连底层极深度最小的都碰不到,我怎么可能碰到这个层级这个像素覆盖到的所有像素呢?这东西本质是一个bbox。

所以查询的方法就是:首先查询一个深度,如果没有碰到,那么加大MIPMAP层级,继续查询(步子更大),如果碰到了,减小MIPMAP层数(步子变小)

这样我们就实现了trace

具体的着色怎么办?其实我们完全可以把path tracing的方法拿来用(都是光追谁看不起谁)

SSR是很好的实现屏幕空间反射的方法,当然它也有一些问题,集中在屏幕空间这一点,蒙特卡洛方法什么的都好

比如下面这个

image-20210517161833176

这个方法可以快速做glossy和specular的反射,这也是为什么很多以前的游戏看起来画面那么油(不diffuse,快)

Glossy情况下的PRT

既然是图形学,那么我们先摆一个渲染方程在这

现在我们认为p点已知,同时把渲染方程写成light和light transport形式,如下

注意到light transport项因为BRDF的关系,同时和入射方向和出射方向有关,意味着从任何一个不同的方向看,都会得出一组不同的向量,也就是说,PRT的结果现在是一个关于出射方向$\omega_o$的函数,如下所示

这样的话我们不如把light transport后的预计算结果视为一个矩阵,即

也就是把不同o得到的vector摆在一起

PRT的问题

1.SH不适合做高频(不是不能,阶数太多)

2.预计算也就意味着场景被固定住了

3.大量的预计算数据存储

Wavelet

Wavelet也是一系列基函数,其定义在一个图像块上,不同的小波定义域不同,下图所示的是Haar wavelet

image-20210515185627873

差不多得了开新东西开新东西

三维空间中的全局光照(Global illumination,GI)

传统的光栅化方法完全无法处理间接光照的问题,对于传统的Blin-Phong模型,GI通常用一个hack来解决,即增加一个Ambient项模拟全局光照,但是这种方法一点都不真实,我们还是需要一个全局光照的技术光追闭嘴

不是光追,咱还真的不能嗯用光线反弹,得用别的方法

我们可以认为每一个发生反射的表面都视为一个光源,那么全局光照可以理解为主光源和所有次级光源共同对场景作用的结果。

为了得到一个点的间接光照我们需要知道什么?我们需要知道以下两件事:

1.场景中又哪些次级光源,也就是说哪里会被光源照到,很显然Shadow Map就能告诉我们这些

2.每一个次级光源的贡献是多少?这部分问题的实质是解一个渲染方程

全局光照是不关心观察方向的,但是众所周知渲染方程没有出射方向就解不了,这个问题应该如何解决呢?我们提出一个大胆的假设:我们认为所有的反射物都是diffuse的,也就是说BRDF是常数,从哪个方向看都是一样的。这样场景中有哪些次级光源,次级光源从哪个方向上照射点p就解决了

Reflective Shadow Maps(RSM)

RSM的实现

首先回顾一下辐射度量学的三个基础概念

image-20210516010051643

我们可以将一个光源对p点着色的贡献的计算从立体角微元转化为光源面积的微元,如图所示

image-20210516010334978

这样渲染方程就被转化为了

$q\to p$指q点到p点的radiance,我们如何解的radiance的大小?

对于diffuse的情况,我们有结论$f_r=\rho/\pi$,而$L_i=f_r·\cfrac{\Phi}{dA}$,其中$\Phi$是光通量

把上两式带入,我们就消去了dA,实际上我们不用关心光源的面积

这样我们就知道了一个次级光源如何照亮着色点

同时我们还有一个观察:离着色点较近的次级光源往往会带来比较大的贡献,这里我们假设在SM上离得近的两点在实际距离上也较近,因此我们在采样SM上的次级光源的时候可以在近点增加采样次数,远点减小采样次数,通过类似的采样方案可以极小的减少采样的次数,带来效率上的提升

这种方法对于类似手电筒这种光源非常的友好,现在也经常被使用

RSM的特点

优点:

​ 1.很好实现

缺点:

​ 1.凡是SM有的问题它都有

​ 2.因为不算反射物到着色点的遮挡,看起来会不真实

Light Propagation Volumes(LPV)

思考下面这个看起来似乎不用思考的问题:在做间接光照的时候,如果我可以获得一个点从所有方向来的radiance,我们才能做间接光照,对吧?

LPV方法基于特别重要的物理事实,即radiance在传播的过程中总保持直线且不会改变。

LPV的要点是把场景分成3D的网格,每一个网格称为体素(Voxel),我们都知道间接光照的来源是场景中光的反射,现在我们想要知道经过类似的反射之后,场景中每一个体素内的光强是多少。

LPV的实现涉及很多的问题,接下来仅针对LPV的实现逻辑进行讲解。
LPV的实现分为以下几个步骤:

1.和RSM类似的,我们需要知道场景中哪些点可以作为次级光源

2.这些点需要被放到体素里面,学名注入(injection)

3.在三维的网格中传播radiance

具体来讲,对于第一步的次级光源判断,我们仍然可以用RSM,同样也可以靠采样来获得一系列次级光源

第二步注入,工业界的做法是生成一张3d texture来记录信息,对于每一个点我可以计算出radiance在每个方向上的分布,这是一个球面函数,可以用SH近似,一般在工业界只使用两阶

第三步我们对于每个网格,都可以计算从六个面传播过来的radiance,并使用SH近似,迭代若干次, 直到整个网格稳定下来。

这种方法听起来非常美好,那么代价是什么呢?

就比如它本质还是忽略了场景遮挡,所以是存在“穿墙照亮”的情况的(light leaking)

Voxel Global Illumination(VXGI)

VXGI和RSM一样是two-pass的算法,他和RSM的区别是什么呢?

首先其把整个场景划分成了一个个格子(想象乐高),其次划分存在层次结构(参考kd树),这样每一个次级光源就是一个体素,而不是一个像素

第二点是不同于LPV,radiance只传播一次,VXGI认为光打在场景表面会辐射出一个光锥,并对这个光锥进行cone tracing,由此可见比LPV慢,但也因为这个特性,其支持反射物表面是glossy的

因为圆锥会越传播越大,所以对于不同的距离可以在不同的层级进行查询,效率会高很多

如果是diffuse的情况,我们可能需要trace一个巨大的圆锥,这里我们选择几个小圆锥一起追踪,如图所示

image-20210516170906079

VXGI的效果还是很不错的,就是开销有点大

环境光照的阴影计算

首先,最High level的答案是不知道怎么做

总归这个事情是非常非常困难的,环境光是四面八方过来的光,你难道把每个点都视为点光源或面光源吗(many-light problem)

还有一个思路就是采样,但众所周知采样也不是什么好方法,关键是我们很难获得一个着色点的visibility term,不知道该怎么采样,盲目的采样那就更慢了

如果我们想要用上一节的拆分方法,也就是这个玩意

积分域小,g(x)足够光滑时,我们可以做如上拆分,但是假如你的BRDF是glossy的,那么就会有多次跳变,频率也会很高。同时积分域还是接收光照的整个上半球,可以发现我们并不能继续用这种拆分方法。

工业界对这个问题的近似方法非常的暴力:你总会有一个或几个最主要的光源(例如太阳),我根据这几个光源生成阴影就好了

如果真的要解决的话,下面要讲的PRT是一种很不错的解决方案,它是一种可以得到非常准确的环境光照阴影的方法

以及光追永远滴神

球面谐波函数(Spherical Harmonics,SH)

前置的数学知识

根据我们高数学习的知识,任意一个函数可以用傅里叶级数逼近,即写成一个常数和若干sin和cos项的线性组合,其中每项我们将其称为一个基函数,那么我们也可以说,一个函数可以写成一系列基函数的线性组合

基函数性质可以写成如下公式

其中$B_i(x)$为第i个基函数

对于一个空间上的函数,如果在这个函数覆盖到的空间中,函数值变化非常剧烈,我们可以认为这个函数频率很高,反之则比较低,如下图所示

image-20210514153342149

左图的人像中,头发的位置变化非常频繁且剧烈,频率较高,而背景和衣服部分频率较低,我们可以获得这个图像的频域上所显示的内容,即为频谱,如右图所示。频谱中间四个明显高的spike是由于频谱识别的时候会默认将图像在U和V方向上平铺,在平铺的交界会产生巨大的变化,频率很高。

如果我们在频域上留下低频的部分,去掉高频的部分,就会使图像的变化不太明显,看起来像整个图像被模糊了一样,如下图所示

image-20210514153823413

滤波(filtering)的意思就是在图像频域上去掉特定范围的内容。上图就是低通滤波的一个例子

另外,在图形学上的卷积和平均是同一个意思,而卷积实际上也可以·看作一种滤波,如下图

image-20210514154111345

卷积的数学表示是如下形式

按照上述定义我们可以扩展概念,认为图形学中对两个函数的乘积进行积分的形式就是图形学中的卷积。同样的,我们认为这种形式存在一定的滤波意义

球面谐波函数的定义

球面谐波函数是一系列二维的基函数,这个系列中每一个函数都是定义在球面上的,可以理解为每一个函数都是对方向的函数,对于三维空间中不同的一个二维方向(用$\theta$,$\phi$表示),该函数都对应一个值。每一个SH都是一个独特的函数

和傅里叶级数的基函数非常相似的是,SH也有不同频率的函数,且不同频率所对应的函数个数不同,具体如图所示并看不懂为什么是这样子

image-20210514155651097

上图是对SH的可视化,颜色代表正负,可以看见,对于第L阶,有2L+1个SH,编号从-L到L

每一个SH关联了一个勒让德多项式(Legendre polynomial),数学上是非常复杂的,但我们并不需要理解这个多项式,我们只需要知道一个SH可以用数学方式定义,同时SH的线性组合可以用来描述球面上函数的性质即可

球面谐波函数是正交的,且球面谐波函数基函数发生旋转都可以用同阶函数的线性组合表示

球面谐波函数在环境光照中的使用

对于一个球面函数,我们如果想要得出对应的SH的系数,可以做如下操作

其中$\Omega$为球面,$\omega$为球面上的一个方向。$f(\omega)$是这个函数本身,求这个系数的过程在数学上定义叫做投影, 投影的过程便被我们写成了一个卷积的形式。

因为级数是无限的,我们不可能真的用无穷级数取逼近原函数,通常只会用低阶的SH去还原原函数,相当于对原函数做了低通滤波。

不难发现环境光就是一个定义在球面上,不同的方向有不同的值的函数,因此我们可以用SH还原环境光。

根据上节课对环境光采样的结果,我们可以得出一个结论:

先滤波+一次采样=不滤波+多次采样

全局光照问题在BRDF diffuse的时候可以说是相对做了简化的,回顾渲染方程

其中$L_i(p,\omega_i)$是环境光,不难发现这相当于将环境光与BRDF做了卷积,如果BRDF即$f_r(p,\omega_i,\omega_o)$是diffuse的,那么相当于对环境光项做了低通滤波。

在这种条件下,我们观察到,大概我们只需要三阶SH就可以很好的还原环境光,从三阶开始系数$c_i$几乎是0

那么用这种方法如何解决阴影呢?

PRT(Precomputed Radiance Transfer)

回到渲染方程来

我们如果想要算渲染方程,最简单的方法自然是对于每个对应的像素都乘起来,然后做积分,计算量可想而知的大

不难发现,除了$L_i(p,\omega_i)$以及BRDF本身是球面函数之外,$V(p,\omega_i)$也是在不同的方向上定义有不同的值,也是球面函数

PRT的基本思想是:假设渲染过程中场景内其余物体不变,只有光照会发生变化,因此我们把渲染方程看成两部分,$L_i(p,\omega_i)$是负责光照的那一部分,其余的$f_r(p,\omega_i,\omega_o)\cos{\theta_i}V(p,\omega_i)$和光照并没有什么关系,记为light transport部分。

可以发现light transport是和光照无关的,被渲染物体本身的性质,那么我们也许可以先完成这部分的预计算。不难发现,light transport项对于一个确定的点p仍然是一个球面函数。

如果BRDF是diffuse的,我们可以认为BRDF是一个常值,直接把BRDF项拿出来记作$\rho$,则上式可以被我们写成如下形式

我们把光照写成用SH表示的形式,即

这里我们认为是对于一个确定的点p,即点p是常值。

那么渲染方程可以进一步化简为

在PRT场合之中一定可以交换积分与求和的顺序,因此我们发现积分项是一个球面函数向SH基函数投影的形式,这部分完全可以预计算。因此渲染方程的最终形式为

可以发现求和项就是对两个向量的点乘

SH有一个性质,就是如果我对原函数进行一个旋转,很快的就能算出旋转后SH对应的系数。也就是说环境光是可以旋转的,但是场景中的物件不能移动

至于BRDF不是diffuse该如何解决,下节课讲ylq摸了

关于shadow的补充

Distance Field Soft Shadow

Distance Functions/(Signed)Distance Field(SDF)

定义了空间中一点到物体表面的最小距离

可能被规定为带正负号的,即到一个表面的有向(signed)距离

不同物体的SDF混合后可以产生平滑的过度,是SDF一个非常好的性质

下图是一个可视化的SDF

image-20210512173710432

SDF的应用

1.Sphere tracing

​ 对于在SDF上进行的ray tracing,一个很简单的实现方式是sphere tracing,实现方法如下

​ 对于ray上一点P

​ 1.求得SDF(P)

​ 2.沿着射线方向移动SDF(P)的距离到达P’

​ 3.对于P’,回到第一步操作直到接触到物体表面(SDF(P)=0)

image-20210512171559692

2.计算软阴影

通过SDF我们可以获得一个不会被遮挡的“安全角度”,原理如图所示

image-20210512172136691

如果安全角度比较小,我们可以认为该点对于光源的可视度低

计算安全角度的方法:对于每次sphere tracing的结果,记录SDF(P)的最小值和最小值对应的点P‘,则安全角度$\theta$对应的公式为

我们自然可以这样计算,但反三角函数计算量非常大,实时渲染中应尽量避免这种运算,所以我们可以进行如下简化

这样可以一步得出可视度,注意到可视度并不会大于1.0,也就是说$k·\cfrac{SDF(P’)}{|P’-O|}$大于1的部分并没有任何作用,不难看出k的变大会使得大于1的部分变多,从而使软阴影渐变的范围变小,提升阴影的硬度。

距离场软阴影的特点

优点

快速

质量不低

缺点

需要预计算

对存储有重度需求(三维的SDF)

SDF存在各种Artifact(瑕疵?还是怎么翻译?)

环境光的着色

简单回顾环境光照

用一张图记录环境中的光照信息

默认所有的光都是来自无限远的

IBL(Image-Based Lighting)

对于处于环境光照下的物体,我们如何给这个物体上的每一个点着色呢?

一说起着色,我们自然就会想到渲染方程

环境光照下并不考虑visibility项,因此正确的渲染方程为

计算机该如何解上述的方程?Games101讲过的蒙特卡洛积分就是对上述方程积分的一个无偏估计。

然而蒙特卡洛积分需要大量的采样,如果对于每一个点都需要应用一次蒙特卡洛积分,这个方法将慢到爆炸,现在基于降噪,采样复用等等技术,这个方法也变得逐渐可用,但本着能不采样就不采样的原则,我们引入如下方法

The Split Sum

我们观察到如果这个物体的BRDF是glossy的(类似镜面反射),那么对于一个观察的视角,只需要计算一个很小的积分域,如果BRDF是diffuse的(漫反射),那么它的BRDF就会比较光滑,如图所示,下图中左边为glossy的BRDF,右边为diffuse的BRDF

image-20210512180127816

还记得上一节课讲到的实时渲染的近似相等吗?

上式在积分域小,g(x)足够光滑时足够准确

可以发现我们的BRDF完美符合了如上条件,所以渲染方程可以被我们拆解为

看着就头疼

莫慌,先看环境光

对于环境光项的处理

注意乘号前面的项,在我们拆出来的两部分中,这项包含了入射光项,我们把它在BRDF所覆盖的范围内积分并取平均,实际上表示我们模糊了作为入射光的环境光贴图,所以实际上我们完全可以提前对环境光贴图进行滤波(可以理解为模糊),更进一步的,我们可以先给环境光贴图做不同强度的模糊,在需要的时候直接查询,这与MIPMAP非常相似

现在前面一半被我们解决了,那后面一半的积分呢?

按照上述的想法,我们好像也可以对这个BRDF做预计算,但是这种预计算实在太浩大了(菲涅尔项,粗糙度等等一堆东西),还是很慢

但是预计算还是要预计算的,维数太多就只能降维估计近似这样子

接下来基本没太懂照搬原话警告

对于BRDF的处理

首先我们回顾一下Microfacet BRDF(微表面BRDF)

对于一个点p,入射方向i,出射方向o,我们可以把BRDF写成如下形式

其中F为菲涅尔项,决定了从不同角度观察材质有多少能量被反射,G为阴影遮蔽项,D为微表面法线分布,h为半程向量((入射向量+出射向量)/2),其中比较重要的是F和D,我们重点考虑

先看菲涅尔项F,这是一个十分复杂的函数,但是有一个近似方法可以很好的近似菲涅尔项,即Schlick’s approximation,根据这种近似,我们有

$R_0$为物体的基础反射率

关于物体的法线,我们以贝克曼分布(Beckmann distribution)为例,公式如下

说实话没太看懂但是知道有这么个分布就得了

ylq原话:非常简单的一个一维的分布

其中$\alpha$代表了物体表面的粗糙度,$\theta_h$表示半程向量h和法线的夹角,我们可以用各种代换把她变成跟入射方向有关的角度,也就是说,菲涅尔项中的$\theta$和法线分布中的$\theta_h$并不是两个互不相关的未知数,而是可以用一个未知数同时表示的两个项。

这样BRDF就被我们降维到了三个维度:$R_0$,$\alpha$和$\theta$

三维还是高了一点,能不能再拆一个变量出去呢?

人们发现别问我怎么发现的当我们用Schlick’s approximation表示菲涅尔项后,BRDF还可以被近似成如下的形式:

生怕我学会的式子

不过看不懂这个式子其实问题不大,本质是公式的整理,关键是现在我们把$R_0$从积分中拆出去了,积分里只剩下两个变量了!

这样我们就可以放心大胆的预计算了,我们可以根据每一个$\alpha$和$\theta$算一遍结果,把结果打印在一张表上,对于图形学来说就是一张2D的texture,前后两部分积分各打一张tex,效果如下

image-20210512191536843

这样我们就解决了(就?)环境光的渲染问题

这节课主要是关于光栅化软阴影的算法光追扎布多德勒

关于shadow mapping的回顾

shadow mapping的步骤

1.生成shadow map

从光源方向看向场景,测量场景最浅的的深度,记录成为shadow map(SM)

2.检测阴影

从相机方向,结合SM判断场景是否在阴影里

shadow mapping的问题

self occlusion

因为shadow map 的精度,每个像素记录的深度为常数,使得深度被离散化产生的问题

解决方法:

1.加入一个bias,可以根据情况调整(工业界常用)

2.second-depth shadow mapping

​ 不但储存最小深度,还储存次小深度,对两个深度取中点作为深度储存。

听起来很美好但限制太多了又慢根本没人用

​ 此外,这个方法要进行两次深度查询,虽然渐近时间复杂度都是O(n)但是实时渲染不相信复杂度

走样

同样是因为shadow map的精度问题,投射的阴影有锯齿

实时渲染中的近似相等

上式在积分域小,g(x)足够光滑时足够准确别问我为什么我不知道我恨数学

依照上式,渲染方程

可被近似为

此式子将会在环境光遮蔽时被使用

PCSS(Percentage Closer Soft Shadows)

传统的shadow mapping方法只能得到硬阴影,但在现实世界中更加常见的是软阴影,PCSS就是一种获得软阴影的方法

PCF(Percentage Closer Filtering)

最开始开发出来是为了做阴影的反走样(抗锯齿),但PCSS用这种方法做反走样

PCF的作用原理

首先,PCF不是对SM求平均,模糊SM,也不是直接对生成完毕后的阴影结果做模糊

对于一个shading point p,在SM上查找其深度时,不止查找p点本身在SM上对应的深度,还查找p点及其周围像素对应的深度值,并与p点对应的值做比较,周围的像素在上述比较过后可以产生两种结果,要么挡得住p点(深度小于p点),要么挡不住p点(深度大于p点),然后对此结果做filter,以此为依据决定阴影的强度

可以看见,当我把这个filter的范围取的比较大的时候,就可以用PCF近似出软阴影的效果了

PCSS中的PCF

那么,我们应该给一个多大的filter,filter的大小必须要是固定的吗?

显然并不是的,对于不同的遮挡物到阴影的距离,要给不同大小的filter,这也很好理解,月食不一定能把太阳全部挡住,但你的手可以

更准确的说法,filter size和相对的,平均的,投射的遮挡物深度有关(relative average projected blocker depth)说人话

image-20210508172429519

从上图可以看到其实就是个简单的相似三角形关系,图来自Games202原视频

PCSS算法全过程

对于面光源无法生成SM的问题:将面光源看作点光源(例如放在面光源中心的点光源)去生成SM

对于一个shading point p:

1.记录p点周围所有可以遮挡住p点的像素(Blocker)的平均深度$d_{Blocker}$

2.根据$d_{Blocker}$求得$w_{penumbra}$

3.PCF

在PCSS中,我们可以用如下式子表示PCF算法

其中$\chi^+()$为符号函数,其中变量大于0函数值为1,反之为0,w为权值

关于取得$d_{Blocker}$时所查询的范围大小,第一可以用一个常数大小(比如5x5)

除此之外还有一个更好的方法,见图

image-20210508174819794

这样这个套娃问题就解决啦

PCSS的问题

开销非常,非常,非常,恐怖

想象一下,对于一个点p,假如PCF的filter范围是5x5,第一步找$d_{Blocker}$的范围也是5x5,一来一去五十次采样,50倍开销,这么压榨电脑,这要是个ai,你就 等着智械危机吧。

不难看出,多次采样就是变慢的元凶,而采样主要发生在第一步和第三步,那么有没有方法减少采样次数,加速PCSS呢?

1.稀疏采样

​ 工业界有在用,但是会导致噪声,目前有先稀疏采样再降噪的做法

2.全新的软阴影生成方法:VSSM

VSSM(Variance Soft Shadow Mapping)

VSSM解决PCF

此方法针对性的解决PCSS第一步和第三步慢的问题

我们在PCF这一步想要知道周围像素是否遮挡的加权平均,不难看出,如果在不加权的情况下,这个问题其实等价于求周围像素遮挡的个数除以总个数,那不就是周围像素遮挡的概率嘛!

换句话说,我们只要能够估计周围像素遮挡着色点的概率,我们就可以在不知道周围像素具体情况(不采样)的情况下实现PCF算法。

我们直接暴力的认为周围采样点的深度的分布是符合正态分布的,那么我们如何估算出这么一个正态分布呢?熟悉个屁概率论的良好大学生很快就能反应过来了:我们需要掌握周围采样点的信息只有两个:均值和方差。

首先是平均值,给你一张图,我问你要一个范围内的平均值,在图形学课程中我们能想到什么?

这玩意不就是MIPMAP嘛

除了MIPMAP,我们还有一种更精准的方法叫做SAT(Summed Area Tables),实现原理是简单的前缀和,SAT的介绍写在后面了

我们可以知道的就是我们很轻松的就可以知道它的平均值,现在平均值有了,那么方差呢?

接下来请出我们的一个刻在DNA里的经典公式概率论双杀

直接拿下

这样我们只需要在缓存SM的同时再缓存一张记录了深度平方的SM就搞定了

现在我们已经可以估计出一个正态分布了,这时候其实我们想要知道的是这个正态分布曲线下面围成的面积,也就是那个熟悉的$P(X> x)$

学过概率论的你估计有反应过来了:这不是那个著名的不等式吗?

没错,隆重请出切比雪夫(chebychev)不等式

到现在就可以摊牌了,正态分布只是方便理解,真正要用到的就是上面这个切比雪夫不等式

虽然这里不是等于,是小于等于,但是实时渲染充满了近似,我们可以近似的认为有

尽管切比雪夫不等式其实有很多的限制都没有考虑,但是这很好用,也很快,误差也尚可接受,所以实时渲染里我们可以用这种方法

以上我们解决了第三步PCF的多次采样问题,那么第一步Blocker search呢?

一次采样的点之中有且仅有两种情况:这个点可以算作Blocker,以及这个点不能算作Blocker

我们记总采样个数$N$,Blocker的平均深度为$z_{occ}$,个数为$N_1$,非Blocker的深度为$z_{unocc}$,个数为$N_2$,平均深度为$z_{avg}$

则很容易看出来对于任意情况总有

我们不难看出$\cfrac{N_2}{N}=P(x>t)$,又可以愉快的切比雪夫了

现在我们知道了$\cfrac{N_2}{N}$,$\cfrac{N_1}{N}=1-\cfrac{N_2}{N}$以及直接从MIPMAP或SAT获得的$z_{avg}$,想要解出$z_{occ}$,但我们还有一个变量不知道:$z_{unocc}$

这里就又是大胆假设出场的时机了:我们直接假设$z_{unocc}=t$,没想到吧

其实是因为绝大部分阴影接收者是个平面,所以这么假设问题不大(你妹啊斜的平面咋办啊?)

总之现在我们也就把这个问题解决了,撒花

SAT(Summed Area Table)

这里插播一个之间就提到的,比MIPMAP精确且可以适应长方形查询平均值的技术:SAT

前面已经说到了SAT是一个基于前缀和的算法,这里放张图大家感受一下什么是前缀和

image-20210508210011660

可以看到SAT的第n项实际上就是把原输入前面的n项加起来,当我们要求m到n项之间的和的时候,只需要用SAT的第n项减去第m-1项就好了,这种预处理的方法快速便捷无误差。

拓展到二维也是一样,我们只需要在预处理生成SAT的时候把1D时的加左方一项改成加上方一项和左方一项,求[a,b]到[x,y]之间的和的时候只需要做如下的操作即可,具体看图

image-20210508210856542

VSSM结尾

虽然VSSM很牛逼,但是业界还就那个逐渐没人用

主要是因为各种降噪手段都出来了,空间域和时间域双管齐下,导致稀疏采样的PCSS逐渐的好起来了,这么多近似的VSSM用的人就少了

但是这个算法是真的牛逼啊,这就是我学概率论的意义吗(

话是这么说但根本没好好学概率论

Moment Shadow Mapping

由于VSSM做出了太多的近似,容易被出题人出数据卡掉在某些环境下不准确

举个例子,如果在切比雪夫估计遮挡率的环节你估计遮挡率低了,阴影就会偏白并发生漏光现象,偏黑也就算了,偏白是绝对不能忍受的

另外当接收物不是平面的时候也会出现问题,参见Blocker search的近似

MSM主要是为了解决VSSM估计分布不准的问题

如果我告诉你Moment在这里是“矩”的意思,你是不是就能猜出点什么了?没错,MSM正式利用了高阶矩来估计分布

不知道矩的自己回去复习概率论虽然我也就知道这么个名词

十分复杂所以闫大神没讲具体实现

我也不想听

现在也没人用

结尾

头一次上课做笔记

这是闫令琪大神在GAMES上发表的课程GAMES202-高质量实时渲染的个人笔记,有想要学习的和图一乐的可以参考一下

本次笔记覆盖到了第三讲和第四讲,real-time shadows部分

做笔记真的累死了但是为了充实blog我忍了

关于命运石之门0

首先开门见山,命运石之门0是一部有缺憾的作品,但对于它所做出的尝试,相对于本篇的补全,以及剧情氛围的营造来讲,我给这部作品满分,或者说不是满分,也是9.5以上

这部作品口碑会两极分化是必然的——有人认为它伏笔收的不如本篇干净利落,也有人认为它压抑的氛围让人不能接受,对于前一个问题后面我会解释,对于第二个问题那纯粹是个人喜好而已,不多加评判

关于伏笔

好了现在开始说第一个问题,很多人觉得0的世界线错综复杂,被动的跳线让人一头雾水,结尾的伏笔又没有本篇那种干净利落的回收,因此认为0在剧情方面被本篇吊打,但实际上,我在这里先声明一个观点,命运石之门0的世界线跳跃,并不是无迹可寻的,不同的世界线之间有着千丝万缕的联系,而玩家在跳跃后不知所措一头雾水的感觉,实际上与胸针的不知所措是相对应的,并不是没有道理,只是不浮于表面罢了

这里如果把每个线的跳跃缘由说出来那可太多了,这里根本就写不下,而且我很懒,就算了(
但大家一定要知道,命运石之门0的跳跃,是很值得研究的,随着研究的深入,你会发现志仓社长真的把世界线这套世界观全部的潜力发挥了出来,完美的诠释了世界线的飘忽不定以及观测者在世界线间漂流的无力感和绝望感,当然了,这与本作优秀的氛围塑造也有一定关系(因为我先玩出的be,对这种悲伤绝望的氛围可能还有更深的感触 ps:助手的电话怎么可以不接啊喂!

但是,我刚开始就说过,这部作品是有缺憾的,缺憾是什么?例如没有认真解释的三千跳,略显赶工的红莉栖线,都是本作在叙事方面的缺憾。那么本作在其它方面就没有缺憾了吗?不是的,说了这么多,在本人深入(一点也不)的研究之后,终于要拿acg界顶级的世界观——世界线,开刀了。

命运石之门系列(包括本篇)的逻辑漏洞

时间跳跃机器

首先是包括本作在内,最大的逻辑漏洞——时间跳跃机器,众所周知时间跳跃机器是将人的记忆压缩之后,时间跳跃到原来的手机上,被当事人接听后实现记忆覆盖为基础原理实现的,而这个发送到过去的过程本质上和dmail没有区别
那为什么不是胸针使用时间跳跃机器后感受到世界线的变化,来到一个自己时间跳跃后做完一切,时间发展到此时此刻的世界线呢?

说白了,如果真的以一个固定的胸针为第一视角,他只会感受到世界线变动才是,并不会感受到时间跳跃。

那如果你每次都以跳跃之后的胸针为视角呢?那逻辑上还是解释不通

rs所带来的记忆覆盖问题

如果你每次都以跳跃过后的胸针为第一视角,那么应该指出后面时间将会发生的,原世界线胸针rs后覆盖跳跃胸针记忆的事情

至于为什么,还是这句话,对于时间跳跃之前的胸针,时间跳跃和发送dmail没有区别,发送dmail的胸针会在发送后跳跃世界线并覆盖掉现在这条世界线上的记忆,时间跳跃机器也会

好,你可以说这样故事没法写,为了感情和表达效果可以对合理性做出一些妥协,可以略过甚至完全忽略每一次记忆覆盖,那拯救n次真由理未果的记忆呢?

完全不符合逻辑的记忆成为了剧情的重要推手,这就一定是你的逻辑漏洞了。
类似的漏洞其实都体现了世界线这个世界观不完善的地方,但在本篇里,不合理性并没有被完全展现出来,因为本篇限制了这套世界观的发挥

在本作里,胸针仅仅以自己作为主视角,主动发生世界线跳跃,且除了sern以外世界线跳动的原因均为自己控制,从这你就能看出来实际上,这套世界观被加了不少限制。而我开头就说过,在命运石之门0里,志仓社长把世界线这套世界观全部的潜力发挥了出来。

那么,代价,是什么呢?

逻辑漏洞放大带来的0篇风评不如本篇

自然是世界线这套世界观里所有的漏洞也一并被暴露了出来,而且很多漏洞真的困难修复(参见时间跳跃机器)所以只能搁这,而这些底层的漏洞反应到表层就是部分逻辑的错乱和剧情衔接问题。
但我们同时也要明白,志仓千代丸,他不是全知全能的神,世界线如果真的毫无漏洞,为什么不能成为解释世界组成的假说?志仓他手下没有顶尖的物理学家团队,不可能写出那么严谨的世界观,而世界线这套世界观它已经足够优秀了,我们也不该过分苛责。

有逻辑漏洞是坏事,但不一定坏

我关于逻辑和剧情平衡的看法

这么说来,我们真的应该如此苛责石头门0的逻辑漏洞吗?也许你可以说石头门0如果保守一点就不会出现这么多让人费解的地方,但我个人真的非常欣赏这种将世界观发挥到极限的做法,况且,令人不知所措,对发生的事情感到费解,明明有特殊能力却完全无法解释当下的情况,不也是石头门0营造的氛围的一部分吗?

从这方面看剧情的连结与发散

我不知道是否有人玩过线性拘束的树状图,但是如果玩过的话,我个人感觉石头门0的各线有一种,te是主线,而其余线类似于树状图那种,为主线发生的一切无声的铺垫(除了be线和篝线),实际上,我个人认为25年的胸针并不一定是真由里派生te线的胸针,也有可能是红莉栖线,真帆线最终跳跃过来覆盖的胸针,这方面我不知道是留白好还是赶工好……(真以为篝线结尾星之奏单纯就是给你放个星之奏听?个人认为其它线最终都是共通的,共同将胸针修正到了te,而只有篝和be是真正的绝望,死路一条)

总结

咳,扯远了,总体来说,我认为命运石之门是在galgame的限制下,做到了最好的五边形战士,而石头门0则是撞开了这些条条框框,格局更大,细节更丰富的作品,当然也让自己遍体鳞伤,总体而言我真的特别欣赏这样的石头门0

结尾

这篇文章是我一年前在晚上2点半写的原稿的轻微修改版本,一年前那篇我发在bangumi上了,可以看见当时我急着去睡觉并没有说的很清楚胡言乱语,今天突然在电脑里看见了就发出来吧,充实一下我的博客,让它看起来像个博客(

因为显得很厉害所以来个大标题

再来个中的

以及小的

和巨小的

这是没有的

这是划掉的