3D 灯光和阴影

简介

灯发出的光与材质混合并产生可见的结果. 光可以来自场景中的几种类型的光源:

  • 以自发光颜色(Emission Color)的形式从材质(Material)本身发出,(虽然除非烘培贴图, 否则它不会影响附近的物体).

  • 光节点: 方向光(Directional), 万向光(Omni), 和聚光灯(Spot).

  • Environment 中的环境光.

  • 烘焙光 (参见 烘焙光照贴图).

自发光颜色是材质特性. 可以在 空间材质 教程中阅读更多相关内容.

光节点

如前所述, 有三种类型的光节点: 方向光 Directional light , 万向光 Omni light 和聚光灯 Spot light . 让我们来看看光的通用参数:

../../_images/light_params.png

它们每一个都有特定的功能:

  • Color(颜色) : 发光的基色.

  • Energy(能量) : 能量乘数. 这对于使灯光饱和或使用 高动态范围光照(HDR) 非常有用.

  • Indirect Energy(间接能量) : 与间接光一起使用的次级乘数(光是会反弹的). 这适用于烘焙的光线或GIProbe(GI探针).

  • Negative(减色) : 光变为减色而不是添加. 手动补偿一些黑暗角落有时很有用.

  • 反射(Specular) : 影响受此光影响的物体中镜面反射高光斑点的强度. 在值为零时, 该光变为纯漫反射光.

  • Bake Mode(烘培模式) : 设置灯光的烘焙模式. 有关更多信息, 请参见 烘焙光照贴图

  • 剔除蒙版Cull Mask: 在下面选定的图层中的物体将受到此光的影响。请注意,通过这个遮罩禁用的对象仍然会投射阴影。如果你不希望被禁用的物体投射阴影,请将GeometryInstance上的 cast_shadow 属性调整为所需值。

阴影贴图

灯光可以可选地投射阴影. 这使它们具有更好的真实感(光线不会照到被遮挡的区域), 但它会带来更大的性能开销. 有一个通用阴影参数列表, 每个参数也有一个特定的功能:

  • Enabled(启用) : 勾选以启用阴影贴图.

  • Color(颜色) : 遮挡的区域会被乘以此颜色. 默认为黑色, 但可以更改来给阴影染色.

  • Bias(偏移) : 当此参数太小时, 阴影会打在物体自己身上. 当太大时, 阴影与物体本体分开. 调整到最适合你的状态.

  • Contact(接触) : 执行一个短促的屏幕空间光线投射, 以减少上述偏差产生的间隙. 接触阴影仅在使用GLES3后端是可用.

  • Reverse Cull Faces(反向剔除平面) : 某些场景在阴影渲染时, 开启反向平面剔除会工作得更好.

下图是调整Bias(偏移)的样子. 默认值适用于大多数情况, 但通常取决于几何的大小和复杂程度.

../../_images/shadow_bias.png

最后, 如果无法解决间隙, ** Contact(接触)** 选项可以帮助:

../../_images/shadow_contact.png

任何类型的偏差问题都可以通过增加阴影贴图分辨率来解决, 尽管这可能会导致低端硬件性能下降.

方向光

这是最常见的光线类型, 代表了很远的光源(如太阳). 它也是计算中最便宜的光, 应该尽可能使用(虽然它不是计算起来最便宜的阴影贴图, 但这点稍后再说).

定向光模拟覆盖整个场景的无限数量的平行光线. 定向光节点由指示光线方向的大箭头表示. 但是, 节点的位置根本不会影响照明, 它可以在任何地方.

../../_images/light_directional.png

每个表面的正面被光线照射, 而其他部分则保持黑暗. 大多数灯光类型都有特定的参数, 但定向灯本质上非常简单, 所以它们没有.

方向光阴影贴图

为了计算阴影贴图, 会从正交视图(仅深度)渲染整个场景(或直到最大距离). 但是, 这种方法存在问题, 因为靠近相机的物体会得到块状阴影.

../../_images/shadow_blocky.png

为了解决这个问题, 使用了名为"Parallel Split Shadow Maps"(平行分割阴影, 或PSSM)的技术. 这将视锥体分成2或4个区域. 每个区域都有自己的阴影贴图. 这允许靠近观察者的小区域具有与巨大的遥远区域相同的阴影分辨率.

../../_images/pssm_explained.png

有了它, 阴影变得更加详细:

../../_images/shadow_pssm.png

为了控制PSSM, 暴露了许多参数:

../../_images/directional_shadow_params.png

每个分割距离都是相对于相机最远处(或阴影 Max Distance(最大距离) , 如果大于零)控制的, 因此 0.0 是眼睛位置, 1.0 是阴影在一定距离处结束的位置. 分割介于两者之间. 默认值通常效果很好, 但调整第一个分割数值是很常见的, 可以为近处对象提供更多细节(比如第三人称游戏中的角色).

始终确保根据场景需要设置阴影 Max Distance(最大距离) . 最大距离越近, 阴影的质量越高.

