2D 灯光和阴影
前言
默认情况下,Godot 中的 2D 场景采用无光照着色,看不到灯光和阴影。虽然渲染速度很快,但无阴影的场景看起来会很平淡。Godot 提供了可以使用实时 2D 照明和阴影的功能,可以大大增强项目的层次感。
节点
完整的二维照明设置涉及多个节点:
CanvasModulate 用于使场景变暗
PointLight2D(用于全向或点光源)
DirectionalLight2D(用于日光或月光)
其他可接收光照的 2D 节点,如 Sprite2D 或 TileMap。
CanvasModulate 用于指定一种颜色作为“环境”基色,从而使场景变暗。这是任何 2D 灯光都 无法 到达区域的最终照明颜色。如果没有 CanvasModulate 节点,由于 2D 灯光只会照亮现有的无阴影外观(看起来完全亮了),最终场景看起来会过于明亮。
Sprite2Ds 用于显示灯泡、背景和阴影投射器的纹理。
Light2Ds 用于点亮场景。光通常的工作方式是在场景的其余部分添加选定的纹理以模拟光照。但它可以以其他方式使用,例如屏蔽部分场景。
LightOccluder2Ds 用于告诉着色器场景的哪些部分投射阴影。 这些遮挡物可以放置为独立节点,也可以作为 TileMap 节点的一部分。
阴影仅出现在 PointLight2D 覆盖的区域上,并且其方向基于 Light 的中心。
备注
背景色 不会 接收任何光照。如果要在背景上投射灯光,则需要为背景添加可视化表示,例如 Sprite2D。
Sprite2D 的 Region 属性有助于快速创建重复的背景纹理,但要记住在 Sprite2D 属性中将 Texture > Repeat 设置为 Enabled 。
点光源
点光源(也称位置光源)是 2D 照明中最常见的元素。点光源可用于表示火把、火、射弹等发出的光。
PointLight2D 提供了以下属性,可在检查器中进行调整:
纹理: 用作光源的纹理。纹理的大小决定光源的大小。纹理可以有一个 alpha 通道,这在使用 Light2D 的 Mix 混合模式时非常有用,但在使用 Add (默认)或 Subtract 混合模式时则不需要。
偏移量: 灯光纹理的偏移量。与移动灯光节点不同,改变偏移量 不会 导致阴影移动。
纹理缩放: 灯光大小的乘法器。数值越大,灯光越亮。较大的灯光会影响屏幕上更多的像素,因此在增大灯光尺寸前要考虑到这一点。
高度: 灯光在法线贴图中的虚拟高度。默认情况下,灯光与接收灯光的表面非常接近。如果使用法线贴图,这将使灯光几乎不可见,因此可以考虑增加此值。只有在使用法线贴图的表面上,调整灯光的高度才会产生明显的不同。
如果没有预制纹理可用于灯光,可以使用这种 “中性 ”点光源纹理(右键单击 > 图像另存为… ):
如果你需要不同的渐变,可以通过在灯光的 纹理 属性上分配 新建 GradientTexture2D 来程序化地创建纹理。创建资源后,展开其 Fill 部分并将填充模式设置为 Radial 。然后,你需要调整渐变本身,使其从不透明的白色开始到透明的白色,并将其起始位置移动到中心。
平行光
Godot 4.0 的新功能是在 2D 中实现定向照明。定向照明用于表现阳光或月光。光线相互平行投射,就好像太阳或月亮离受光表面无限远一样。
DirectionalLight2D 提供以下的属性:
高度: 灯光在法线贴图中的虚拟高度(
0.0
= 平行于曲面,1.0
= 垂直于曲面)。默认情况下,灯光与接收灯光的表面完全平行。如果使用法线贴图,这将使灯光几乎不可见,因此可以考虑增大此值。调整灯光的高度只会对使用法线贴图的表面产生视觉差异。 高度 不会影响阴影的外观。最大距离: 物体距离摄像机中心的最大距离(单位:像素)。减小该值可以防止对位于摄像机外的物体投射阴影(同时还能提高性能)。 最大距离 不考虑 Camera2D 的缩放,这意味着在较高的缩放值下,当缩放至给定点时,阴影会更快消失。
备注
无论 Height 属性的值是多少,定向阴影看起来总是无限长。这是 Godot 中用于 2D 灯光的阴影渲染方法的限制。
不想获得无限长的定向阴影,应禁用 DirectionalLight2D 中的阴影,并使用自定义着色器来读取 2D 带符号距离场。该距离场从场景中的 LightOccluder2D 节点自动生成。
常用灯光属性
PointLight2D 和 DirectionalLight2D 都提供共同的属性,这些属性是 Light2D 基类的一部分:
启用: 允许切换灯光的可见性。与隐藏灯光节点不同,禁用此属性不会隐藏灯光的子节点。
仅编辑器:如果启用,灯光仅在编辑器中可见。在运行的项目中将自动禁用。
颜色: 灯光的颜色。
能量: 灯光强度乘数。数值越大,光线越亮。
混合模式: 用于光线计算的混合公式。默认的 添加(Add) 适合大多数使用情况。 减(Subtract) 可用于负光,负光在物理上并不精确,但可用于特殊效果。 混合(Mix) 模式通过线性插值将灯光纹理对应的像素值与灯光下方的像素值混合。
范围 > Z 下限: 受光线影响的最小 Z 值。
范围 > Z 上限: 受光线影响的最大 Z 值。
范围 > 层下限: 受光线影响的最小层数值。
范围 > 层上限: 受光线影响的最大层数值。
范围 > 对象遮罩: 根据其他节点的可视层选项**Occluder Light Mask**(遮挡掩膜),控制那些节点接收到来自这个节点的光线。通过这种方式可以让某些物体不被光线照射。
设置阴影
启用一个 PointLight2D 或者 DirectionalLight2D 节点的 Shadow > Enabled 属性之后,你将看不到任何变化。这是因为在你的场景中还没有任何节点拥有投射阴影需要使用的 遮挡器 。
要在场景中显示阴影,必须在场景中添加 LightOccluder2D 节点。这些节点还必须具有与精灵轮廓相匹配的遮光多边形。
除了多边形资源(必须设置多边形资源才能产生视觉效果)之外,LightOccluder2D 节点还有两个属性:
SDF碰撞:如果启用,则遮挡器将成为可在自定义着色器中使用的实时生成的 签名距离字段 (signed distance field)的一部分。当不使用从此 SDF 中读取的自定义着色器时,启用这个功能不会带来视觉上的差异,并且没有性能成本,因此默认情况下为方便起见它是启用的。
遮挡器光照蒙版: 这与 PointLight2D 和 DirectionalLight2D 的 ** Shadow > Item Cull Mask ** 属性一起使用,以控制哪些对象为每个光源投射阴影。 这可用于防止特定对象投射阴影。
有两种方法可以来创建光线遮挡器:
自动生成光遮蔽器
遮挡器可以自动地 Sprite2D 节点上创建,需要选中节点,单击2D编辑器顶部的 Sprite2D 菜单,然后选择 创建 LightOccluder2D 兄弟节点 从而自动进行。
在出现的窗口中,一个外框将会包裹在你的精灵的边缘。如果外框贴合在了你的精灵的边缘上,你可以点击**确定**。如果外框离你的精灵边缘太远(或者它穿过了你的精灵的边缘),调整**扩展(像素)**和**收缩(像素)**,然后点击**更新预览**。重复这一个操作直到你对结果满意为止。
手动绘制光遮蔽器
创建一个 LightOccluder2D 节点,然后选择该节点并单击 2D 编辑器顶部的“+”按钮。 当要求创建多边形资源时,回答 是 。 然后,你可以通过单击创建新点来开始绘制遮挡多边形。你可以通过右键单击现有点来删除它们,也可以通过单击线条然后拖动来从现有线条创建新点。
启用阴影的 2D 灯光能够调整以下属性:
Color:阴影区域的颜色。默认情况下,阴影区域是全黑的,但这可以出于艺术目的而改变。颜色的 alpha 通道控制的是阴影被指定颜色着色的程度。
Filter:阴影所使用的过滤模式。默认值为 None,渲染速度最快,并且非常适合像素艺术风格的游戏(因为它具有“方块”视觉效果)。如果你想要柔和的阴影,请使用 PCF5。PCF13 则更柔和,但渲染需求更高。由于渲染成本较高,PCF13 只应用于少量光源同时存在的情况下。
Filter Smooth:过滤平滑。控制的是当 Filter 为 PCF5 或* PCF13* 时,应用于阴影的柔化程度。较高的值会导致阴影更加柔和,但可能会出现带状伪影(特别是使用 PCF5 时)。
Item Cull Mask:项目剔除遮罩。控制的是哪些 LightOccluder2D 节点能够投射阴影,取决于对应的 Occluder Light Mask(遮挡器灯光遮罩)属性。
遮挡器绘制顺序
LightOccluder2D 遵循常规的 2D 绘图顺序。这对于 2D 灯光而言非常重要,因为可以用来控制遮挡器是否应该遮挡精灵本身。
如果 LightOccluder2D 节点是精灵的兄弟节点,并且场景树中的遮挡器被放在精灵的下方,会遮挡住精灵本身。
如果 LightOccluder2D 节点是一个精灵的子节点,如果在 LightOccluder2D 节点中禁用了 Show Behind Parent(显示在父级之后)这个遮挡器将遮挡住精灵本身(该选项默认禁用)。
法线和镜面贴图
法线贴图和镜面贴图可以大大提升你的2D光照的立体感。与3D渲染类似,法线贴图可以根据接收光线的表面方向来改变光线的强度,从而使照明效果不再平面化(按像素进行调整)。镜面贴图通过让一部分光线反射回观察者来进一步改善视觉效果。
PointLight2D 和 DirectionalLight2D 都支持法线贴图和镜面贴图。自 Godot 4.0 起,法线贴图和镜面贴图可分配给任何 2D 元素,包括继承自 Node2D 或 Control 的节点。
法线贴图表示每个像素 “指向” 的方向。引擎会利用这些信息,以物理上合理的方式将光照正确应用到 2D 表面。法线贴图通常由手绘的高度贴图创建,但也可以由其他纹理自动生成。
镜面贴图定义了每个像素对光线的反射程度(如果镜面贴图包含颜色,则定义反射的颜色)。亮度值越高,纹理上指定位置的反射就越亮。镜面贴图通常以漫反射纹理为基础,通过手动编辑创建。
小技巧
如果在你的精灵中没有使用法线贴图或者镜面贴图,可以使用免费的开源工具`Laigter <https://azagaya.itch.io/laigter>`来生成。
要在 2D 节点上设置法线贴图和/或镜面贴图,请为绘制节点纹理的属性创建一个新的 CanvasTexture 资源。例如,在一个 Sprite2D 节点上创建一个新的 CanvasTexture 资源:
展开新创建的资源。你可以找到需要调整的几个属性:
Diffuse > Texture:(漫反射 > 纹理)基础的颜色贴图。在这个属性中,加载你将使用在精灵本身的纹理。
Normal Map > Texture:(法线贴图 > 纹理)法线贴图的纹理。在这个属性中,你可以加载从高度图生成的法线贴图纹理(见上面的提示)。
Specular > Texture:(镜面反射 > 纹理)镜面贴图纹理,可以控制漫反射纹理上每个像素的镜面反射强度。镜面贴图通常使用灰度反射,但是它也可以包含色彩来增强反射的颜色。在这个属性中,加载一个已创建的镜面贴图纹理(见上面的提示)。
Specular > Color:(镜面反射 > 颜色)镜面反射的颜色乘数。
Specular > Shininess:(镜面反射 > 光泽度)用于镜面反射的高光指数。值越低,反射的明亮度和扩散性会增加,而值越高,反射会更加局部化。较高的值适用于湿润表面。
Texture > Filter:(纹理 > 过滤器)可以设置为覆盖纹理过滤模式,无论节点属性设置如何(或渲染 > 纹理 > 画布纹理 > 默认纹理过滤项目设置)。
Texture > Repeat:(纹理 > 重复)可以设置为覆盖纹理过滤模式,无论节点的属性如何设置(或者渲染 > 纹理 > 画布纹理 > 默认纹理重复项目设置)。
启用法线贴图后,你可能会注意到灯光会显得较弱。为了解决这个问题,可以增加 PointLight2D 和 DirectionalLight2D 节点上的 Height 属性。也可以略微增加灯光的 Energy 属性,以接近启用法线贴图之前的照明强度。
使用添加式精灵作为 2D 灯光的快速替代品
如果在使用 2D 灯光时遇到性能问题,不妨将其中一些节点替换为使用叠加混合的 Sprite2D 节点。这尤其适用于短暂的动态效果,如子弹或爆炸。
添加式精灵的渲染速度要快得多,因为它们不需要通过单独的渲染管道。此外,这种方法还可以与 AnimatedSprite2D(或 Sprite2D + AnimationPlayer)一起使用,这样就可以创建动画二维 “灯光”。
不过,与 2D 灯光相比,添加式精灵有一些缺点:
与 “实际 ”二维光照相比,混合公式并不准确。这在光线充足的区域通常不是问题,但这会妨碍添加精灵去正确照亮那些完全黑暗的区域。
添加式精灵不能投射阴影,因为它们不是灯光。
添加式精灵会忽略其他精灵上使用的法线贴图和镜面贴图。
要显示一个混合叠加效果的精灵,需要创建一个 Sprite2D 节点并分配一个纹理给它。在检视窗口中,往下滚动到 CanvasItem > Material 部分,展开它并点击 Material 属性旁边的下拉菜单。选择 New CanvasItemMaterial,点击新建的材质来编辑它,然后将 Blend Mode 设置为 Add。