# 袋鼠云面试 **Repository Path**: agiser0/kangaroo-cloud-interview ## Basic Information - **Project Name**: 袋鼠云面试 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-04-11 - **Last Updated**: 2024-04-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 渲染管线 CPU :给 GPU 下达命令渲染什么样的画面 准备好以下内容给 GPU(顶点数据 vertexBuffer、索引数据 indexBuffer、摄像机数据<位置、方向、所看的范围 {FOV}、近平面以及远平面的裁切>、光线(其他光和太阳光的方向、颜色、强度等信息)、shader(代码、贴图、参数){材质球,决定三D模型以什么样的效果绘制到屏幕上}) 显卡(GPU)图形处理器 **GPU 接收到 CPU 的命令,将CPU传递过来的一些数据进行处理,最终输出结果绘制到屏幕上。这一连串的处理就是渲染管线。** ![QQ截图20240410095135](assets/QQ截图20240410095135.jpg) 绿色:可以进行编程修改的阶段;也是 shader 发挥作用的地方 Vertex Shader:顶点着色器,使用到模型顶点数据,对模型顶点的位置和其他属性做一些额外的处理(顶点动画,顶点坐标变换) Triangle Processing:遍历模型上的所有三角形,把模型的三角面投影到屏幕上。 Rasterization:把三角形变成屏幕上对应的像素(光栅化);GPU 做了 剔除遮挡、插值计算 Pixel sahder:模型着色阶段。根据灯光做出明暗变换的效果,根据贴图坐标去采样贴图 Frame Buffer:进一步处理(看锯齿、校色、添加 bloom(辉光)之类),后处理阶段 ## PBR 基于物理渲染(物理网格材质) ### 简介 基于物理的渲染(PBR(Physically Based Rendering))是计算机图学中的着色方法,目的在于模拟光线的物理行为及其与材质的交互作用,从而实现逼真的视觉效果。PBR采用基于物理精确公式的算法来重现真实世界中的材质,从而生成一致性的逼真环境。PBR 是一种拟真渲染技术,主要体现在材质上。 满足以下条件的光照模型才能称之为PBR光照模型: - 基于微平面模型。 - 能量守恒。 - 使用基于物理的BRDF。 ## 1、理论 ### 1.1微平面 ![img](assets/1617944-20190425001035853-530443647.png) ​ 该理论认为**在微观上所有材质表面都是由很多朝向不一的微小平面组成**,有的材质表面光滑一些,有的粗糙一些。 ![img](assets/1617944-20190425001105154-1541680139.png) ​ 当光线射入这些微平面后,通常会产生镜面反射。对于越粗糙的表面,由于其朝向更无序,反射的光线更杂乱,反之,平滑的微平面,反射的光线更平齐。 ​ 从微观角度来说,没有任何表面是完全光滑的。**由于这些微平面已经微小到无法逐像素地继续对其进行细分,因此我们只有假设一个粗糙度参数**,然后用统计学的方法来概略的估算微平面的粗糙程度。 ​ **半角向量(Halfway Vector)** ,可以基于一个平面的粗糙度来计算出某个向量的方向与微平面平均取向方向一致的概率。这个向量便是位于光线向量l𝑙和视线向量v𝑣之间的中间向量 。**越多的微平面取向与其半角向量一致,材质镜面反射越强越锐利。加上引入取值0~1的粗糙度,可以大致模拟微平面的整体取向。** **计算** ![img](assets/1617944-20190309225859464-558296878.png) ````glsl // lightPos是光源位置,viewPos是摄像机位置,FragPos是像素位置 vec3 lightDir = normalize(lightPos - FragPos); vec3 viewDir = normalize(viewPos - FragPos); vec3 halfwayDir = normalize(lightDir + viewDir); ```` ![img](assets/1617944-20190425001124143-1042432350.png) *粗糙度从0.1~1.0的变化图。粗糙度越小,镜面反射越亮范围越小;粗糙度越大,镜面反射越弱。* (金属度调为 1;演示) ### 1.2能量守恒 ​ 出射光的总能量不超过入射光的总能量(自发光材质除外)。PBR会简化折射光,将平面上所有折射光都视为被完全吸收而不会散开。 进入材质内部的折射光就是入射光减去反射光后余下的能量。例:材质粗糙度越大,反射的范围越大,但整体亮度变暗。 **镜面反射和漫反射** ​ *照射在平面的光被分成镜射出表面,成为漫反射。*面反射和折射光,折射光在跟物体微粒发生若干次碰撞之后,有可能发射出表面,成为漫反射。**PBR会简化折射光,将平面上所有折射光都视为被完全吸收而不会散开。** ![img](assets/1617944-20190425001140230-2041289785.png) **计算** ​ 可以先计算镜面反射部分,此部分等于入射光线被反射的能量所占的百分比。而折射部分可以由镜面反射部分计算得出。 ````glsl float kS = calculateSpecularComponent(...); // 反射/镜面部分 float kD = 1.0 - kS; // 折射/漫反射部分 ```` ### 1.3反射方程(PBR的渲染方程) ​ **渲染方程**(Render Equation)是用来模拟光的视觉效果最好的模型。而PBR的渲染方程是用以抽象地描述PBR光照计算过程的特化版本的渲染方程,被称为**反射方程**。 ![QQ截图20240410101654](assets/QQ截图20240410101654.jpg) ![QQ截图20240410110418](assets/QQ截图20240410110418.jpg) ![QQ截图20240410193520](assets/QQ截图20240410193520.jpg) ​ 反射公式计算了点p在ωo方向上被反射出来的辐射率Lo(p,ωo)的总和。或者换句话说:L0计算的是在ωo方向的眼睛观察到的p𝑝点的总辐照度。 ​ 反射率方程概括了在半球领域Ω内,碰撞到了点p上的所有入射方向ωi上的光线的辐射率,并受到fr的约束,然后返回观察方向上反射光的Lo **等式的左边** 𝐿𝑜(𝑝,𝜔𝑜),它表示在某点*p*,出射光线𝜔𝑜的***辐照度***;*暂时可以将其理解成在这一点上我们最终着色的颜色。 **等式右边** ∫Ω的含义就是在 Ω范围内进行积分,而在渲染方程中,Ω表示的通常是一个半球区域。意思就是我们在一个半球区域内将所有的入射光线计算fr(p,ωi,ωo)Li(p,ωi)n⋅ωi的值,然后让这个区域中的所有结果加起来。 注:另外一边的半球因与视线方向相反,不能被观察,也就是辐射通量贡献量为0,所以被忽略。 ![img](assets/5e97c361021444299f1254431908a576tplv-k3u1fbpfcp-jj-mark3024000q75.awebp#) 对于每一根入射光线,都根据公式 **fr(p,ωi,ωo)Li(p,ωi)n⋅ωi** 计算其结果,最后将结果进行相加。(**定积分**)在半球Ω按一定的步长将反射方程离散地求解,然后再按照步长大小将所得到的结果平均化 ````glsl int steps = 100; // 分段计算的数量,数量越多,计算结果越准确。 float dW = 1.0f / steps; vec3 P = ...; vec3 Wo = ...; vec3 N = ...; float sum = 0.0f; for(int i = 0; i < steps; ++i) { vec3 Wi = getNextIncomingLightDir(i); sum += Fr(P, Wi, Wo) * L(P, Wi) * dot(N, Wi) * dW; } ```` ***n*⋅*ω**i***:**光线的方向向量和平面法向量的点积 𝑛⋅𝜔𝑖 * 就是 **cos*θ*** ,辐射率受到入射光线与平面法线间的夹角*θ*余弦值cos*θ*的影响。当直接辐射到平面上的程度越低时,光线就越弱,而当光线完全垂直于平面时强度最高。 ***Li*(*p*,ω**i)**:则是表示在p点,入射光线𝜔𝑖**的辐照度,可以理解为是**这一点的光线强度**。 **𝑓𝑟(𝑝,𝜔𝑖,𝜔𝑜)**:**BRDF**项。BRDF可以近似的求出每束光线对一个给定了材质属性的平面上最终反射出来的光线**所作出的贡献程度**。 **辐射率(Radiance)**:在这里用L来表示。辐射率被用来量化来自单一方向上的光线的大小或者强度。 **辐射通量**:辐射通量Φ表示的是一个光源所输出的能量,以瓦特为单位。**RGB**来作为辐射通量表示的简化。 **辐射度**:也可以简单地理解成颜色值。 **立体角**:它可以为我们描述投射到单位球体上的一个截面的大小或者面积。投射到这个单位球体上的截面的面积就被称为立体角(Solid Angle),你可以把立体角想象成为一个带有体积的方向: **辐射强度**:辐射强度(Radiant Intensity)表示的是在单位球面上,一个光源向每单位立体角所投送的辐射通量。 **辐照度**:**所有**投射到点p𝑝上的光线的总和 **∫**:积分(Integral)的数学手段,也就是反射率公式中的符号∫,它的运算包含了半球领域ΩΩ内所有入射方向上的dωi。积分运算的值等于一个函数曲线的面积, **𝑓𝑟**,被称为BRDF,或者双向反射分布函数(Bidirectional Reflective Distribution Function) ,它的作用是基于表面材质属性来对入射辐射率进行缩放或者加权。 ### 1.4BRDF 双向反射分布函数,它接受入射(光)方向ωi,出射(观察)方向ωo,平面法线n以及一个用来表示微平面粗糙程度的参数a作为函数的输入参数。BRDF可以近似的求出每束光线对一个给定了材质属性的平面上最终反射出来的光线所作出的贡献程度。 ![QQ截图20240410110418](assets/QQ截图20240410110418.jpg) ***Cook-Torrance*** 模型。(常用版本) ![QQ截图20240410112816](assets/QQ截图20240410112816.jpg) - *ω**o*:出射光线方向 - 𝜔𝑖*ω**i*:入射光线方向 - 𝑛*n*:法线方向 ​ D、B、F 函数都是用来估算相应的物理参数的,用来实现相应物理机制的每种函数都有不止一种形式。 - D:Normal Distribution Function **法线分布函数** - G:Geometry Function **几何函数** - F: Fresnel Function **菲涅尔方程** #### 1.4.1法线分布函数(D): 从统计学上近似的表示了与某些(如中间)向量hℎ取向一致的微平面的比率。 ![QQ截图20240410113017](assets/QQ截图20240410113017.jpg) h是用来测量微平面的半角向量,α是表面的粗糙度,n是表面法线 如果将h放到表面法线和光线方向之间,并使用不同的粗糙度作为参数,可以得到下面的效果: ![img](assets/1617944-20190309225859464-558296878-17128048771913.png) ![img](assets/1617944-20190425001124143-1042432350-17128048645101.png) ​ 当粗糙度很低(表面很光滑)时,与中间向量hℎ取向一致的微平面会高度集中在一个很小的半径范围内。由于这种集中性,NDF最终会生成一个非常明亮的斑点。但是当表面比较粗糙的时候,微平面的取向方向会更加的随机,与向量hℎ取向一致的微平面分布在一个大得多的半径范围内,但是较低的集中性也会让最终效果显得更加灰暗。 **代码** ````glsl //  表面法线向量、视线方向、表面粗糙度 float D_GGX_TR(vec3 N, vec3 H, float a)  {      float a2     = a*a;      float NdotH  = max(dot(N, H), 0.0);      float NdotH2 = NdotH*NdotH;        float nom    = a2;      float denom  = (NdotH2 * (a2 - 1.0) + 1.0);      denom        = PI * denom * denom;        return nom / denom;  } ```` #### 1.4.2菲涅尔方程(F): ​ 在不同观察方向上,表面上被反射的光除以被折射的光的比例。这个比率会随着我们观察的角度不同而不同。我们从理想的90度视角观察光点,即与表面垂直的方向,所有的平面都能够完全反射光线。这时候我们会看到明亮且清晰的镜面反射,因为光线直接被反射回观察者的眼睛,形成了高度可见的镜面反射效果。 Fresnel-Schlick近似表示: ![QQ截图20240410113413](assets/QQ截图20240410113413.jpg) ````glsl //  表面法线向量、视线方向、albedo 漫反射部分、金属度 vec3 fresnelSchlick(float cosTheta, vec3 F0)  {      return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);  } ```` #### 1.4.3 几何函数(G): ![img](assets/1617944-20190425001746103-48046007.png) ​ 几何函数模拟微平面相互遮挡导致光线的能量减少或丢失的现象。粗糙度影像。 ![QQ截图20240410113254](assets/QQ截图20240410113254.jpg) ![QQ截图20240410113259](assets/QQ截图20240410113259.jpg) ​ α的值取决于你的引擎怎么将粗糙度转化成α ​ 为了有效地模拟几何体,我们需要同时考虑两个视角,视线方向(几何遮挡)跟光线方向(几何阴影) ````glsl  float GeometrySchlickGGX(float NdotV, float k)  {      float nom   = NdotV;      float denom = NdotV * (1.0 - k) + k;        return nom / denom;  }    float GeometrySmith(vec3 N, vec3 V, vec3 L, float k)  {      float NdotV = max(dot(N, V), 0.0);      float NdotL = max(dot(N, L), 0.0);      float ggx1 = GeometrySchlickGGX(NdotV, k);      float ggx2 = GeometrySchlickGGX(NdotL, k);        return ggx1 * ggx2;  } ```` ## 2、实现 ### 2.1ue5中 通过指定基础色,调节 金属色(是否是金属材质)、高光度(表面反射光的亮度)、粗糙度(表面的光滑程度)来模拟真实的效果 **基础色(Base Color)**:为材质提供基础纹理色,是Vector3(RGB),它们的值都限定在0~1之间。 **粗糙度(Roughness)**:表示材质表面的粗糙程度,值限定在0~1之间。越粗糙材质高光反射越不明显,金属和非金属的粗糙度有所区别。 **金属度(Metallic)**:表示材质像金属的程度,0是电介质(绝缘体),1是金属。金属没有漫反射,只有镜面反射。 **镜面度(Specular)**:表示材质的镜面反射强度,从0(完全无镜面反射)~1(完全镜面反射。UE4的默认值是0.5。万物皆有光泽(镜面反射),对于强漫反射的材质,可通过调节粗糙度,而不应该将镜面度调成0。 UE deom ### 2.2three.js 中 ​ PBR 材质能更好的模拟金属和非金属材料,更加真实。但是也会占用更多的电脑硬件资源。three.js 不用写这些光照模型算法,通过网络模型材质实现了。两个PBR材质相关的API:MeshStandardMaterial`和`MeshPhysicalMaterial,他们用的光照模型不同。 ![QQ截图20240409191346](assets/QQ截图20240409191346.jpg) ![QQ截图20240409191811](assets/QQ截图20240409191811.jpg) PBR材质相比MeshLambertMaterial和MeshPhongMaterial可以提供更逼真的、更接近生活中的材质效果,当然也会占用更多的电脑硬件资源。 deom three 中设置金属度、粗糙度、环境贴图 、清漆层、物理材质透光率等 ### 2.3WebGL 中源码 ​ 场景的辐射率L度量的是来自光源光线的辐射通量ϕ穿过指定的立体角ω,假设立体角ω无限小,小到辐射度衡量的是光源射出的一束经过指定方向向量的光线的通量。 ​ 当为平面上某个特定的点p着色的时候,所有可能的入射光方向都会经过半球Ω,但只有一个入射方向ωi是直接来自点光源的,又因为我们的场景中只包含有一个光源,且这个光源只是一个点,所以p点所有其它的入射光方向的辐射率都应该是0. ​ ![img](assets/1617944-20190425001858034-592400550.png) 这也将我们带回了在表面半球ΩΩ的积分∫。我们知道,多个单一位置的光源对同一个表面的同一个点进行光照着色并不需要用到积分,我们可以直接拿出这些数目已知的光源来,分别计算这些光源的辐照度后再加到一起,毕竟每个光源只有一束方向光能影响物体表面的辐射率。这样只需要通过相对简单的循环计算每个光源的贡献就能完成整个PBR光照计算。**当我们需要使用IBL将环境光加入计算的时候我们才会需要用到积分,因为环境光可能来自任何方向。**