glTF
glTF是一个优秀的三维数据规范,其中有很多细节都值得我们学习,按照我的理解,可以分为三大块:
- Accessor数据访问机制 一套访问二进制数据的规范,将逻辑层和数据层隔离 同传输和读取以及存储灵活性上都有上佳表现
- 针对OpenGL渲染进行的数据结构优化 封装:Mesh与Primitive中的Vertex Buffer, Index Buffer,Vertex Array,还有Texture与State Management,在灵活和易用上都有不错的设计 压缩: 针对3D Geometry的Draco压缩#6191
- Scene& Node 节点间的依赖和位置关系 实例化设计
glTF2.0
相比1.0,glTF2.0的改变可以用“一增一删修修补补”来概括,主要有:
- 新增PBR(Physically-Based Rendering )材质 本文重点,稍后继续
- 删除Technique和Shader 个人理解:剥离渲染部分,glTF规范更专注数据层面 更好的融入Web体系
- 其他 明确两个非重点:不支持流媒体,结构以JSON格式呈现,原始数据在可读性上并不友好 兼容性:2.*之间版本向下兼容,但不保证2.0的数据在1.0的Parser的兼容性(Cesium目前解析1.0的代码时,会自动升级为2.0) 顶点索引支持UNSIGNED_INT 字节对齐,Accessors Bounds,实例化设计(MorphTarget) 3D Geometry Compression: Draco
Light and material interaction
下面,正式进入本文的重点。
计算模型的反射是渲染的核心,这牵扯到物体光照和缓冲期纹理的像素颜色。当前的反射模型分为两类,实用型和理论型。比如我们常见的ADS光照模型属于前者,计算效率高,但在(物理)理论角度的正确性不足,实际上,这类模型的目的是在物体表面提供亮光,让物体具备立体效果,这也是为什么,我们看到游戏场景绚丽,但缺少真实感,因为违背大脑对自然规律的认知(比如能量守恒)。理论型模型则牵扯到大量的实验数据和计算量,但更有真实感,比如Disney的动画,就具备高度的真实性,很大的因素就是他们采用物理渲染引擎和对应的材质。PBR技术在使用上不难,在Web上,Cesium,Three等都已经支持,但要理解,里面的知识点好多。
首先,光落在你脸上有千百种效果,但结果只有两个:折射和反射。初中物理告诉我们他们符合该公式:
。N表示不同介质见的折射率,根据能量守恒定律,一束光的能量等于反射光和折射光总和。
如下是一个完整的示意图:一部分能量转化为反射光,绿色部分(specular);一部分能量折射穿透表面,要么被吸收(转化为热能),要么被溅射出来,红色部分(diffuse)。当然,完整的光照模型还需要考虑另外两种独立的光源:环境光(ambient)和自发光(emissive)。
传统的光照模型遵守该自然规律,但有两个问题,第一,反射光和折射光的计算各自独立,没有考虑能量守恒(energy conservation);第二,偏重考虑光和物体表面的交互(光线),但材质属性上的考虑不足,如下图roughness(1),subsurface scattering(2)以及fresnel(3),在传统模型中都没有针对性的考虑。
1. Energy conservation:反射越强(白点),漫反射就约弱(黑色区域),能量和不变
2.Roughness:光的反射计算时,我们通过normal map来反映该物体的orientation,这由物体的形状来决定。但在真实世界,物体表面粗糙程度各不相同,而该属性无法通过normal来体现
3. subsurface scattering:半透明,透明,厚度等材质对折射的影响
4. Fresnel:边缘的反射率会变大
举个例子,杨过学打狗棒法,洪七公教了招式,这算是传统光照模型的程度,有模有样,但黄蓉传授心法后,杨过才能心领神会,运用自如。这个心法,就是如下的数学模型:
- L(P,V)是经过P点反射(diffuse或specular)后进入视点V的光
- L(P,-V)是从-V方向射入P点的光
- R是对应的BRDF函数,会考虑上述各种物理现象
不难理解,对各个方向的光源积分,就可以得到对应点P的结果。我们先侧重理解,文末会给出BRDF的专门介绍。
上图是针对100种材质,采用5种常见的材质的效果对比,可见Cook-Torrance(BRDF的一种)在实验中相对有较好的表现,而glTF2.0就是采用 Cook-Torrance Model。
PBR
首先,如果模型采用PBR材质,最少需要两个两张纹理:albedo和metalRoughness。Albedo对应该模型的纹理,就是我们常用的的diffuseColor。metalRoughness对应rgba的g和b两位,范围是[0,1],对应模型的g(roughness)和b(metallic)。Roughness之前介绍过,这个metallic属性,用来描述该模型对应P点在绝缘体和金属之间的一个度,金属的反射率相对较高,该系数用来调整diffuse和specular的能量分配。如下是diffuseColor,metallic和roughness对应模型的效果。原图效果,后两张算是灰度图,值越大越白,越偏金属(图2),也越粗糙(图3)。
这里,glTF的采用的是Disney给出的BRDF公式:
,diffuse采用的是Lambert模型:
,C是漫反射光的颜色Color,这里认为该点微观上是一个平面,漫反射以一个半圆180°均匀反射,所以除以π,当然还有其他的模型(Oren-Nayar)。如上的模型,对应的diffuse效果如下,在点光源下,对应metallic纹理,塑料材质会有较多的漫反射,而金属材质下,大部分能量则转化为镜面反射:
Specular就有点复杂D,F,G是三个函数,应用角度看,我们并不需要理解它们,直接套公式即可,感兴趣的同学仔细看一下下面这张图以及右侧对应的具体参数。
套用该公式,我们获取的specular效果如下,右侧是diffuse specular的效果:
接下来,是对环境光的考虑,所谓环境光,就是来自四面八方的光,做一遍如上的计算,然后求和,就是前面对应的BRDF的积分公式,可想而知计算量很大。这里数学可证,能够将该积分近似拆分为两部分:Environment Map和BRDF lookup table。前者根据不同的Roughness,计算对应光源的平均像素值。后者根据Roughness和视角计算出对应颜色的调整系数(scale和bias)。虽然两部分的计算量比较大,但都可以预处理,通常Cube Texture都是固定的,我们可以预先获取对应的Environment Map,根据Roughness构建MipMap;后者只跟Roughness和视角V的cos有关,一旦确定了BRDF模型,计算公式是固定的,也可以预先生成U(Roughness) V(cosV)对应的 Texture:
Cook-TorranceModel对应的LUT
这样,我们可以获取环境光的计算公式如下:
finalColor = PrefilteredColor * ( SpecularColor* EnvBRDF.x EnvBRDF.y );对应的效果如下,在这个基础上,我们在添加对应的Ambient Occlusion Texture,Emissive Texture,就是右侧效果。
上面是PBR渲染的完整思路,整个过程其实和传统的ADS并没有差别,只是每一部分采用不同的计算公式,考虑更全面,计算量也随之复杂,因此,为了保证real time的效率,通过预处理的方式来简化计算量。
BRDF
BRDF是Bidirectional reflectance distribution function的缩写,下面介绍一下该数学模型对应的F,G,D函数,以及积分求和,而这,也是PBR中最核心,但也最难理解的部分。
首先,光是可逆的,上面也提到,要遵守能量守恒,因此要满足两个条件:
在我阅读的论文里,最常见的BRDF模型就是Cook-Torrance specular BRDF和Lambert diffuse BRDF:
(F)resnel reflectance
既然是Fresnel,不难理解,该函数主要是用来计算不同介质之间光的折射,简化后的Schlick公式可以取得近似值,公式如下,在中心点时l和h为零度角,cos=1.为F(0),为该材质的base reflectivity,在45°时,还基本不变,但接近90°时,则反射率则迅速提升到1,符合之前Fresnel的变化曲线
Normal (d)istribution term
该函数用来描述材质的roughness,在能量守恒下,控制物体在反射与折射间的分配,glTF中采用的是Trowbridge-Reitz GGX公式,其中α是唯一参数,而h可以通过粗糙度α和法线n求解:
(G)eometric term
表示从L光源能够到达V视角的概率,这里glTF采用的是GGX,而Cesium则是Schlick模型:
最后考虑到能量守恒,需要在diffuse和specular之间添加参数,控制两者的比例,这里也有多种方式,比如前面提到的Disney给出的公式(glTF和Cesium中都采用该公式),也有论文里提到的diffuse Fresnel term:
Importance sampling
上面给出了一个点光源的计算,对于环境光,就有一个求和的过程,这里,对该积分做了如下两步近似求解:
正是因为如上的近似推导,证明我们可以通过EnvironmentMap和BRDF lookup table两张纹理,对Sum求和的过程预处理,减少real time下的计算量。具体的代码在论文中,不妨阅读理解一下,要展开说细节太多了,我仅把个人觉得有意思的几点放到others中,都可以在论文中找到更详细的解释。
Others
Sampling
Ray Tracing时,需要对光源进行采样,其大概的思路是通过采样算法,创建一个均匀分布的二维点集合,然后映射到对应的光源,但不同材质下分布并不均匀,因此还需要通过PDF(probability density function)给出其对应的权重。这个过程下,主要有三个知识点:Hammersley,ImportanceSampleGGX,pdf。
Gamma correction
同样一张图片,为何不同显示器下效果会有差别,这下终于找到原因了。也算是历史遗留问题,算是CRT显示器的产物。
因为BRDF的过程需要对原始Color进行计算,需要将纹理颜色解析成真实的颜色,计算后再编码成Gamma对应的颜色。
Rational Approximation
类似泰勒公式,但泰勒公式仅对X(0)原点附近的精度有保证,同时,我们对应的F,D,G函数也并不容易求出N阶导数,以及能够创建满足先决条件的泰勒展开式,比如D正态分布,要求展开式积分为1。
因此,在论文中给出了Rational Approximation的算法,比如真实的Fresnel函数如下,通过该算法才得以简化,实用,就问你服不服。