对闫令琪老师在GAMES101课程讲的东西进行梳理,主要是为了把所有知识点过一遍,说明白why、how、what的问题,不涉及具体的线性代数、计算机、物理、信号处理、数值处理等具体知识。加了一些其他地方看来的资料。
本人菜鸟,如果哪里不对,欢迎指正!
model:把模型从本地坐标转换成世界坐标。
view:把世界坐标转换成观察坐标。先把相机放到某一位置,在保持相机的方向,把相机移动到原点。
projection:包括正交投影和透视投影。正交投影:无视z坐标,移动到原点;透视投影:把平截头体压缩成立方体,再进行正交投影。经过这一步得到标准化坐标。
*在一般的图形学渲染管线里,mvp都是发生在几何阶段的,具体过程是,先进行mv矩阵变换,然后对顶点进行着色(包括法向量、纹理坐标、颜色等),之后投影矩阵变换,得到一个标准化立方体(正交投影得到标准立方体好理解,但其实透视投影得到的也是标准立方体,经过压缩之后的),之后经过视锥剔除这一步操作,避免不必要的时间消耗,此时得到的还是标准化立方体,顶点的坐标还是三维的,然后经过视口变换,得到对应屏幕大小的二维图像,此时的顶点包括所有需要的信息(包括法向量、纹理坐标、颜色等)。然后进入光栅化阶段,先进行三角形处理,对顶点与顶点之间做插值,然后做光栅化,对应到像素,然后做shadding(包括纹理映射),对像素着色,之后做可视化处理、alpha测试、模板测试等,上屏到显示设备。
*矩阵相乘的顺序是从右往左,矩阵变换的顺序应该是——缩放、旋转、平移。
区分向量和点,更易于表示仿射变换
*仿射变换就是线性的几何变换加上一个平移,包括旋转、缩放、平移、切变。
最基本的几何平面;保证是平面;容易进行插值操作(重心坐标)
实际上就是用像素对图像进行采样,会出现锯齿(aliasing)。
解决方法:出现锯齿,进行反走样(antialiasing),先进行模糊操作再进行采样。
为什么模糊操作可以反走样?采样就相当于在时域用冲激函数对原函数做乘积,而采样频率越低,冲激函数频率越低,在频域的表现,冲激函数频率越高。而频域的原函数对冲激函数进行卷积操作,相当于对原函数图像的重复,冲激函数频率越高,越容易出现混叠。模糊操作消去了高频信号,高频信号便不容易混叠。
*时域的卷积相当于频域的乘积,时域的乘积相当于频域的卷积。
现实点的解决方法:
1、提高分辨率,就不关图形学的事儿了;
2、MSAA(multisampling AA)/超采样,把一个像素变成多个像素进行采样,得到的值求平均,还是用一个像素来表现,近似了模糊的操作,并没有提高分辨率,缺点是效率问题(问了老师,老师把antialising叫做反混淆,实际指的可能就是MSAA?);
3、FXAA(fast approximate AA),图像处理层面,对生成的锯齿图去锯齿;
4、TAA(temporal AA),把MSAA对样本的处理分布在时间上。
5、超分辨率/超采样/DLSS(deap learning super sampling)
*这一块的前提好像就是模型的复杂度/图像的分辨率足够,采样点不足。如果采样点足够多,就不会出现锯齿现象!
shading:给物体施加材质的过程。包括光照和texture。
specular高光:与观察方向有关
diffuse漫反射:与观察方向无关,光线打过来,均匀地反射到四面八方。
ambient环境光:可以设置为常数
这里的光线考虑衰减,I/r^2,实际衰减的是irradiance(单位面积的光的power)
对面shading(flat)
对vertex做shading(gouraud)
对像素做shading(phong shading)
三维空间顶点经过mvp矩阵变换,变成屏幕空间的顶点。
屏幕空间的顶点,经过三角形处理,组成三角形。
三角形经过光栅化,变成fragment。
fragment经过shading,计算光照、纹理映射(比如采用布林冯模型、微表面模型,就在此处进行相应的计算),变成fragment。
带材质的fragment经过framebuffer(比如深度处理),变成屏幕上的图像。
*具体过程是,先进行mv矩阵变换,然后对顶点进行着色(包括法向量、纹理坐标、颜色等),之后投影矩阵变换,得到一个标准化立方体(正交投影得到标准立方体好理解,但其实透视投影得到的也是标准立方体,经过压缩之后的),之后经过视锥剔除这一步操作,避免不必要的时间消耗,此时得到的还是标准化立方体,顶点的坐标还是三维的,然后经过视口变换,得到对应屏幕大小的二维图像,此时的顶点包括所有需要的信息(包括法向量、纹理坐标、颜色等)。然后进入光栅化阶段,先进行三角形处理,对顶点与顶点之间做插值,然后做光栅化,对应到像素,然后做shadding(包括纹理映射),对像素着色,之后做可视化处理、alpha测试、模板测试等,上屏到显示设备。
如果纹理太小怎么办?纹理上一个texel被图像上多个pixel包含,texel不够用。
方法:用插值的方法补texel!近邻、双线性、双次立方。
如果纹理过大怎么办?图像一个pixel包含多个texel(摩尔纹)。
方法:mipmap。mipmap的好处,额外的内存开销小,只有原来的三分之一;缺点,只能对正方形区域查询,有的pixel包括的texel是斜着的一条区域,mipmap仍然不能表现该区域的属性。
改良:ripmap,各向异性过滤,开销是原来的三倍。
环境贴图、环境光(包括很多游戏大作的画面非常好,有些就是提前计算好光照,做成光照贴图)、
*环境贴图可以是球面或立方体面
纹理不只可以表示颜色,也可以表示高度/法向量(bump/normal),只是影响了shading计算,没有真正地改变顶点的位置。用displacement mapping,可以实际上改变顶点的位置。(bump/normal这一步,纹理影响shading,作用于fragment,在fragment做shading的阶段进行处理;而如果是displacement,应该是顶点处理阶段进行。看来,着色频率的选取跟纹理贴图的应用有实际的对应关系。)
三维噪声函数定义纹理,在任意点都能取得相应的值。(参考隐式的几何表达。)
隐式的几何表达
代数公式、水平集、分形/自相似(fractals)
CSG(constructive solid geometry):通过简单几何体的布尔运算获得复杂的几何体、
距离函数:指的是到几何体点的最小距离,当两个几何体的点近,通过融合距离函数,生成新的几何体
显式的几何表达:直接或由参数映射,给出几何体上的点
点云、几何面、.obj(不太理解为什么把.obj模型文件也归在这儿)
隐式集合表达的好处:
很好区分内外关系,光线追踪时易于求交,处理拓扑结构方便,紧凑的表达
隐式几何表达的坏处:
对于一些复杂几何形状很难表示
描述物体/相机的运动过程(animation)需要用到曲线。
曲线就是贝塞尔曲线,用一些控制点+插值的方法对曲线进行控制。
贝塞尔曲线的代数表达(德卡斯特里奥算法)、性质(比如曲线包含在控制点的凸包内)。
分段贝塞尔曲线(及判断连续性)。
样条:表示曲线的分段多项式函数。简单来说就是控制曲线的函数。
B-样条:既有贝塞尔曲线的性质,相比起来又需要更多的信息,比样条短(指的是函数表达?)。可以进一步推广为NURBS(非均匀有理b样条)。闫老师说B样条很有可能是图形学里最难的一块儿,所以了解下就差不多了。对了,B样条属于数值分析的一块儿内容。
曲面是由贝塞尔曲线表达的。
1、曲面细分
Loop细分:只能对三角形进行,一分四,优化可以靠加权重进行。
Catmull-Clark细分:对矩形进行,先找奇异点,奇异面。
2、曲面简化
靠曲面坍缩进行。
二次度量误差:每次细分要保证新的点到原来的边的距离之和最短,迭代此过程动态进行。
光栅化的渲染是将场景的渲染任务按层次拆解:物体——三角面——像素。这种拆解会导致全局信息的丢失,因此光栅化实现不了软阴影、间接光照等。
光栅化没法考虑全局光照(GI),只能计算直接光照,没法考虑光源与物体间的遮挡,无法计算阴影。那怎么产生阴影的效果?用shadow mapping!但是只能处理点/方向光源,只能产生硬阴影(要么在阴影里,要么不在)。
key:一个点不在阴影里,就要同时被光线和相机看到。如果只被光线看到,不被相机看到,就不会被渲染;如果被光线看到,没被相机看到,就是阴影。
实现:先从光源渲染,得到深度图;再从相机出发,经物体折射到相机,得到第二份深度,和原深度图对比,数值不同,则表示物体被遮挡,产生阴影。
问题:画面会脏,数值精度问题(浮点数的相等不好判断,可以改判大小,或加个bias,但效果不好);受限于shadow mapping的分辨率;只能产生硬阴影。
应用:所有的3d游戏和早期动画。
从相机出发,对于每个像素点向场景投射光线,直到光线与场景中第一个物体相交,在交点处,根据物体本身性质、光源属性和光照模型等来计算像素点的颜色;
光线投射只考虑投射光线,光线与物体相交后不会继续跟踪,不考虑后续的折射、反射等。
一般认为光线投射是光线追踪的前身。
想法:从照相机逐像素投射光线,在三维空间中经过传播、反射、折射(设置停止次数),然后从光源向空间中所有打到的点投射光线,求该点的颜色,最后沿视线打回到像素上。
最笨的方法是对每个像素打视线,对所有三角形面求交,在空间中反射、折射几次求几次,太费事。有以下几个方法:
whitted-style在物理上并不准确,只能表示镜面反射、折射(无法表示glossy材质), 打到漫反射就停止,无法继续计算,所以得到的效果也不真实,为了用真实的物理量进行渲染,引进辐射度量学的知识。
1、几个概念
energy:光的能量,以焦耳做单位。
flux/power:功率,单位时间的光的能量,相比energy,更常用于对光的能量的度量。
intensity:单位立体角的power。用 I 表示。
irradiance:单位面积的power。 用 E 表示。
radiance:单位立体角、单位面积的power。用 L 表示。
2、双线性反射分布函数 BRDF(bidirectional reflectance distribution function)
BRDF是一个函数,将入射的irradiance映射成出射的radiance。简单来说,BRDF决定了对于入射光,如何分配出射的光。
3、Reflection Equation
L=E+KE (K是BRDF和角度的积分)
出射的光等于自身发出的光加从别的光源打过来的光。
经过推导,得Rendering Equation: L=E+KE+(K^2)E+…
其中 E和KE就是光栅化能计算的部分。
4、概率论的回顾
X是随机变量(将事件映射为随机变量的取值),P(x)是对应的概率。略。
因为whitted-style光线追踪还有这样那样的问题(如打到漫反射就停止等),提出path-tracing。路径追踪从属于光线追踪,也是用来计算全局光照的,它是绝对物理正确的。
基本思想:从一个像素打出光线,在空间中折射、反射、漫反射…直到打到光源为止;在光线遇到表面需要反射时,以表面法线为中心做一个半球,向半球上的若干方向引出射线,递归计算这束光纤中,每条光线对表面上这个点的光照贡献。
(1)定积分的求解
计算的时候用蒙特卡洛积分近似计算。
(2)光线的数量会爆炸
一个像素只有在打出一条光线的情况下,才不会计算爆炸,但是一条光线会产生噪声,所以一个像素打出数条。
(3)光线迭代的终止
那么打出的光线什么时候停止呢?一是每次迭代生成随机数,根据随机数直接截断;二是引进俄罗斯轮盘赌方法,设置概率p,当光线打在物体上,有p的概率继续走,或者停止。
(4)如果光源面积比较小,从相机发射的光线到达光源的概率变小,有很多光线被浪费(提前截停),产生大量的噪声
不对半球上的立体角积分,而是在光源上采样,对光源方向采样。
particle tracing、光子映射、VCM、Metropolis light transport…
除了Games101,此处还参考了百人计划课程
材质就是BRDF!
镜面反射、漫反射、glossy、几种特殊的折射…
costics(焦散):水底的条状的高亮,原因是水面的凹凸不平,导致光线经过折射打到一条带上。
斯涅尔定律:入射折射率×入射角的正弦值=出射折射率x出射角的正弦值。经过推导,当入射折射率大于出射折射率,有可能发生全反射。引申:斯涅尔窗。
菲涅尔项:视线垂直于物体的表面,物体的反射较弱;视线平行于物体的表面,物体的反射较强(一个例子是低头看湖面,水很清澈,反射弱,看远处的湖面,远山倒影,反射强)。绝缘体和导体的菲涅尔项有较大差异。
F0是平面基础反射率,即垂直看物体表面时候的反射率。
下图:绝缘体(如玻璃)的菲涅尔反射率随着观察角度变化的趋势
下图:导体(如Glossy的金属)的菲涅尔反射率随着观察角度变化的趋势
补充:光线如果被物体完全反射,则物体不带有颜色,如果被物体部分吸收,则呈现颜色。
PBR(基于物理渲染)渲染管线之所以能够比较真实地表现各种不同的材质,是因为它采用了更加复杂的材质模型。在PBR渲染管线出现之前,我们通常采用的材质模型由三部分组成:环境光(Ambient)、漫反射(Diffuse)以及镜面反射(Specular)。但是这种模型难以模拟出表面不同的粗糙程度造成的不同反射效果,导致它模拟出来的材质在视觉上看起来更加接近于塑料的光滑表面。
PBR渲染管线中采用的是一种叫做微表面(Microfacet Model)的材质模型,它基于物理的观察,认为物体表面由很多凹凸不平的微小镜面组成。这些具有不同大小、方向的微表面在对入射光线进行反射时产生了不同的反射效果,从而使得人眼能观察到不同的材质属性,比如物体的粗糙程度就可以用微表面模型的法向量分布表示。因此,微表面模型能够更加真实地模拟各种不同的材质。
1、各向同性和各向异性。前者是微表面没有方向性,后者是微表面有明显的方向性(如经过拉丝处理的金属,铝制品、光盘等,这些物体往往呈现出特别的高光。)。
2、BRDF的性质:非负性、可逆性、线性可加性、能量守恒…
3、BRDF的测量、存储…
先介绍两个概念,无偏方法和有偏方法。无偏是不管样本有多少,都会得到正确的结果;而有偏是随着样本的多少,结果会变化,如果样本无限多,结果是正确的,那么这种有偏方法就能称为consistent。
1、双向路径追踪BDPT(Bidirectional path tracing)
从光源和相机各打出一条光线,连接终点。BDPT对光源情况比较复杂的场景效果不错。无偏的方法。
2、MLT(Metropolis light transport)
应用马尔科夫链和蒙特卡罗方法。以一定的概率扰动光线的传播,得到一条新的路径。但是收敛的时间不能确定,导致画面比较脏。无偏的方法。
3、光子映射(Photon mapping)
从光源打出光线,到漫反射停止,再从相机打出光线,到漫反射停止,收集平面上周围的N个光子,光子越多就越亮。当N取值小,画面会脏,当N取值大,画面会糊。是consistent的方法,打出无限多的光线,得到的结果很真实。对SDS(specular-diffuse-specular)的路径和caustics(中文翻为焦散但不准确)效果不错。
4、Vertex connection and merging(VCM)
BDPT和Photo mapping的结合,想法是对BDPT,如果两边打出的光线不能连接,也不浪费这样的光线,利用光子映射把能融合的光子融合起来。常被用于电影产业。有偏的方法。
5、实时辐射度 Instant Radiosity(IR)
把光线打出,然后把光线的落点也当成光源,进行渲染。优点是计算快,而且对漫反射场景渲染效果好,缺点是不能处理glossy材质,而且场景缝隙会产生亮点。
非表面模型:
1、散射介质:
雾、云…视线在物体里任意方向任意距离传播,在每个着点对光源进行着色。
2、头发/毛发:
头发是柱状模型,可以直接反射,也可以发生折射进入内部。
3、颗粒材质
如沙子,可以用程序生成。
表面模型:
1、次表面散射(Subsurface scattering)
光线是可以打入物体内部的,如皮肤、玉石等等。用BSSRDF渲染人物比BRDF效果好。
2、衣物
衣服的渲染分三种,当作表面、当作散射介质、当作实际的构造。实际的构造指的是,纤维构成股,股构成线,线编制成衣服。
3、细节表面 Detailed appearance
真实的世界很复杂,物体的表面会有瑕疵、磨损。微表面模型的BRDF的NDF(法线分布)不该是均匀的。不同材质的微表面模型的NDF呈不同的分布。最近的趋势是波动光学,当物体足够小,光呈波动性,比如纯色的物体,当在一个光源下进行观察时,呈花色。
程序化生成 Procedural Appearance
利用噪声函数生成三维的纹理。
拓展:闫令琪老师博士期间的三个研究:动物毛发模型、实时光线追踪、细节表面。
传感器接收到的是irradiance,无法判别方向,所以传感器需要镜头(透镜)来给接收到的光按方向区分开,不然就是模糊的一片。传统的针孔照相机的原理也是如此(针孔照相机无景深效果,景深是因为有透镜)。
焦距和fov(field of view):焦距越短,fov越大(广角镜头)。
光圈和fov:光圈越大,f数越小(f数是焦距/光圈直径),虚化越重。
曝光三要素:光圈、快门、ISO。
defocus blur:coc(circle of confusion)指的是,是因为透镜的原因,实际拍照时,被摄物体不在焦点,成像不是清晰的一个点,而是变成coc。光圈大,coc大,虚化重。
depth of field:景深(清晰焦段的深度),只要coc足够小(比像素小),得到的画面就是清晰的,而在焦点附近,有一段深度(景深),我们认为它是清晰的。大焦距,浅景深;大光圈,浅景深。
全光函数:θ、φ(观察的方向)、Vx、Vy、Vz(位置)、λ(波长/颜色)、t(时间)
光场:θ、φ(观察的方向)、Vx、Vy、Vz(位置)。光场即是,在任何一个位置,任何一个方向,都能知道光的强度。因为光是无限的直线,所以可以简化为四维函数(用包围盒围起一个场景,通过查询uv坐标确定位置;或可以用两个平面上的两对uv确定)。
光场相机:相机的传感器接收到的是irradiance,无法确定光的方向,光场相机就是对每个单位面积的irradiance,加一个透镜进行分方向的操作。光场相机可以在后期重新对焦、移动相机位置。
光谱:在光谱上400-700nm波长的光为可见光。
谱功率密度SPD(spectral power distribution):光线在光谱上的能量分布(不同光线的SPD不同,所以本身能被人看见的颜色也不同)。
注:光线本身是不带颜色的,颜色是人赋予的属性。就像如果收音机是生物,那光线对它来说是带有声音属性的一样。
人眼相当于照相机,瞳孔相当于光圈,晶状体调节焦距,视网膜相当于传感器。视网膜上有两种细胞,柱状细胞和锥状细胞,前者感受光的强度,后者感受光的颜色。锥状细胞分为三种,S、M、L,对光的不同波长的感受能力不同。人观察到颜色的过程是:光线被三种细胞感受到,光线本身的SPD(光在不同波长的分布)和三种细胞对不同波长的感受曲线进行积分得到的三个数S、M、L,传到人脑的视觉部分,得到的颜色的感知。
CIE RGB:色域有限,要得到某些颜色,r需设为负值。
CIE XYZ:人为设置
HSV:饱和度、色相、亮度
CIELAB:三轴,亮度和两对互补色(红-绿,蓝-黄)
CMYK:用于印刷,减色系统(RGB:加色)
animation怎么翻译?模拟、仿真、动画…大致就是考量模型怎么动起来。可以是衣服仿真,可以是流体仿真……animation是和渲染分割开的单独步骤,先考量顶点的位置,再进行渲染。
1、质点弹簧系统(mass spring system)。需要考虑的是弹簧拽着的力、系统的损耗。可以通过质点弹簧系统模拟绳子、布料。
2、粒子系统(partical system)。大致需要考虑的是粒子间的排斥力、吸引力、整体的方向。可以模拟流体、飞鸟……
1、正向运动学。直接控制每个节点的位置。缺点是不能让艺术家直接地控制。
2、逆向运动学。直接拽着叶节点移动。
rigging的中文:蒙皮、操偶、软选取……
如果想使一个模型运动,控制所有的顶点移动,太繁琐,所以为模型加上控制点(骨骼),通过控制控制点,周围相关联的顶点都跟着移动。RIGGING的实现可以参考之前的贝塞尔曲线,不直接控制,而是通过控制点间接控制。对模型形状变化做blend,实际blend的是控制点。
idea——原画集——完整的设计(人物、故事、场景)——layout(布景)——建模——材质——rigging——animation——VFX(视觉效果)——lighting&渲染——合成——2DVFX——颜色校正——done。
欧拉方法:不稳定、有误差
优化的欧拉方法:中点法、自适应步长调整(按不同的ΔT跑欧拉,选择最优步长)、隐式欧拉方法、Verlet积分(基于位置)。引申:龙格库塔解ODE(常微分方程)
欧拉方法和拉格朗日方法、MPM(material point method,结合欧拉、拉格朗日方法)。
PBR(Physically Based Rendering):基于物理的渲染;PBR是一套框架/工作流,基于此框架可以很好地实现真实感渲染的效果
包括:
需要满足三个条件:
微平面理论的理想是:将物体表面做成无数微观、有随机朝向的理想镜面反射的小平面。
概念:出射光的能量永远不能大于入射光的能量。
表现:随着粗糙度的上升,镜面反射区域会增加,为了保持能量守恒,镜面反射区域的平均亮度会下降。
使用能量反射方程:
能量反射方程的理解:
出射光的强度 = 入射光导致的反射光的强度 + 自身发出的光的强度
入射光导致的反射光的强度 = 反射系数/BxDF * 入射光强度 * 入射光的衰减
入射光的衰减:半角向量 dot 法线 (半角向量是入射光和视线的夹角)
双向反射分布函数 / Bidirectional Reflectrance Distribution Function
BSDF = BRDF + BTDF (T :Transmitted Scatter / 折射)
一般把BRDF分为漫反射和高光反射(包括纯镜面反射
漫反射部分通常采用Lambert光照模型(经验模型)
传统的高光反射部分采用Phong或Blinn-Phong模型(都是经验模型);
Phong考虑的是反射光线的方向和视角方向,Blinn-Phong考虑的是半角向量和法线。
基于物理的高光反射,是利用Cook-Torrance反射率方程来计算光照,此方程对BRDF的部分进行更新,包括漫反射和高光反射,从而更新能量守恒公式,得出一个视觉上正确的结果。
法线分布函数考量了法线对光线衰减带来的影响,是GGX光照模型,此前涉及到的Blinn-Phong光照模型也考量了法线对光线衰减带来的影响,但GGX光照模型的效果更好。
基于物理的材质里面,使用的是微平面理论,每个微平面互不干扰,但实际世界中,物体表面的凹凸是存在相互遮挡的。
几何(遮蔽)函数所做的就是在统计学上近似地求解了微平面间相互/自身遮蔽的比率。遮蔽会导致光线能量的损耗。
ggx是一种微表面反射光照模型,这种光照模型材质可以很好地表现金属高光边缘的消散效果。
使用Cook-Torrance反射率方程来计算出射光
Lamert处理漫反射
迪士尼提出的BRDF规范,原则包括:
用了11个参数即可非常真实地模拟出金属、非金属以及不同粗糙度的材质光照结果:
分为高光流/漫反射流和金属度/粗糙度两套工作流,区别主要在于高光流提供SpecularMap,金属流提供MetallicMap。
像素的最终亮度取决于相机的设置,EV值决定了是否产生Bloom的效果。当入射的亮度超过了传感器本身最大的亮度值,就会产生Bloom。
对于自发光表面:可以给设计师提供工具控制Bloom。给自发光表面定义“曝光补偿”,用于调整它的强度,确保该强度超过饱和点。该项是场景全局值,额外的曝光被注入场景内,使得自发光表面变亮或者变暗。
曲面细分,英文称Tessellation,如果直译的话应该译作“镶嵌化处理技术”。由ATI开发,微软采纳后将其加入DirectX 11,成为DirectX 11的组成部分之一。由于这种技术广泛的应用在曲面的几何处理上,因此国内翻译时通常译作“曲面细分”。但实际上,这种技术不是只能用在曲面的细分处理上。
那么为什么不直接使用更精细的模型、贴图呢?因为使用曲面细分着色器可以根据距离或一些规则动态调整模型的复杂度,带来更好的性能;可复用性。
在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader),几何着色器的输入是一个图元(如点或三角形)的一组顶点。几何着色器可以在顶点发送到下一着色器阶段之前对它们随意变换。然而,几何着色器最有趣的地方在于,它能够将(这一组)顶点变换为完全不同的图元,并且还能生成比原来更多的顶点。
做一些几何的绘制,可以用来做一些简单的几何动画;可以做爆破之类的特效;也可以定义草地,结合曲面细分着色器,得到一个动态调整密度的草地,远疏近密。
Hull Shader主要作用是用来定义一些细分的参数,比如每条边上如何细分和内部三角形如何细分
Domain Shader它的空间在重心空间,我们需要把这个重心空间转换到我们要用的空间.
技术美术百人计划】图形 3.3 曲面细分与几何着色器 大规模草渲染
LearnOpenGL-几何着色器
百人计划 学生笔记
百人计划 学生笔记
【Joseph Bennett, Global Illumination on a Mobile Phone: Scalable Real-time Global Illumination using Sparse Radiance Probes, 2019】
Light Probes 基本理论介绍
还是要从Kajiya在1986年的渲染方程讲起…
对于Kajiya的渲染方程求解,需要考虑积分项和BRDF,所以效率不高,一个优化的想法是,假设场景中所有物体的表面都是漫反射兰伯特材质——
则渲染方程可以写作——
其中ρ就是BRDF项,除以PI是经过数学推导的。但还是需要积分的求解,而光小球的概念则不需要积分求解——
通过在场景中摆很多小球,在计算某点在某个方向观察的亮度时,就可以通过计算这些小球上对应的方向的光照强度值得到的加权平均来近似。就好像每个小球是一个球形的Texture2D纹理,按照方向对所有小球上纹理对应方向的颜色值做加权平均即可快速得到某点的颜色值。
而公式中的ωi,即是小球的权重,小球的权重是通过计算小球到待计算光照的点的距离而得出的。
在计算场景中左上角的物体对x点的光照影响时,如果所有光小球的采样都是按照x点到物体的方向来,那结果就不对,所以要让光小球向物体方向进行采样。
对于一个比较大的场景,可能要放比较多的光小球。每个光小球存储一个Texture2D的纹理的话,内存消耗是比较大的,所以引入球谐函数做一个近似计算。
要了解球谐函数,就要先讲一下傅里叶变换 Furious transform——
对于,一个在时域上分布的信号,可以在频域上,拆解成一个系数基函数的和来模拟,这就是傅里叶变换。
其中c是系数,f是基函数。
而球谐函数相当于是在极坐标系下的傅里叶变换,将小球在极坐标系下(θ, φ)的值f(θ, φ)表示为一系列系数基函数的和。
如下图表示了取前n项所获得的小球和原小球 / 原信号的对比示意图。
Unity使用球谐函数一般取前3-4项。
使用cubemap通过球谐函数来生成Light Probes里的Probe的过程如下图所示:
首先对于cubemap的图像,表示成球的样子(其中越亮的地方,在图中的小球的展示就越突出),作为原始图像信号,用球谐函数表示,然后取前n项作为Probe的表示。
这样做的好处就是
前向渲染就是先计算光照再裁剪,延迟渲染就是先裁剪再计算光照。
最基础,大多数引擎使用的标准。前向渲染的过程是,对每个几何体,逐个进行渲染管线的绘制,从顶点着色器、曲面细分着色器、几何着色器、片段着色器,生成一系列片段,然后进行深度测试,对最终可见的像素进行渲染生成。前向渲染是线性的,当场景中有多个几何体时,就挨个对其进行渲染,完成一个再继续下一个。
前向渲染的好处:
当需要使用多个着色模型,渲染管线里存在多种着色模型和渲染技术时,前向渲染很好支持,而且对于半透明物体的生成效果不错。在大多数手游里采用此种渲染方式。
前向渲染的缺点:
(1)无效渲染太多,在渲染管线里,几何体间存在遮挡关系,按照前向渲染,渲染完一个几何体后,再渲染下一个几何体,如果之前渲染的几何体的部分被后一个渲染的几何体挡着,那被挡着的这部分就是无效的计算。
(2)难以支持过多的光源,对于需要逐像素进行计算的光源,渲染一个几何体时需要做对每个光源逐像素进行光源计算。比如m个物体,n个光源,那么渲染整个场景需要进行m*n次光照计算,而且其中还有许多计算被挡住了。
解决前向渲染的一些方法 / 前向实时剔除:
(1)early-z
对于片段,先进行深度测试,先计算光照
对于光栅化生成的片段,先进行深度测试,利用zbuffer剔除看不见的片段,再进行片段的着色、光照计算。而不是传统的图形渲染管线里的,先进行片段着色、光照计算,再进行深度测试。
(2)z-prepass
执行两次顶点着色器,第一遍计算深度,渲染时只渲染通过深度测试的片段
early-z在计算光照前对片段进行了深度剔除,但还是存在一些问题,比如对第一个几何体进行渲染时,此时深度通道/zbuffer是没有深度信息的,所有的片段都通过了深度测试,进行了渲染,但其中有一些渲染是无效渲染,是会被之后的片段所遮挡的片段。所以z-prepass针对这样的问题,对整个场景中的所有模型都渲染一遍,只计算深度信息,将所有深度信息写入z-buffer,然后再进行一次渲染,只渲染通过深度测试的片段。这种方法虽然避免了因深度遮挡而产生的无效的光照计算,但是进行了两次顶点着色器,所以适用于顶点数量不是很多但是光源很复杂的情况。
(3)Hi-z
上面都是在GPU阶段进行的,Hi-z是在CPU阶段进行的,对提交到GPU的几何体,先进行遮挡测试,如果几何体被遮挡了就不会提交到GPU。这种方法直接减少了GPU需要渲染的几何体数量,适用于几何体数量多且细碎的场景。
延迟渲染就是把光照计算延迟到深度测试之后的渲染方式,适合场景中实时光照很多的情况,可以对每个光源按逐像素的方式计算。
z-prepass也是先进行深度测试,再进行光照计算,那么延迟渲染跟z-prepass有啥区别呢?区别在于g-buffer,z-prepass在计算完所有几何体的深度之后,对几何体还是逐个完成渲染的,但延迟渲染计算完深度之后,把所有几何体的信息发送到g-buffer,之后就跟有多少个几何体、有多少模型没关系了,接下来的操作都是对g-buffer的数据整体进行的。g-buffer保存渲染一个像素所需要的信息,不同的引擎对于g-buffer的处理也不一样,对于一个pbr的引擎来说,可能包括法向量、颜色、深度、材质等等。
如果前向渲染m个物体和n个光源计算产生的计算量是m*n,那么延迟渲染的计算量就是m+n。这是因为延迟渲染的思路就是把所有几何体的信息都渲染到g-buffer中,然后对g-buffer整体进行光照计算,g-buffer中存储的信息都是最终会呈现在屏幕上的,不存在无效计算。
延迟渲染分为GBuffer阶段和光照阶段,我们来看其具体步骤:
延迟渲染的优点: 所有几何体都写入g-buffer,光照计算跟场景中有多少个物体、多少个三角形无关,不存在无效的计算,只渲染可见的像素。
延迟渲染的缺点:
(1)要求场景中shader/渲染方式的种类比较少,如果渲染方式很多,对每个模型、材质都需要使用不同的shader,那么在g-buffer里该存哪些信息呢?延迟渲染对于程序和项目管理是个优点,可以提高性能、提升效率,但是对于美术是个缺点,限制了效果的表现。
(2)前向渲染只需要深度缓冲和最终的颜色缓冲就行,但延迟渲染需要缓冲的信息太多了,带宽开销增加,所以手机多使用前向渲染,延迟渲染不适用于手机,因为手机的带宽小,没法在缓冲区保存这么多数据。
原神的手游尝试了延迟渲染。
(3)对于半透明无能为力,半透明物体需要等不透明物体延迟渲染渲染完后,再用前向渲染渲染出来。
虽然延迟渲染已经让减少了光源数量带来的计算开销,但光源更多了还是撑不住
所以引入分块延迟渲染,g-buffer分成小块,对每个块分析受到哪些光源影响,然后逐块进行着色。
现在的游戏引擎比如Unity和UE4 ,不管是延迟渲染还是前向渲染都有做支持,你可以在这两个引擎中选择一种渲染管线来使用。虽然二者都支持两种渲染管线,但是给人们的印象里,还是Unity用前向渲染的比较多,而UE4则几乎是代表了延迟渲染,实际体验中也可以得出相似的结论。
现在一般做次世代游戏都选择UE4,比如《地平线零之曙光》《盗贼之海》《星球大战》等。
而风格化的游戏都不太选择UE4,Unity对风格化的游戏具有更好的支持,可以很方便的配置各种渲染效果。而UE4如果想要方便的获得一个风格化效果,只能是靠后处理来实现,而要想获得真正理想的风格化效果,就需要花很大力气来改管线了。
手游一般都是前向渲染,而且国内的手游一般都是用Unity做的,最近这些年也有很多项目开始使用UE4做手游了,但是即使是使用UE4做手游也都是前向渲染。
这里不得不提一个异类就是《原神》,作为使用Unity开发的手游,居然使用了延迟渲染管线,可以说是一次非常有前瞻性的尝试了。我认为随着以后手机的性能不断进步,人们对画面质量的要求越来越高,延迟渲染也迟早会在手机上铺开的。