《Unity Shader入门精要》六、Unity 中的基础光照

6.1 我们是如何看到这个世界的

要模拟真实的光照环境来生成一张图像,需要考虑 3 种物理现象:

  • 光线光源中被发射出来
  • 光线和场景中的一些物体相交:一些光线被吸收了,另外的被散射到其他方向
  • 最后,摄像机吸收了一些光,产生了一张图像

6.1.1 光源

  • 通常把光源当成一个没有体积的点,用 <math xmlns="http://www.w3.org/1998/Math/MathML"> l l </math>l 来表示它的方向

  • 使用辐照度(irradiance)来量化光------发射出了多少光

    • 对于平行光,通过计算在垂直于 <math xmlns="http://www.w3.org/1998/Math/MathML"> l l </math>l 的单位面积上单位时间内穿过的能量来得到
    • 若物体表面和 <math xmlns="http://www.w3.org/1998/Math/MathML"> l l </math>l 不垂直,可以使用 <math xmlns="http://www.w3.org/1998/Math/MathML"> l l </math>l 和表面法线 n 之间的余弦值来得到
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> c o s θ cos\theta </math>cosθ 可用光源方向 l表面法线 n点积来得到,所以点积可用来计算辐照度

6.1.2 吸收和散射

光线与物体相交的结果:散射吸收

  • 散射只改变光线的方向,但不改变光线的密度颜色
  • 吸收只改变光线的密度颜色,不改变方向
  • 散射后有两种方向:
    • 散射到物体内部,被称为折射(refraction)透射(transmission)
    • 散射到物体外部,被称为反射(reflection)
  • 对于不透明物体折射进物体内部的光线会继续和内部的颗粒相交,其中一些光线最后会重新发射出物体表面,而另一些则被物体吸收
    • 那些从物体表面重新发射出的光线将具有和入射光线不同的方向分布和颜色
  • 为了区分这两种不同的散射方向,光照模型中使用不同的部分来计算它们
    • 高光反射(specular):物体表面如何反射光线
    • 漫反射(diffuse):有多少光线会被折射、吸收和散射出表面
  • 根据入射光线的数量和方向,可以计算出射光线的数量和方向,通常使用出射度(exitance)来描述它

6.1.3 着色

根据材质属性(如漫反射等)、光源信息,使用一个等式 去计算沿某个观察方向的出射度的过程

  • 这个等式被称为光照模型(Lighting Model)
  • 不同的光照模型有不同的目的,有的用于描述粗糙物体表面,有的用于描述金属表面

6.1.4 BRDF 光照模型

BRDF(Bidirectional Reflectance Distribution Function)包含了给定模型表面上的一个点外观的完整描述,用于计算出射方向上的光照能量分布

  • 本章涉及的 BRDF 都是对真实场景进行理想化和简化后的模型,不能真实反映现实的交互效果

6.2 标准光照模型

  • 在 BRDF 理论被提出之前,标准光照模型已被广泛使用
  • 基本理念:只关心直接光照------那些直接从光源发射出来照射到物体表面后,经一次反射直接进入摄像机的光线
  • 基本方法,将进入摄像机的光线分为 4 部分,每部分使用一种方法计算其贡献度
    • 自发光(emissive):描述一个表面会向指定方向发射多少辐射量
    • 高光反射(specular):描述当光线照到表面时,该表面会在完全镜面反射方向散射多少辐射量
    • 漫反射(diffuse):描述当光线照到表面时,该表面会向每个方向散射多少辐射量
    • 环境光(ambient):描述其他所有间接光照

6.2.1 环境光

物体会被间接光照所照亮,光线通常会在多个物体之间反射,最后进入摄像机

  • 在标准光照模型中,使用一种被称为环境光的部分来近似模拟间接光照
  • 它通常是一个全局变量,即场景中所有物体都使用这个。计算环境光公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> c a m b i e n t = g a m b i e n t c_{ambient}=g_{ambient} </math>cambient=gambient

6.2.2 自发光

光线不经任何物体反射,直接由光源发射入摄像机

它的计算很简单:

<math xmlns="http://www.w3.org/1998/Math/MathML"> c e m i s s i v e = m e m i s s i v e c_{emissive}=m_{emissive} </math>cemissive=memissive

6.2.3 漫反射

漫反射是用于对那些被物体表面随机散射到各个方向的辐射度进行建模的

  • 视角位置不重要,可以认为在任何反射方向上的颁都是一样随机的
  • 入射光线的角度很重要
  • 漫反射光照符合兰伯特定律 :反射光线的强度与表面法线光源方向之间夹角的余弦成正比:

