14 KiB
RDG
FrameGraph
首先需要了解Frame Graphe的设计思想,为了速成,看了知乎大佬 奔驰 的文章。但也因为速成,必然会在一些地方有所误解,还请见谅。这里说一下我的理解:Frame Graphe思想可以理解为一帧中执行逻辑的图,没有限定是渲染还是Gameplay逻辑,但不管怎么说其重点在于图。 大致操作为将逻辑以最小功能为标准封装成单独的Pass,Pass与Pass之间是变量(或者可以理解为资源)的传递的关系,因此就可以把整个逻辑关系转换成一个Graphe。
UE4的RDG全名为Render Dependency Graph,感觉是为了能更好的管理资源的加载回收(尤其是RT),拯救一下显卡上珍贵的显存以及IO带宽。同时方便程序员理解代码,不会写出毛线团一样的代码。至于生成的图没有看过在哪,估计在4.25加在Insight调试系统里吧。
大致流程
通过FRDGBuilder对象构建RDG渲染流程。之后塞入定义的RDG Uniform以及Shader后,运行这个流程。RT会使用外部的IPooledRenderTarget接口取得(比如各种GBuffer),当然也可以自己创建,之后绑定到RDG Uniform中的RenderTargets[0]数组中。最后根据是ComputShader还是PixelShader选择分发或是绘制即可。但源码里也没有RDG的PixelShader的代码(95%都是ComputeShader的),经过试验,RDG的PixelShader的使用方法还是和旧版的GloalShader比较像的。
既然说到这个,之前我也是看了虚幻开放日的技术分享,知道了两者的区别。
ComputeShader与PixelShader对比
ComputeShader对于PixelShader的优势:
- PixelShader只能处理当前Shader,ComputeShader是任意位置可写。可以用于编写屏幕空间反射等需要将效果写入任意位置的效果。
- 可以更好地利用显卡的并线单元。
- 共享内存:举个例子模糊、等需要多次采样各个像素的算法,使用共享内存就可以减少采样次数与消耗。
PixelShader对于ComputeShader的优势
- PixelShader可以预加载贴图并且缓存UV,使得读取贴图的效率会非常高。而ComputeShader需要计算出UV,所以做不到这点。
- PixelShader支持FrameBuffer压缩,减少带宽压力。
- PixelShader支持更多的贴图格式。
ComputeShader还存在无法读取UAV,比如把RT转化成SRV后才能读取,问了EPIC的luoshuang,说是历史遗留问题,UE5会解决。
制作玉石效果
使用寒霜的FastSSS方案,这里我下载了演讲的PPT。厚度贴图可以使用Substance Painter烘焙。算法是反转法线后使用AO算法,在像素对应的三维坐标球形区域内生成若干采样点,计算模型内部的点与总点数的比。之后就是直接套寒霜公式了。
光线追踪
《Ray Tracing in One Weekend》只是尝个鲜,学习《Ray Tracing from the Ground Up》后,拥有了一个基础的Ray Tracing框架。然后就是PBRT了,相当不错的教科书。因为单线程渲染效率太低,所以使用了直接使用Inter的TBB库,但最后发现写的渲染器 会出现样本混乱的情况的。同时因为没有写并行库的基础,所以之后的后面几章就没有去写代码了。如果有时间会去看《c++并发编程实战》的英文版来学习并行编程。
PBRT
- pbrt采用了FLOAT宏来调整浮点类型,不过我记得VS的设置就可以直接调整。并且手动实现了EFLoat类来消除浮点数在四则运算后积累的误差。
- pbrt采用c++11的线程编写,没使用任何并行库。
- 实现了一个场景描述文件格式,可以将dcc软件制作的场景导入渲染。
一束光线
我们从眼睛中看到的图形可以理解为视觉细胞接收到场景中辐射与反射的光辐射信息的结果,光线追踪就是计算机通过模拟这一过程所发明出的算法。根据光的波长不同,将其排列成为光谱。但受限于人眼感光细胞,人眼无法看到所有颜色,对于可见光,人们建立了不同色域范围的色彩标准。常用的是rgb、sRGB等。
光源
PBRT为了将光谱值转换为RGB值,实现了SampledSpectrum(采样频谱)与RGBSpectrum(RPG频谱)类,这里我没看懂,好像是通过对3条曲线采样求积分得到3轴的比值。
采样
除非直视光源,不然人眼接收到的光全都是经过N次反射的,因为我们可以把人眼接收到的图像理解成一个N阶多项式方程。同时这也相当于将一个自然现象模拟问题转化为一个数学问题,可以用其他的数学工具来解决问题。但该方程无法直接求解,所以会使用蒙特卡洛方法求近似值。
蒙特卡洛方法
是一种以概率统计理论为基础的数值计算方法。它的核心思想就是使用随机数(或更常见的伪随机数)来解决一些复杂的计算问题。通过对大量抽样样本进行统计,计算出其分布规律。
概率密度函数 pdf 一个描述这个随机变量的输出值,在某个确定的取值点附近的可能性的函数。 累积分布函数 cdf 是概率密度函数的积分,能完整描述一个实随机变量X的概率分布。
有关抽样:
逆转法
舍选法
某些函数,可能无法通过对其进行积分从而获得pdf,或者无法通过计算获得逆cdf。舍选法是一种无需进行以上任意一个步骤就能根据函数分布生成样本的技术。本质上它是一种投飞镖法,假设我们想从某个函数f(x)中抽取样本,但是我们有一个pdf p(x)满足f(x)< c p(x),假设我们知道如何从p中获取样本
这个过程反复选取一对随机变量(X,ξ),如果点(X,ξc p(X))是在f(X)下面,那么接受样本X。否则,它将被拒绝,并再次选取一个新的样本对。这个方法的效率取决于f(x)与cp(x)的接近程度,越相似收敛地越快。而且适用于任意维度的函数。
重要性采样
选择一个与目标概率密度函数具有相同形状的分布函数进行抽样,来减少方差。形状越接近,收敛越快。在《Raytracing in weekend》中对方形面光源运行了重要性采样。
分层采样
对采样区域进行分割,各个子区域单独生成样本。
低差异序列
低差异序列可以非常高效的生成分布非常均匀的高质量样本集合,相比伪随机数极大的提高蒙特卡洛积分收敛的效率,并且它们的实现都不复杂。
- Van der Corput:根据底数将指定长度的正整数序列转换为指定进制的数序列,之后以小数符号为分界线,将每个数左边翻转到右边去。
- HAMMERSLEY 哈默斯利:使用底数为 样本序号/样本总数 的Van der Corput序列。
- Halton 霍尔顿:使用底数为质数序列的Van der Corput序列。
UE4中的TAA好像就是用的低差异序列生成的样本。
使用均值样本会产生 混叠现象,也就是摩尔纹
在信号处理以及相关领域中,走样(混叠)在对不同的信号进行采样时,导致得出的信号相同的现象。它也可以指信号从采样点重新信号导致的跟原始信号不匹配的瑕疵。它分为时间走样(比如数字音乐、以及在电影中看到车轮倒转等)和空间走样两种(摩尔纹)。这里我们不详细展开。
采样器基本原理
事先生成若干组采样点样本,之后进行乱序操作。在进行逐像素采样时,使用伪随机的方式选取随机序号的采样点组。
抗锯齿
因为场景的定义在三维空间中是连续的,而最终显示的像素则是一个离散的二维数组。所以判断一个点到底没有被某个像素覆盖的时候单纯是一个“有”或者“没有"问题,丢失了连续性的信息,导致锯齿。
SSAA:超采样抗锯齿,高分辨渲染,之后取平均值结果。 MSAA:在光栅化阶段判断一个三角形是否被像素覆盖的时候会计算多个覆盖样本(Coverage sample),最后根据权重样本占比所在的片元进行着色,不兼容延迟渲染管线。 FXAA:边缘检测抗锯齿,是一种后处理抗锯齿技术。 TAA:时间抗锯齿,将采样放到时间轴上的抗锯齿技术。对一个像素使用抖动效果,之后再将所有的采样混合起来。他需要解决判断每帧中这个像素的移动位置。但在一些运动较大、或者帧数较低的情况会出现 重影的问题。
贴图采样
- 双线性插值
- ISOTROPIC TRIANGLE FILTER 各项同性三角形过滤 然无法生成高质量的结果,但速度比较快。该滤波器因为各项同性的关系不支持非正方形或非轴对称的范围。该滤波器的主要缺点是:在斜角度观察纹理时图像容易变模糊。因为不同的角度会导致采样率不一致。
- ELLIPTICALLY WEIGHTED AVERAGE 椭圆权重均值
偏差分析与重采样
没学过相关知识,无法继续。
加速结构
如果光线每次与物体交互都需要遍历所有三角形,那效率将会非常低,为了提高求交速度,我们可以在渲染前实现构建加速结构,常用的有:BVH、KD。
BVH:
- 计算每个图元的边界信息并且存储在数组中(图元号,包围盒以及中心点)
- 使用指定的方法构建树(中间对半切的方法、SAH:首先计算图元位置,将其分配到12个桶中,并且计算每个桶最大边界盒,再遍历每个桶边界盒的Min与Max坐标计算最佳切分点,通过 切分后两个边界盒的表面积/当前包围盒的表面积来计算求交效率)子图元中质心距离最大的轴向作为分割方向。
KD树:对3个轴的所有的切分方案进行尝试,最终计算出最优的空间分割方案。
KD树的分割(每个轴遍历所有分割方案)与BVH(SAH)分割(每个轴12次分割)更加精确,BVH保证每个图元只有一次引用,内存占用较少,构建较为简单,而KD树并不保证,所以内存占用较多。因此KD树的求交效率要比BVH好,也导致了渲染时需要花费比BVH更多的时间在构建KD树上。 同时KD树在求交时会计算与光线与分割面的相交位置是否在[min,max]中来判断是否需要与茎节点中的两个节点一一求交,以此来减少求交计算量。而BVH通过深度优先遍历树进行求交的。 总结:BVH是基于物体分割的,KD数是基于空间分割的。
早几年NVIDIA研发出将两者结合的方案SBVH。这种算法可以节约因为图元分布(大小)不均匀而造成的BVH树求交效率低下。
- 找到一个对象分割候选者:使用SAH算法构建一个BVH。
- 找到一个空间分割候选者:类似构建KD树算法。
- 选择获胜者:基于SAH,选择消耗最小的作为最后结果。如果不满足叶子节点生成条件则继续分割。
我记得是前几次进行空间分割,后续使用对象分割。
材质模型
接下来就是处理光线与物体交互的结果了。PBRT把材质分开金属、非金属、与半导体,其中半导体较为复杂,Pbrt里没有介绍。
根据材质性质不同,可以分为一下表面分布函数: BRDF双向反射分布函数:在单位微表面上反射辐亮度与入射辐射照度的比值。 BTDF双向透射分布函数:在单位微表面上折射辐亮度与入射辐射照度的比值。 与两者结合的BSDF,对于半透明材质则使用BSSSDF双向表面散射分布函数。
微表面模型
本来对于一个不确定表面可能需要使用采样要求积分,因为物体的微观表面参差不齐,无法通过分析的方法精准模拟。但微表面模型假定物体由大量微小的绝对光滑表面组成,之后就可以根据统计学计算出模拟出看上去正确的结果。 现在用得较多的Burley模型(迪士尼模型),也是UE4使用的,就是基于这个微表面模型。
BRDF公式
对于各项同性的BRDF公式就是: 漫反射 +(菲尼尔函数 * 法线分布项 D * 几何项G)/(4 cos入射角 * cos出射角) Cook-Torrance
菲尼尔
- 一般都会使用近似近似函数Schlick(施利克)
- 菲涅尔方程描述了光线接触到表面后反射与透射的比,它实际上Maxwell方程在光滑表面上的求解。 因为在现实环境中光的偏振现象较少,所以在PBRT假设光不偏振。
- 一般情况下,计算计算机图形学中的常见操作都会忽略色散现象,以此极大地简化光线传输计算(色散:折射率随着光的波长而变化)。
微表面法线分布函数 (法线分布项 D)
- 法线分布函数由粗糙度决定,主要影响高光
- Beckmann–Spizzichino(贝克曼-斯皮兹奇诺)
- Phong
- Trowbridge–Reitz(特罗布里奇 瑞兹)ggx
- 迪士尼改进了 Trowbridge–Reitz ,将其命名为GTR。
几何衰减因子(Shadowing和Masking)几何项G
- Smith阴影函数
- 迪士尼对Smith 改造,smith-ggx
漫反射
- pbrt中使用 OREN–NAYAR(奥伦-纳亚尔)漫反射模型(基于微表面模型)
在《Raytracing in weekend》中,会使用LAMBERTIAN漫反射 (辐射度/π) 作为漫反射项,即不论任何角度,都会反射出一样的辐射度。 Lambert漫反射模型在边缘上通常太暗,Disney开发了一种用于漫反射的新的经验模型,以在光滑表面的漫反射菲涅尔阴影和粗糙表面之间进行平滑过渡。思路是使用了2个Schlick Fresnel近似相乘作为调节因子
测量BRDF
pbrt中还介绍了一种通过测量来拟合brdf的方法,ff15中用到这种方法。
BTDF
没看过,应该乘以 菲尼尔来计算的
BSSDF
没看过,UE4好像通过生成均值采样点,之后再通过距离进行近似计算的
迪士尼模型
根据透明度参数,对透明渲染结果与绝缘体渲染结果混合,之后再根据金属度与金属渲染结果混合。
路径追踪
从摄像机发射采样光线,最后把这条光线若干次反射过程中结果累加起来。
优化
光子映射
通过让光源发射光子,来生成光子贴图,这样就能让收敛速度变快。
双向路径追踪追踪
同时从光源与摄像机发射采样光线,一起构建光路。之后将各个反射点直接连接起来,这样就能快速构建多条光路了。大大加快收敛速度。当然这些光路需要进行可见性测试。