有时, 一个分割与下一个之间的过渡看起来很糟糕. 要解决此问题, 可以打开 "Blend Splits"(混合分割) 选项, 牺牲细节以换取更平滑的过渡:

../../_images/blend_splits.png

当物体垂直于光线时, "Normal Bias"(法线偏差) 参数可用于修复自阴影的特殊情况. 唯一的缺点是它使阴影更薄.

../../_images/normal_bias.png

"Bias Split Scale"(偏差分割缩放) 参数可以控制远处分割的额外偏差. 如果仅在远处的分割上发生自阴影, 则此值可以修复它们.

最后, "Depth Range"(深度范围) 有两个设置:

  • Stable(稳定) : 在相机移动时保持阴影稳定, 并且当靠近阴影边缘时出现在轮廓中的块保持原位. 这是默认, 通常想要的设置, 但会降低有效的阴影分辨率.

  • Optimized(最优) : 会尝试在任何给定时间达到最大可用分辨率. 这可能会导致阴影边缘出现 "移动锯" 效果, 但同时阴影看起来更加细致(因此这种效果可能非常轻微, 可以被原谅).

尝试哪种设置更适合你的场景.

可以在Project Settings(项目设置) ->Rendering(渲染) ->Quality(质量)中更改方向光的阴影贴图大小:

../../_images/project_setting_shadow.png

增加它可以解决偏差问题, 但会降低性能. 阴影贴图(Shadow mapping)是一门调整的艺术.

全向光

全向光是一种点光源, 可在所有方向上发射光, 直至给定半径.

../../_images/light_omni.png

在现实生活中, 光衰减是个和距离成反比的函数, 这意味着全向光没有半径. 这是一个问题, 因为这意味着计算几个全向光会变得很困难.

为了解决这个问题, 引入了 Range(范围) 和衰减函数.

../../_images/light_omni_params.png

这两个参数允许调整其在视觉上的工作方式, 以便找到美学上令人愉悦的结果.

../../_images/light_attenuation.png

全向光阴影贴图

全方位光影贴图相对简单. 需要考虑的主要问题是用于渲染它的算法.

全向光阴影可以渲染为 "Dual Paraboloid"(双抛物面)或"Cube Mapped"(方形映射) . 前者渲染速度很快但会导致变形, 而后者更正确但成本更高.

../../_images/shadow_omni_dp_cm.png

如果渲染的对象大部分是不规则的, 那么Dual Paraboloid 通常就足够了. 无论怎么说, 由于这些阴影被缓存在阴影图集中(后面会详细讲), 对于大多数场景而言, 它可能不会对性能产生影响.

Spot Light(聚光灯)

聚光灯类似于全向光, 除了它们只发光到锥形(或 "截断"). 它们可用于模拟手电筒, 汽车灯, 反射器, 光点等. 这种类型的光也会向其指向的相反方向衰减.

../../_images/light_spot.png

聚光灯和 Omni Light(全向光) 共用 Range(范围)Attenuation(衰减) , 并添加两个额外参数:

  • Angle(角度) : 光线的光圈角度

  • Angle Attenuation(角度衰减) : 锥形衰减, 有助于软化锥形边界.

聚光灯阴影贴图

聚光灯不需要任何阴影映射参数. 请记住, 在超过89度的光圈下, 阴影会停止对聚光灯起作用, 你应该考虑使用全向光.

影子图集

与具有自己的阴影纹理的定向光不同, 全向光和聚光灯被分配了阴影图集的槽位. 可以在Project Settings(项目设置) -> Render(渲染) -> Quality(质量) -> Shadow Atlas(阴影图集) 中配置此图集.

../../_images/shadow_atlas.png

这个分辨率适用于整个影子图集. 该地图集分为四个象限:

../../_images/shadow_quadrants.png

每个象限可以细分, 分配任意数量的阴影贴图. 以下是默认细分方式:

../../_images/shadow_quadrants2.png

分配逻辑很简单. 最大的阴影贴图大小(当不使用细分时)表示一个屏幕大小(或更大)的光. 细分区块(较小的贴图)表示远离视图并且成比例地较小的光的阴影.

每一帧, 以下过程被应用于所有光:

  1. 检查灯光是否在正确大小的插槽上. 如果没有, 重新渲染它并将其移动到更大/更小的插槽.

  2. 检查影响阴影贴图的任何对象是否已更改. 如果是的话, 重新渲染光线.

  3. 如果上述情况均未发生, 则不执行任何操作, 阴影保持不变.

如果一个象限中的槽位满了, 光线会被推回到更小的槽位中, 取决于大小和距离.

此分配策略适用于大多数游戏, 但在某些情况下你可能会想用一个单独分开的贴图(例如, 一个自上向下看的游戏, 其中所有灯光大小大概相同, 所有象限的细分可能会具有相同大小).

阴影滤波质量

可以调整阴影的滤波质量. 这可以在Project Settings(项目设置) -> Rendering(渲染) -> Quality(质量) -> Shadows(阴影)中找到. Godot支持无滤波,PCF5和PCF13.

../../_images/shadow_pcf1.png

它会影响阴影轮廓的块状程度:

../../_images/shadow_pcf2.png