图形学理论 局部光照
前言
本篇将介绍局部光照模型——每个物体的光照独立于其他物体,即仅考虑光源直接发出的光线,忽略场景中反弹的
为何我们可以看见这个五彩缤纷的世界?
光源是由红蓝绿三色的混合光,而人的视网膜有三种光受体,分别对于红绿蓝三色光敏感,当光线刺激到相应的光受体,基于光线的强度而产生不同程度的刺激,随后光受体将神经冲动沿着神经传至大脑,大脑再根据光受体所受刺激再观察者大脑中生成相应画面。总的来说,就是人眼接收到了从物体射来的光线
光照与材质
如下图所示,下图左边是一个不受光照的物体,而右边是一个受光照的物体,显然易见,右边更有立体感,而左边看着像个2d物体。这都是光照和阴影的功劳。实际上,我们在视觉上对世界的感知依靠与光照与材质
材质可以看作是确定光照和物体表面如何进行交互的属性集,其中的属性包括:表面反射光和吸收光的颜色,表面材质的折射率,表面材质的折射率,表面的透明度。根据这些属性,即可创造处许多种类的物体
光照现象
所有的光和物质相互作用都是两种现象的结果:散射(scattering)和吸收(absorption)
- 散射:发生在当光线遇到任何种类的光学不连续性时,可能存在于具有
不同光学性质的两种物质分界之处
,晶体结构破裂处,密度的变化处等。散射并不改变光的总量,只改变方向
。其中散射又分为反射(reflection)和折射((refraction)
- 吸收:发生在
物质内部
,造成一些光转变成另一种能量并消失。吸收会减少光量,但不影响方向
光照类型
光照类型有四种
-
漫反射光
当光线照射到表面某一点时会发生散射,一部分光进入物体内部,并和表面附近的物质相互作用,而另一部分光会向各个方向均匀散射
-
镜面光
当光线照射到两种不同折射率介质间的物体表面时,一部分光被吸收,而剩下的光发生散射(菲涅尔效应)。若折射光沿折射向量从介质的另一侧射出,并进入观察者眼中,此时物体将呈现透明状
-
环境光
由其他物体反射来的间接光照
-
自发光
物体自身发射的光
经验模型
经验模型有三种
-
Lambert漫反射模型
只考虑环境光和漫反射光.漫反射效果与光源位置有关,与观察者位置无关
漫反射公式
\(k_d\)为漫反射系数,I为入射光强度,n和l分别为法向量和入射方向
效果
可以看到漫反射已经让物体呈现出立体感了,但还缺少高光
-
Phong模型
额外考虑镜面反射
镜面反射公式
\(L_s = K_s(I / r^2)max(0, cos \alpha)^p\),其中\(\alpha\)是高光的反射方向与观察方向的夹角,而p用于衰减光强(因为离反射光越远,能看到的反射光应越弱)效果
-
Blinn-Phong模型
Blinn-Phong基于Phong模型将反射方向与人眼观察方向夹角替换为半程向量h和法线向量的夹角\(\alpha\)
Blinn-Phong与Phong模型的区别
- 半程向量相对反射向量计算更快,且效果差不多
- 若入射方向和视线方向不变,半角向量只需计算一次并重用
- phone模型在某些情况下效果不如Blinn-Phong模型,Blinn-Phong高光更加平滑。比如,当反射光方向与观察方向的夹角大于90°时(入射光与观察方向在同一侧),此时高光项为0无贡献,就会导致断层
效果
着色频率
我们以三角形面的法线向量和三角形顶点的法线向量计算出来的光照效果是不同的,以下将介绍三种着色频率
-
面着色/逐三角形着色(Flat Shading)
面着色以一个面作为一个着色单位,对每个面的法线向量进行一次Blinn-Phong反射计算.因为模型大部分都是以三角形进行表示的,所以也记录了每个三角形面的法线向量
效果
优点
- 计算很快
缺点
- 效果很差
-
逐顶点着色(Gouraud Shading)
逐顶点着色对每个三角形顶点进行一次着色
-
如何求得每个顶点的法向量?
将所有共享一点的面的法线向量求和再求平均,最后进行标准化
对于上图顶点法线的求法:\(n_{avg} = \frac{n_0 + n_1 + n_2 + n_3}{\|n_0 + n_1 + n_2 + n_3\|}\)
效果
-
-
逐像素(Phong Shading)
对三角形内每一点都进行光照计算
-
如何求得三角形内任意一点的法向量?
利用重心坐标(按理来说,重心坐标一定是采用观察空间里的重心坐标,但计算一般采用投影后的二维平面,这里存在一个误差需要进行矫正)公式进行插值https://www.cnblogs.com/chenglixue/p/16867745.html
效果
优点
- 效果很好
缺点
- 计算量太大
-
-
随模型精度的增加,三种着色方法的效果
可以看出,模型精度增加到一定程度,三种方法呈现的效果是差不多的
透视矫正
-
为什么需要透视矫正(Perspective-Correct Interpolation)?
在Phong Shading中,我们提到过通过使用重心插值的方式求三角形内任意一点,但这个重心插值的对象本应是世界空间中的坐标,但大多数计算都是使用透视投影后的二维平面内的数据,而恰恰问题就出在这里。如下图所示,在屏幕空间进行插值后得到c的intensity为0.5,但在观察空间中,点c的intensity不为0.5,也就是说透视投影后进行插值的坐标或属性会存在一定误差
-
如何进行矫正?
为了证明更简洁,在此处我们采用深度值z的插值且只考虑x,z轴来介绍
如下图所示,我们定义屏幕空间的插值比例为s,而观察空间的比例为t,坐标\(a(u_1,d),c(u_s,d),b(u_2,d),A(X_1,Z_1),C(X_t,Z_t),B(X_2,Z_2)\)
推导过程如下:
根据上图可以轻易得到以下三个式子:
- \(\begin{equation} \frac{X_1}{Z_1} = \frac{u_1}{d} \Rightarrow X_1 = \frac{u_1 Z_1}{d} \tag{1} \end{equation}\)
- \(\begin{equation} \frac{X_2}{Z_2} = \frac{u_2}{d} \Rightarrow X_2 = \frac{u_2 Z_2}{d} \tag{2} \end{equation}\)
- \(\begin{equation} \frac{X_t}{Z_t} = \frac{u_s}{d} \Rightarrow Z_t = \frac{d X_t}{u_s} \tag{3} \end{equation}\)
对观察空间和屏幕空间进行插值,可以得到如下三个式子:
- \(\begin{equation} u_s = u_1 + s(u_2 - u_1) \tag{4} \end{equation}\)
- \(\begin{equation} X_t = X_1 + t(X_2 - X_1) \tag{5} \end{equation}\)
- \(\begin{equation} Z_t = Z_1 + t(Z_2 - Z_1) \tag{6} \end{equation}\)
将式子(4)和(5)带入(3),可得:
\(\begin{equation} Z_t = \frac{d(X_1 + t(X_2 - X_1))}{u_1 + s(u_2 - u_1)} \tag{7} \end{equation}\)
将式子(1)和(2)带入(7),可得:
\(\begin{equation} Z_t = \frac{d(\frac{u1 Z_1}{d} + t(\frac{u_2 Z_2}{d} - \frac{u_1 Z_1}{d}))}{u_1 + s(u_2 - u_1)} = \frac{u_1 Z_1 + t(u_2Z_2 - u_1 Z_1)}{u_1 + s(u_2 - u_1)} \tag{8} \end{equation}\)
将式子(6)带入(8),可得:
\(\begin{equation} Z_1 + t(Z_2 - Z_1) = \frac{u_1 Z_1 + t(u_2 Z_2 - u_1 Z_1)}{u_1 + s(u_2 - u_1)} \tag{9} \end{equation}\)
观察式子(9)可以得知,已经成功求得一个t和s的关系式,化简可得:
\(\begin{equation} t = \frac{s Z_1}{s Z_1 + (1-s)Z_2} \tag{10} \end{equation}\)
将式子(10)带入(6),可得:
\(\begin{equation} Z_t = Z_1 + \frac{s Z_1}{s Z_1 + (1-s)Z_2}(Z_2 - Z_1) \tag{11} \end{equation}\)
对式子(11)化简,可得最终答案:
\(\begin{equation} Z_t = \frac{1}{\frac{1}{Z_1} + s(\frac{1}{Z_2} - \frac{1}{Z_1})} \tag{12} \end{equation}\)
虽然以上证明是线性插值的矫正过程,但对于重心坐标插值也同样适用。此处不再对其进行推导,直接给出公式。
重心坐标插值的透视矫正公式
:\(\begin{equation} Z_t = \frac{1}{\frac{\alpha}{Z_A} + \frac{\beta}{Z_B} + \frac{\gamma}{Z_C}} \tag{13} \end{equation}\)
-
推广,任意属性(法向量等)的插值结果
推导如下:
设任意属性为I
易得:
\(\begin{equation} I_t = I_1 + t(I_2 - I_1) \tag{14} \end{equation}\)
将式子(10)带入(14),可得:
\(\begin{equation} I_t = I_1 + \frac{s Z_1}{s Z_1 + (1-s)Z_2}(I_2 - I_1) \tag{15} \end{equation}\)
再排列一下:
\(\begin{equation} I_t = \frac{(\frac{I_1}{Z_1} + s(\frac{I_2}{Z_2} - \frac{I_1}{Z_1}))}{(\frac{1}{Z_1} + s(\frac{1}{Z_2} - \frac{1}{Z_1}))} \tag{16} \end{equation}\)
将式子(12)带入(16),可得:
\(\begin{equation} I_t = \frac{(\frac{I_1}{Z_1} + s(\frac{I_2}{Z_2} - \frac{I_1}{Z_1}))}{\frac{1}{Z_t}} \tag{17} \end{equation}\)
类似,推出重心坐标插值的任意属性公式:
\(\begin{equation} I_t = \frac{I_A\frac{\alpha}{Z_A} + I_B \frac{\beta}{Z_B} + I_c \frac{\gamma}{Z_C}}{\frac{1}{Z_t}} \tag{18} \end{equation}\)
变换法向量
-
为什么需要变换法向量?
虽然法向量存于世界空间,但从局部空间变化到世界空间可能会导致模型的位置形状发生改变。因此,法向量需随模型发生变化
-
如何进行法向量变化?
如下三图所示,向量u与法向量n正交,若对此应用一个非等比缩放变换A(下图将x轴放大两倍),变换后的u和n不再正交
那么问题来了,若给定一个用于变换点与向量的变换矩阵A,如何可以求出这样一个变换矩阵B:通过它变换法向量n,使变换后的u和法向量再次正交——\(uA · uB = 0\)?
-
推导
若法向量n正交于u,则能逐步推出:
- \(u·n = 0\)
- \(u n^T = 0\)。将点积变为矩阵乘法
- \(u(AA^{-1})n^T = 0\)
- \((uA)(A^{-1}n^T) = 0\)
- \((uA)((A^{-1}n^T)^T)^T = 0\)
- \((uA)(n(A^{-1})^T)^T = 0\)
- \(uA · n(A^{-1})^T = 0\)
- \(uA · nB = 0\)。即求出,\(B = (A^{-1})^T\),也就是说这个矩阵是A的逆转置矩阵
总而言之,当需要对非等比变换或剪切变换后的法向量进行变化时,则使用逆转置矩阵
-
光源
D3D中光源共有三种类型:平行光,点光源,聚光灯
-
平行光(亦称方向光)
定义:一种距离目标物体非常远的光源
特点
- 发出的入射光可看作是彼此平行的
- 由于光源距离物体十分远,因此可以忽略距离的因素,认为平行光的光强是恒定的
- 开销最低
下图中左边光源发出的的光线近似为平行光
-
点光源
定义:具有颜色和位置,但它向所有方向发射的光都一样。如电灯泡
特点
-
向各个方向射出的光线的强度是一样的
-
受距离影响,光强会发生改变
在物理学中,光强根据平方反比定律而随距离函数发生衰减,因此距离光源d处的某点光强为:\(I(d) = \frac{I_0}{d^2}\),\(I_0\)表示d = 1处的光强
因此在此处运用一个线性衰减(fallof)函数来控制点光源的衰减:\(att(d) = saturate(\frac{fallofEnd - d}{fallofEnd - fallofStart})\),此函数会将参数限定在[0,1]区间:\(saturate(x) = \left\{ \begin{array}{rcl} x & 0 \leqq x \leqq 1 \\ 0 & x < 0 \\ 1 & x > 1 \end{array}\right.\).此线性衰减函数并不会影响环境光,因为环境光模拟的是向四处反弹后的结果
如下图所示,当d的距离大于等于falloEnd便衰减至0,不受光照
此函数的好处:在着色器程序中,若一个点超出光照有效范围,便可采用动态分支跳过此处的计算并提前返回
-
开销比平行光昂贵。因为需要针对距离d进行计算
-
-
聚光灯光源
定义:聚光灯是由位置Q向方向d照射出呈圆锥体的光
特点
-
在上图中,只有在-L(L表示指向光源的光向量)与d间的夹角\(\phi\)小于圆锥体的半顶角\(\phi_{\max}\)时,P(物体)才处于聚光灯的圆锥体范围内
-
聚光灯圆锥体范围内所有光线强度不尽相同。位于圆锥体中心的光线(Qd)强度最强,随着角\(\theta\)增大,光强会逐渐趋于0
如何控制光强的衰减和圆锥体大小?
使用先前提到的控制粗糙度来模拟镜面光照的函数,但需用\(\phi\)替换\(\theta_h\),以s替换m:\(k_{spot}(\phi) = max(0,cos\phi)^s\).此公式描述了,光强随着\(\phi\)增加而连续且平滑地衰减,而s用于控制\(\phi_{\max}\)的角度,如s = 8,则半顶角逼近45°
-
代价最高,因为需要额外计算聚光灯因子\(k_{spot}\),并让其与聚光灯光强相乘
-
reference
[Game-Programmer-Study-Notes/README.md at master · QianMo/Game-Programmer-Study-Notes · GitHub](https://github.com/QianMo/Game-Programmer-Study-Notes/blob/master/Content/《Real-Time Rendering 3rd》读书笔记/README.md)
传统的经验光照模型详解_白筱风的博客-CSDN博客_光照模型
计算机图形学五:局部光照模型(Blinn-Phong 反射模型)与着色方法(Phong Shading)_剑 来!的博客-CSDN博客_计算机图形学光照模型
https://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf
Directx12 3D 游戏开发实战