Skip to content

光照和雾化

这里主要讨论OpenGL和DirectX中“标准”的光照模型,尽管它们有自身的局限性,但是仍具有参考意义。由于篇幅限制不再对不同的光照技术进行一一介绍。

色彩的数学

计算器中使用的色彩模型是RGB模型,其中R表示红色,G表示绿色,B表示蓝色。

我们将RGB认为是0到1之间的向量,例如用(0, 0, 0)表示黑色,用(1, 1, 1)表示白色,灰色则在这两点的连线上

同时它们支持加、减、乘以标量,也支持色彩之间的“按位乘”,用符号表示

img.png

当然可能存在色彩分量越界的情况,即不在[0, 1]之间,只需要将各个分量除以最大的分量即可,这个过程称为截断

例如:色彩[1, 2, 1],其中绿色越界,截断之后为[0.5, 1, 0.5]

光源

光源也分为多种,接下来讨论大多数API中都支持的常见光源

点光源

点光源是四面八方发生光线的单点,又称为全向光、球状光。它有方向和色彩,同时也有一个辐射衰减半径来控制照亮的范围。

它的强度通常是随着到光源的距离不断进行衰减,最终为0。在生活中常见的点光源,例如:灯泡、电灯、火把等

img.png

平行光

平行光表示从无限远处射来的点光源的光线,场景中所有光线都是平行的,它没有位置的概念,也无衰减。

太阳光就是典型的平行光

img.png

聚光灯

聚光灯则是从特定光源向特定的方向射出的锥形光。例如信号灯、车头灯。它不仅有位置,也有方向,还有辐射距离的概念

img.png

环境光

环境光(也称为漫射环境光)是场景周围存在的光,并非来自任何特定的光源对象。如果不考虑环境光,物体的影子将完全是黑色

标准光照方程

Clit=Cspec+Cdiff+Camb
  • Clit是指打开光照的情况下计算颜色值的结果。注意这里与生活中的“光照”不同,计算机中的光照是在关闭物体纹理的颜色值计算的
  • Cspec是镜面反射分量
  • Cdiff是散射分量
  • Camb是环境分量

物体外观主要取决于以下四点因素:

  • 物体表面的性质,即材质属性
  • 表面的方位和朝向,朝向常用单位法向量表示
  • 照射来的各光源的性质
  • 观察者位置

标准光照方程中已经考虑了以上因素

镜面反射分量

镜面反射分量指的是光源经物体表面反射进入人眼睛的光线,它是物体看上去有光泽,由于粗糙表面的反射率不高,所有缺乏此类效果。

镜面反射的强度取决与物体、光源和观察者

img.png

其中:

  • n为物体表面的法向量
  • l指向光源
  • v指向观察者
  • r是l相对于n的镜像向量
  • θ是r和v的夹角,值与r·v相等

INFO

所有单位均为法向量

img.png

可知r、l、n之间的关系为

r=2(n·l)nl

镜面反射模型的Phong模型为:

cspec=(cosθ)mglssspecmspec=(v·r)mglssspecmspec

其中:

  • mgls表示材料的光泽度,也称为Phong指数:值越大,物体表面越光滑。相应的,也会影响物体表面的光照情况
    • 值越小,光斑的范围越大、越平滑
    • 值越大,光斑的范围越小、越亮
    • 边界情况
      • 完全的反射面,如玻璃,具有非常大的值,导致没有亮斑
      • 不完全的反射面,如苹果,具有较大的亮斑
  • mspec表示材料的反射颜色,它控制光斑的强度。
  • sspec表示光源的镜面反射颜色,控制光本身的色彩和强度,它常等于光的漫反射颜色sdiff

img.pngmspec从左到右由黑至白,mgls从上到下由大至小

当观察者、光源不断远离物体时,向量v可以视作是一个常数,同样向量l也可视为一个常数

那么如果向量n在持续变化,就需要不断计算向量r,而Blinn模型便通过另外一种方式省去了这个计算步骤

img.png

Blinn模型使用向量h,表示v、l的中间向量,有

h=v+lv+l

镜面反射模型的Blinn模型为:

cspec=(cosθ)mglssspecmspec=(n·h)mglssspecmspec

漫反射分量

与镜面反射类似,漫反射分量也刻画了光线在物体表慢的反射,只不过它描述的是在随机散开方向上的反射,这是由于物体的粗糙引起的。

