光照模型分为两种:
  • 一种是基于物理的光照模型(PBR),非常真实的反映了现实世界的光照,但是需要大量复杂的计算
  • 另一种是经验光照模型(如兰伯特),用较为简单的方式模拟了物理光照,也能得到不错的效果

本章节主要介绍经验光照模型

基本概念

光源(Light Source)

  光从光源中发射出来,照到物体表面。对于不同类型的光,需要设置不同种类的光源(例如影响全局的场景光、点光源以及投射灯等等)。
  在Unity中,当我们选择创建光源时,有以下四种选项(反射球暂时先不考虑):

辐照度(Irradiance)

  辐照度是衡量一个光源发出的光的多少使用的单位,用来量化光。

  • 对于平行光,辐照度可以通过计算垂直于光源方向l的单位面积上单位时间内穿过的能量得到。
  • 而存在一定夹角的时候,可以通过求光源方向l和法线之间的夹角的余弦值求得(就是投影到垂直平面的方向的分量)。
      当光线间隔相同时,光与平面的夹角越大,单位面积内光的数量越少。

出射度(Exitance)

  衡量出射光线的数量和方向的量,利用入射光线的数量和角度计算。和辐照度存在线性关系。

吸收和散射

  光与物体相交有两个结果:散射吸收

  • 散射(Scattering)
      散射只改变光的方向,但不改变光的颜色和密度,如下图:

  散射分为折射(Refraction)反射(Reflection),如上图的两个不同方向的出射光所示。对于不透明物体,折射进入物体内部的光线还会据徐与内部的颗粒进行相交,其中一些光效最后会重新发射出物体表面,而另一些则会被物体吸收。反射则在物体表面直接被弹出来了,但是如果物体表面粗糙会往不同的方向进行反射。
  针对反射的两种性状,我们有漫反射(Diffuse)模型高光反射(Specular)模型

  • 吸收(Absorption)
      吸收只改变光线的密度和颜色,不改变光线的方向。也就是说,吸收将光的能量吸收走了,而散射并没有只是改变了光的方向。
graph LR
  光与物体相交 --> 散射
  光与物体相交 --> 吸收
  散射 --> 折射
  散射 --> 反射
  反射 --> 漫反射
  反射 --> 高光反射

着色(Shading)

  着色指的是根据材质属性、光源信息去使用一个或多个等式计算沿某个观察方向的出射度的过程。这些等式就是“光照模型(Light Model)”

标准光照模型

  标准光照模型只关注直接光照(Direct Light),也就是从光源出发,照射到物体表面,反射回摄像机的光线。于是,我们可以称标准光照模型为局部光照模型。
  进入摄像机的光线(符号:c~光线类型~)往往分为以下四种:

漫反射(Diffuse)

兰伯特模型(Lambert)

  用于描述当光线照射到物体表面,物体表面会向每个方向散射多少辐射量。对于漫反射而言,观察角度并不重要,因为我们默认漫反射在各个方向的分布是一样的。但是入射光线非常重要。
  漫反射符合兰伯特定律(Lambert’s law)

cdiffuse=(clightmdiffuse)max(0,n^l^)c_{diffuse} = (c_{light} \cdot m_{diffuse})max(0, \hat{n} \cdot \hat{l})

  其中,n^\hat{n} 为法线向量(normal vector, 一般用n表示),l^\hat{l} 为光源的单位矢量,mdiffusem_{diffuse} 就是材质的漫反射颜色,clightc_{light} 是光源颜色

半兰伯特模型(Half Lambert)

  漫反射可以有逐顶点和逐片元两种模型(均满足兰伯特定律),但是它们的暗部都是全黑的,这使得从暗部观察物体时不利于细节呈现并且看起来像个平面。通过更改兰伯特定律公式,我们可以得到半兰伯特模型的漫反射计算公式:

cdiffuse=(clightmdiffuse)(α(n^l^)+β)c_{diffuse} = (c_{light} \cdot m_{diffuse})(\alpha (\hat{n} \cdot \hat{l}) + \beta)

  与兰伯特模型不同的是,半兰伯特模型对法线和光源的点乘进行的一个缩放并偏移。通常,α\alphaβ\beta 的值均为0.5,可以将光与法线的点积映射到[0, 1]范围中。
  三种方式计算漫反射背光部分的差别:

  三种计算漫反射的方式(左:逐像素;中:逐顶点;右:半兰伯特),可以看出逐顶点漫反射有明显的锯齿边