<math xmlns="http://www.w3.org/1998/Math/MathML"> c d i f f u s e = ( c l i g h t ⋅ m d i f f u s e ) m a x ( 0 , n ⋅ I ) c_{diffuse}=(c_{light}\cdot m_{diffuse})max(0,n\cdot I) </math>cdiffuse=(clight⋅mdiffuse)max(0,n⋅I)

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> n n </math>n 是表面法线, <math xmlns="http://www.w3.org/1998/Math/MathML"> I I </math>I 是指向光源的单位矢量, <math xmlns="http://www.w3.org/1998/Math/MathML"> m d i f f u s e m_{diffuse} </math>mdiffuse 是材质的漫反射颜色, <math xmlns="http://www.w3.org/1998/Math/MathML"> c l i g h t c_{light} </math>clight 是光源颜色

6.2.4 高光反射

用于计算那些沿着完全镜面反射方向被反射的光线,以让物体看起来是有光泽的

  • 这里的高光反射是一种经验模型,并不完全符合真实世界中的高光反射现象

利用 Phong 模型来计算

<math xmlns="http://www.w3.org/1998/Math/MathML"> c s p e c u l a r = ( c l i g h t ⋅ m s p e c u l a r ) m a x ( 0 , v ^ ⋅ r ^ ) m g l o s s c_{specular}=(c_{light}\cdot m_{specular})max(0,\hat{v}\cdot \hat{r})^{m_{gloss}} </math>cspecular=(clight⋅mspecular)max(0,v^⋅r^)mgloss

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> m g l o s s m_{gloss} </math>mgloss 是材质的光泽度,也称为反光度,用于控制高光区域的"亮点"有多宽,值越大,亮点越小
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> m s p e c u l a r m_{specular} </math>mspecular 是材质的高光反射颜色,用于控制材质对于高光反射的强度和颜色
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> c l i g h t c_{light} </math>clight 是光源的颜色和强度

使用 Blinn 模型来计算

Blinn 提出一个简单的修改方法来得到类似效果。基本思想是,避免计算反射方向 <math xmlns="http://www.w3.org/1998/Math/MathML"> r ^ \hat{r} </math>r^

引入新矢量 <math xmlns="http://www.w3.org/1998/Math/MathML"> h ^ \hat{h} </math>h^,通过对 <math xmlns="http://www.w3.org/1998/Math/MathML"> v ^ \hat{v} </math>v^ 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> I ^ \hat{I} </math>I^ 平均后再归一化得到。即:

<math xmlns="http://www.w3.org/1998/Math/MathML"> h ^ = v ^ + I ∣ v ^ + I ∣ \hat{h}=\frac{\hat{v}+I}{|\hat{v}+I|} </math>h^=∣v^+I∣v^+I

然后再使用 <math xmlns="http://www.w3.org/1998/Math/MathML"> n ^ \hat{n} </math>n^ 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> h ^ \hat{h} </math>h^ 之间夹角进行计算

<math xmlns="http://www.w3.org/1998/Math/MathML"> c s p e c u l a r = ( c l i g h t ⋅ m s p e c u l a r ) m a x ( 0 , n ^ ⋅ h ^ ) m g l o s s c_{specular}=(c_{light}\cdot m_{specular})max(0,\hat{n}\cdot \hat{h})^{m_{gloss}} </math>cspecular=(clight⋅mspecular)max(0,n^⋅h^)mgloss

  • 若摄像机和光源距离足够远的话,Blinn 模型会快于 Phong 模型
  • 实际上,在一些情况下,Blinn 模型更符合实验结果

6.2.5 逐像素还是逐顶点

我们在哪计算这些光照模型呢?

在片元着色器中,也被称为逐像素光照

以每个像素为基础,得到它的法线(可对顶点法线插值得到,也可从法线纹理中采样得到),然后进行光照模型的计算

  • 这种在面片之间对顶点法线进行插值的技术称为 Phone 着色

在顶点着色器中,也被称为逐顶点光照

在每个顶点上计算光照,然后在渲染图元内部进行线性插值,最后输出像素颜色

  • 被称为高洛德着色
  • 顶点数往往小于像素数,因此逐顶点光照的计算量往往要小于 逐像素光照
  • 会出现的问题
    • 由于光照结果依赖于线性插值来得到,当光照模型中有非线性计算(如高光)时,逐顶点光照会出问题
    • 由于会在渲染图元内部对顶点颜色进行插值,会导致渲染图元内部的颜色总是暗于顶点处的最高颜色值,这在某些情况下会产生明显的棱角现象

6.2.6 总结

  • 标准光照模型由于它的易用性、计算速度和得到的效果都比较好,因此一直被广泛使用,尽管它并不完全符合真实世界中的光照现象
  • 但这种模型有很多局限性,很多重要的物理现象无法用这个模型表现出来,如菲涅耳反射,和有些表面是具有各向异性反射性质的物体旋转时的光照效果

6.3 Unity 中的环境光和自发光

Window - Lighting - Ambient Source/Ambient Color/Ambient Intensity

6.4 在 Unity Shader 中实现漫反射光照模型

6.4.1 实践:逐顶点光照

源码

注意:若物体显示不正常,尝试将 PASS 的标签改为

js 复制代码
Tags { "LightMode"="UniversalForward" }

6.4.2 实践:逐像素光照

源码

注意事项同上