漫反射不依赖观察者位置,因为它是随机的。相反,光源和物体的相对位置显的更加重要

img.png

漫反射光服从Lambert法则:反射光强度与表面法向量和光线夹角的余弦成正比

cdiff=(n·l)sdiffmdiff

其中:

  • n表示物体表面的法向量
  • l表示指向光源的法向量
  • sdiff表示光源散射色,一般和光源镜面色sspec一致
  • mdiff表示物体材料的散射颜色,即物体颜色

环境光分量

镜面反射和漫反射分量都是刻画光源经物体反射后直接进入人眼的光线,但是现实中会存在多次反射再进入人眼的情况,这称为环境光

环境光取决于物体材质和全局环境光

camb=gambmamb

其中:

  • gamb表示整个场景的环境光值
  • mamb表示材质的环境光分量,总是等于漫反射分量

光的衰减

光随着距离会发生衰减,衰减方程为:

i1i2=d22d12

其中:

  • i为光强
  • d为距离

在实践中,上述公式并不方便,我们常用另一个简单的基于辐射衰减距离的模型替代

i(d)={1,ddmindmaxddmaxdmin,dmin<d<dmax0,ddmax
  • dmin内,光强不衰减
  • dmindmax之间,光强从1衰减到0
  • dmax外,光强为0

基于辐射衰减距离的模型适用于点光源、聚光灯,聚光灯会多一个影响因素--Hotspot辐射衰减半径,表示光亮在光锥边上的衰减

计算出衰减系数i后,即可将它乘以镜面反射分量漫发射分量,注意环境光是没有衰减的

合成光照方程

在知道镜面反射、漫反射分量、环境光分量、衰减系数之后,我们便可以完成单一光源的标准光照模型

Clit=Cspec+Cdiff+Camb=i(n·h)mglssspecmspec+i(n·l)sdiffmdiff+gambmamb

img.png

当有多个光源时,对所有的镜面反射、漫反射求和即可,最终的光照方程如下

假设有n个光源

j=1n(ij(max((n·hj),0)mglssjspecmspec+max((n·lj),0)sjdiffmdiff))+gambmamb

雾化

在现实中,空气中会存在许多粒子,例如雾、灰尘、烟等,这些粒子会干扰光线的传播。当粒子越多时,视觉上的颜色会逐渐偏向雾的颜色

我们假设这样一个雾模型,雾的浓度在[0, 1]之间变动,0表示没有雾,1表示完全雾化,最终的颜色值由物体颜色和雾颜色线性插值求得

那么如何计算雾浓度呢?如果知道雾的粒子数,并将它转换为雾浓度即可,但是粒子数获取难度大。

我们可以通过另一种方式模拟它,粒子数有两个影响因素,分别是场景中的全局雾浓度、观察者和物体间的距离,因此简化的雾浓度模型为

f(d)={0,ddminddmindmaxdmin,dmin<d<dmax1,ddmax
  • dmin内,雾浓度为0
  • dmindmax之间,雾浓度从0增长到1
  • dmax外,雾浓度为1

INFO

  • 公式假设雾是均匀的,但是现实并非如此,现实中雾的下层浓度更大,公式中并不包含这个信息
  • 距离的定义是可变的
    • 欧式空间距离,得到的是球状雾效果,但是要开方运算

那么添加了雾化效果的颜色为

cfogged=clit+f(gfogclit)

其中:

  • clit为计算光照后的物体表面颜色
  • f为雾浓度转化规则
  • gfog为全局雾颜色
  • cfogged为最终效果

为了得到雾化效果,需要传入API三个信息

  • 雾化开关
  • 雾的颜色,即gfog
  • 雾化距离,dmindmax

flat着色和Gouraud着色

根据着色方式不同,可以分为三种

  • 逐像素计算:Phong着色模型
  • 逐多边形计算:flat着色模型
  • 逐顶点计算:Gouraud着色模型

INFO

注意这里的Phong着色模型与Phong镜面反射模型不同

flat着色

对整个三角形只计算一次光照值,最终效果将多边形的棱角也展示出来

img.png

Gouraud着色

对多边形的顶点计算光照和雾,再通过线性插值用于整个多边形,最终效果更加平滑。但是无法很好的计算出镜面高光

img.png

Phong着色

逐像素计算,计算时间长,但是更加逼真

img.png