高光反射(Specular)

  用于描述当光线照射到物体表面,物体表面会在完全镜面反射方向散射多少辐射量。
  高光反射需要知道四个向量的值,即人眼观察方向(v)、法线方向(n)、光源方向(入射光l)、反射方向(出射光r)。

Phong模型

  Phong模型和物理中的光学模型类似,主要特征是入射光 l^\hat{l} 、出射光 r^\hat{r} 和法线 n^\hat{n} 的夹角相同。 出射光r^\hat{r} 的计算公式为:

r^=2(n^l^)n^l^\hat{r} = 2(\hat{n} \cdot \hat{l}) \hat{n} - \hat{l}

  高光反射光的计算公式为( mglossm_{gloss} 为材质光泽度,值越大材质光斑越小。max的作用是防止负数出现,避免背后的光照亮物体):

cspecular=(clightmspecular)max(0,v^r^)mglossc_{specular} = (c_{light} \cdot m_{specular})max(0, \hat{v} \cdot \hat{r})^{m_{gloss}}

Blinn-Phong模型

  Blinn-Phong模型对Phong模型进行了简化,引入了一个新的矢量 h^\hat{h} 代替反射光线。 h^\hat{h} 是对 v^\hat{v}l^\hat{l} 取平均再统一化得到的,指的是v和l中间的向量:

h^=v^+l^v^+l^\hat{h} = \frac{\hat{v} + \hat{l}}{\vert \hat{v} + \hat{l}\vert }

image   高光反射光的计算公式为: $$c_{specular} = (c_{light} \cdot m_{specular})max(0, \hat{n} \cdot \hat{h})^{m_{gloss}}$$

  当摄像机无穷远时,我们认为 v^\hat{v}l^\hat{l} 均为定值,此时 h^\hat{h} 为定值,使用Blinn模型的速度将更快。而当 v^\hat{v}l^\hat{l} 处在变化之中时,Phong模型更占优势。此外,一些适合Blinn模型更符合实验结果。因此具体选择哪种攻击计算高光反射需要依效果而定。比如当入射光方向和人眼观察方向在法线的同一侧时,Phong模型会产生奇怪的日食状“断层”现象,而Blinn-Phong模型则不会:

Gourand模型

中文名是高洛德模型,这是一种在顶点着色器下计算的光照模型,适合顶点比较多的模型(不然效果太锯齿了)。不过由于是通过插值计算的高光,导致一个高光顶点会向四周蔓延,效果很拉。也因为这个原因,该模型不怎么用

  从左到右:逐像素高光、逐顶点高光(可见锯齿)、Blinn-Phong高光模型

环境光(Ambient)

  环境光用于描述其他所有的间接光照。通常用于模拟间接光照,是一个全局(goble)变量,一个场景中所有物体都使用这个环境光:

cambient=gambientc_{ambient} = g_{ambient}

自发光(Emissive)

  用于描述给定一个方向时物体本身能向该方向发射多少辐射量,指一个物体材质本身会发光,光再照射到摄像机中。所以标准光照模型中,自发光就等同于材质(metrail)的自发光颜色。

cemissive=memissivec_{emissive} = m_{emissive}

  让自发光产生照亮其他物体效果的前提是使用全局光照模型,否则只是自己看起来更亮了而已(因为标准光照模型是局部的光照模型)。

着色频率

  根据着色的形式,着色频率分为逐三角形渲染(flat shading)、逐顶点渲染(gourand shading)或逐像素渲染(phong shading),如下图所示。
image

  由于顶点数量远少于像素数量,所以vert的计算量比frag小,但是vert依赖于线性插值(Lerp),可能会导致光照模型中的非线性运算影响到实际输出效果

顶点法线的计算

高洛德着色涉及顶点的法线计算,对于一个点要怎么找它的法线呢?
一个顶点往往与一至多个面相连接,而面的法线是很好求的。所以,对于顶点连接的面的法线求一个平均就可以等价为顶点的法线
image

像素法线的计算

冯着色涉及像素的法线计算,对于一个像素要怎么找它的法线呢?
有人觉得既然一个像素在某个面上,那么就直接用这个面的法线充当像素的法线,实际上是不对的。像素法线的求法实际上如下图所示,是用重心坐标求得的:
image

Shader函数学习:normalize、saturate与mul

  • normalize()
    该函数能将输入的参数转化为长度为1的单位向量,不改变输入参数的方向。输入的参数不仅可以是float,也可以是vec2、vec3等
  • saturate()
    该函数能将输入的数映射到[0, 1]围内,常用于颜色等限制分量范围在[0, 1]内的参数
  • mul(a, b)
    该函数返回a与b的点积,即aba \cdot b