问题:光照无法到达的区域,模型的外观通常是全黑的,使该区域看起来像一个平面。半兰伯特光照模型可以改善这个问题

6.4.3 半兰伯特模型

《半条命》游戏开发公司 Valve 提出的一种技术,在原兰伯特光照模型基础上进行了一个简单的修改,因此得名

广义的半兰伯特光照模型公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> c d i f f u s e = ( c l i g h t ⋅ m d i f f u s e ( α ( n ^ ⋅ I ) + β ) ) c_{diffuse}=(c_{light}\cdot m_{diffuse}(\alpha (\hat{n}\cdot I)+\beta)) </math>cdiffuse=(clight⋅mdiffuse(α(n^⋅I)+β))

  • 与原模型相比,半兰伯特模型没使用 max 操作来防止 <math xmlns="http://www.w3.org/1998/Math/MathML"> n ^ \hat{n} </math>n^ 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> I I </math>I 的点积为负,而是对结果进行了一个 <math xmlns="http://www.w3.org/1998/Math/MathML"> α \alpha </math>α 倍的缩放再加上一个 <math xmlns="http://www.w3.org/1998/Math/MathML"> β \beta </math>β 大小的偏移
  • 绝大多数情况下, <math xmlns="http://www.w3.org/1998/Math/MathML"> a l p h a alpha </math>alpha 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> β \beta </math>β 的值均为 0.5

<math xmlns="http://www.w3.org/1998/Math/MathML"> c d i f f u s e = ( c l i g h t ⋅ m d i f f u s e ( 0.5 ( n ^ ⋅ I ) + 0.5 ) ) c_{diffuse}=(c_{light}\cdot m_{diffuse}(0.5 (\hat{n}\cdot I)+0.5)) </math>cdiffuse=(clight⋅mdiffuse(0.5(n^⋅I)+0.5))

修改一下 6.4.2 小节的代码就可实现:

效果图(逐顶点、逐像素、半兰伯特)

6.5 在 Unity Shader 中实现高光反射光照模型

6.5.1 实践:逐顶点光照

源码

效果:

问题:高光部分不平滑。原因是高光反射部分的计算是非线性 的,而在顶点着色器中计算光照再进行插值的过程是线性 的,破坏了原计算的非线性关系,就会出现较大的视觉问题 (详解

6.5.2 实践:逐像素光照

源码

效果(右边的是逐像素):

6.5.3 Blinn-Phong 光照模型

另一种高光反射模型------Blinn,它没有使用反射方向,而是引入一个新的矢量 <math xmlns="http://www.w3.org/1998/Math/MathML"> h ^ \hat{h} </math>h^,它是通过下面公式计算得到:

<math xmlns="http://www.w3.org/1998/Math/MathML"> h ^ = v ^ + I ∣ v ^ + I ∣ \hat{h}=\frac{\hat{v}+I}{|\hat{v}+I|} </math>h^=∣v^+I∣v^+I

Blinn 模型计算高光反射的公式是:

<math xmlns="http://www.w3.org/1998/Math/MathML"> c s p e c u l a r = ( c l i g h t ⋅ m s p e c u l a r ) m a x ( 0 , n ^ ⋅ h ^ ) m g l o s s c_{specular}=(c_{light}\cdot m_{specular})max(0,\hat{n}\cdot \hat{h})^{m_{gloss}} </math>cspecular=(clight⋅mspecular)max(0,n^⋅h^)mgloss

源码

效果(最右的):

可以看出 Blinn-Phong 光照模型的高光反射部分更大、更亮一些。在实际渲染中,绝大多数情况都会选择这个模型

6.6 召唤神龙:使用 Unity 内置函数

源码

效果跟原来的一致

  • 注意:内置函数得到的方向是没有归一化的,需要我们自己补充
相关推荐
字节跳动视频云技术团队3 天前
沉浸式文旅新玩法-基于4D GS技术的真人数字人赋能VR体验升级
unity3d·音视频开发·虚拟现实
这是我583 天前
unity实现梦日记式效果
unity·c#·游戏引擎·unity3d·游戏开发·csharp·c#11.0
kirayoshikake5 天前
UGUI 性能优化系列:第二篇——Canvas 与 UI 元素管理
unity3d
kirayoshikake5 天前
UI框架从0到1第九节:【脚本模板生成】怎么用代码生成代码?
unity3d
kirayoshikake5 天前
Unity入门教程之异步篇第五节:对UniTask的高级封装
unity3d
kirayoshikake5 天前
UI框架从0到1第二节:【全控件适配】把 Toggle、InputField 全都拉进事件系统
unity3d
kirayoshikake5 天前
UI框架从0到1第六节:【轻量 MVVM】用属性驱动 UI,彻底抛弃命令式调用
unity3d
kirayoshikake5 天前
Unity入门教程之异步篇第一节:协程基础扫盲--非 Mono 类如何也能启动协程?
unity3d
kirayoshikake5 天前
响应式编程入门教程第一节:揭秘 UniRx 核心 - ReactiveProperty - 让你的数据动起来!
unity3d