Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

2D 灯光和阴影

介绍

默认情况下,Godot 中的 2D 场景没有着色,不会显示任何灯光和阴影。虽然渲染速度很快,但无着色的场景可能会显得平淡。Godot 支持实时的 2D 光照和阴影,可以显著增强项目的深度感。

无 2D 灯光和阴影,场景无着色

无 2D 灯光和阴影,场景无着色

启用 2D 灯光(无阴影)

启用 2D 灯光(无阴影)

启用 2D 灯光和阴影

启用 2D 灯光和阴影

节点

完整的 2D 光照布置涉及多个节点:

CanvasModulate 用于通过指定一种颜色作为基础“环境”色来使场景变暗。该颜色是任何 2D 灯光都无法照射到的区域的最终光照颜色。如果没有 CanvasModulate 节点,2D 灯光将仅照亮现有的无光照外观(其本身看起来是完全亮着的),导致最终场景显得过亮。

Sprite2D 用于显示光晕、背景和阴影投射器的纹理。

PointLight2D 用于为场景提供光照。灯光通常的工作方式是在场景的其余部分叠加选定的纹理来模拟光照。

LightOccluder2D 用于告诉着色器场景中哪些部分会投射阴影。这些遮挡物可以作为独立节点放置,也可以作为 TileMapLayer 节点的一部分。

阴影只会出现在 PointLight2D 覆盖的区域上,方向基于 Light 的中心。

备注

背景色不接受任何光照。如果希望背景也受到光照影响,需要为背景添加一个可视化表示,例如 Sprite2D。

Sprite2D 的 Region 属性有助于快速创建重复的背景纹理,但记得在 Sprite2D 属性中将 Texture > Repeat 设置为 Enabled

点光源

点光源(也称为位置光源)是 2D 照明中最常见的元素。点光源可用于表示火把、火焰、投射物等发出的光。

PointLight2D 提供以下属性,可在检查器中进行调整:

  • Texture:用作光源的纹理。纹理的大小决定光源的大小。纹理可以带有 alpha 通道,这在使用 Light2D 的 Mix 混合模式时非常有用,但在使用 Add (默认)或 Subtract 混合模式时则不是必需的。

  • Offset:灯光纹理的偏移量。与移动灯光节点不同,改变偏移量不会导致阴影移动。

  • Texture Scale:灯光尺寸的乘数。数值越大,灯光的延伸范围越广。较大的灯光会影响屏幕上更多的像素,因此性能消耗也更高,所以在增大灯光尺寸前务必考虑这一点。

  • Height:灯光在法线贴图中的虚拟高度。默认情况下,灯光非常接近接收光照的表面。如果使用法线贴图,这将使灯光几乎不可见,因此可以考虑增加此值。调整灯光的高度只对使用法线贴图的表面产生可见差异。

如果没有预制纹理可用于灯光,可以使用这种 “中性 ”点光源纹理(右键单击 > 图像另存为... ):

中性点光源纹理

中性点光源纹理

如果你需要不同的衰减效果,可以通过在灯光的 Texture 属性上分配新建 GradientTexture2D 来程序化创建纹理。创建资源后,展开其 Fill 部分,将填充模式设置为 Radial 。然后你需要调整渐变本身,使其从不透明白色过渡到透明白色,并将其起始位置移动到中心。

平行光

平行光用于表示日光或月光。投射的光线相互平行,就好像太阳或月亮离接收光照的平面无穷远一样。

DirectionalLight2D 提供以下属性:

  • Height:灯光在法线贴图中的虚拟高度(0.0 = 平行于表面,1.0 = 垂直于表面)。默认情况下,灯光与接收光照的表面完全平行。如果使用法线贴图,这将使灯光几乎不可见,因此可以考虑增大此值。调整灯光的高度只会对使用法线贴图的表面产生可见差异。 Height 不会影响阴影的外观。

  • Max Distance:物体在其阴影被剔除前与相机中心的最大距离(单位:像素)。降低这个值可以防止位于相机外部的对象投射阴影(同时还可以提高性能)。 Max Distance 不考虑 Camera2D 的缩放,这意味着在更高的缩放值下,当缩放到一个给定的点时,阴影会更快地淡出。

备注

无论 Height 属性的值如何,平行光阴影看起来总是无限长。这是 Godot 中 2D 光照所用阴影渲染方法的一个限制。

要让平行光阴影不是无限长,应禁用 DirectionalLight2D 的阴影,转而使用自定义着色器读取 2D 带符号距离场。该距离场由场景中的 LightOccluder2D 节点自动生成。

通用灯光属性

PointLight2D 和 DirectionalLight2D 提供通用的属性,这些属性是 Light2D 基类的一部分:

  • Enabled:用于切换灯光的可见性。与隐藏灯光节点不同,禁用此属性不会隐藏灯光的子节点。

  • Editor Only:如果启用,灯光仅在编辑器中可见。在运行的项目中将自动禁用。

  • Color: 灯光的颜色。

  • Energy: 灯光强度乘数。数值越大,光线越亮。

  • Blend Mode:用于光照计算的混合公式。默认的 Add 适合大多数使用情况。Subtract 可用于负光,这在物理上并不精确,但可用于特殊效果。Mix 模式通过线性插值将灯光纹理对应的像素值与灯光下方的像素值混合。

  • Range > Z Min:受光照影响的最小 Z 值。

  • Range > Z Max:受光照影响的最大 Z 值。

  • Range > Layer Min:受光照影响的最小层数值。

  • Range > Layer Max:受光照影响的最大层数值。

  • Range > Item Cull Mask:用于控制哪些节点接收此光源的光照,取决于其他节点启用的可视层 Occluder Light Mask。这可以用来防止特定物体接收到光照。

设置阴影

在 PointLight2D 或者 DirectionalLight2D 节点上启用 Shadow > Enabled 属性之后,一开始不会看到任何视觉上的变化。这是因为场景中还没有任何节点拥有作为阴影投射基础的 遮挡器

要在场景中显示阴影,必须在场景中添加 LightOccluder2D 节点。这些节点还必须具有与精灵轮廓相匹配的遮挡多边形。

除了多边形资源(必须设置多边形资源才能产生视觉效果)之外,LightOccluder2D 节点还有两个属性:

  • SDF Collision:如果启用,则遮挡器将成为实时生成的带符号距离场的一部分,该距离场可在自定义着色器中使用。当不使用读取 SDF 的自定义着色器时,启用此属性不会带来视觉上的差异,也没有性能成本,因此为方便起见,默认情况下它是启用的。

  • Occluder Light Mask:此属性与 PointLight2D 和 DirectionalLight2D 的 Shadow > Item Cull Mask 属性搭配使用,用于控制每个光源下哪些物体会投射阴影。这可用于防止特定物体投射阴影。

有两种方法可以创建光线遮挡器:

自动生成光遮蔽器

可以通过选中 Sprite2D 节点,点击 2D 编辑器顶部的 Sprite2D 菜单,然后选择 创建 LightOccluder2D 同级节点 来从 Sprite2D 节点自动生成遮挡器。

在出现的对话框中,精灵边缘会出现一道轮廓。如果轮廓与精灵的边缘非常吻合,你可以点击确定。如果轮廓距离精灵的边缘太远(或“吃掉”了精灵的边缘),请调整扩展(像素)收缩(像素),然后点击更新预览。重复该操作,直到获得满意的结果为止。

手动绘制光照遮挡器

创建一个 LightOccluder2D 节点,然后选择该节点并单击 2D 编辑器顶部的“+”按钮。 当询问是否创建多边形资源时,回答。 然后你就可以通过点击来创建新的点,开始绘制遮挡多边形。你可以右键点击现有点来删除它们,也可以点击现有线条上的某处并拖动来创建新的点。

启用阴影的 2D 灯光能够调整以下属性:

  • Color:阴影区域的颜色。默认情况下,阴影区域是全黑的,但出于艺术目的可以更改。颜色的 alpha 通道用于控制阴影被指定颜色着色的程度。

  • Filter:阴影所使用的过滤模式。默认值为 None,渲染速度最快,并且非常适合像素艺术风格的游戏(因为它具有“方块”视觉效果)。如果你想要柔和的阴影,请使用 PCF5PCF13 则更加柔和,但渲染需求最高。由于渲染成本较高,PCF13 只应用于少量光源同时存在的情况下。

  • Filter Smooth:FilterPCF5PCF13 时,控制应用于阴影的柔和程度。数值越高,阴影越柔和,但可能会出现带状伪影(特别是使用 PCF5 时)。

  • Item Cull Mask:控制的是哪些 LightOccluder2D 节点能够投射阴影,取决于它们对应的 Occluder Light Mask(遮挡器光照掩码)属性。

备注

像素艺术游戏中的光照和阴影分辨率

引擎以视口的像素分辨率计算 2D 光照和阴影,而非源纹理的纹素分辨率。光照和阴影的外观取决于窗口或视口的分辨率,而不是单个精灵纹理的分辨率。

如果你制作的是一款像素艺术游戏,并且想要实现与你的美术风格相匹配的像素化或块状光照和阴影效果,那么 Nearest(最近邻)纹理过滤无法实现这一效果。最近邻过滤只会影响引擎对纹理的采样方式,并不会改变引擎渲染光照和阴影的方式。

要实现像素化的光照和阴影,可以使用自定义着色器修改 LIGHT_VERTEXSHADOW_VERTEX ,使光照采样吸附到像素网格 。以下着色器使用 floor()函数将光照吸附到网格 :

shader_type canvas_item;

uniform float pixel_size = 4.0;

void fragment() {
    // Snap lighting and shadows to pixel grid.
    LIGHT_VERTEX.xy = floor(LIGHT_VERTEX.xy / pixel_size) * pixel_size;
    SHADOW_VERTEX = floor(SHADOW_VERTEX / pixel_size) * pixel_size;

    // Normal rendering.
    COLOR = texture(TEXTURE, UV);
}

其工作原理是:首先将位置除以 pixel_size 转换为网格空间,然后使用 floor() 向下取整到最近的网格点,最后再乘回去转换回屏幕空间。这样就能强制引擎从离散的网格位置采样光照,从而产生像素化效果。

有关画布项着色器如何工作的更多信息,请查看《使用画布项着色器》。

硬阴影

硬阴影

柔和阴影(PCF13,过滤平滑 1.5)

柔和阴影(PCF13,过滤平滑 1.5)

有带状伪影的柔和阴影,原因是过滤平滑太高(PCF5,过滤平滑 4)

有带状伪影的柔和阴影,原因是过滤平滑太高(PCF5,过滤平滑 4)

法线和镜面贴图

法线贴图和镜面贴图可以大大提升 2D 光照的立体感。与 3D 渲染类似,法线贴图可以根据接收光照的表面方向(基于像素)来改变光照强度,从而使照明效果不再平面化。镜面贴图则通过将一部分光线反射回观察者来进一步改善视觉效果。

PointLight2D 和 DirectionalLight2D 都支持法线贴图和镜面贴图。自 Godot 4.0 起,法线贴图和镜面贴图可分配给任何 2D 元素,包括继承自 Node2D 或 Control 的节点。

法线贴图表示每个像素“指向”的方向。引擎会利用这些信息,以物理上合理的方式将光照正确应用到 2D 表面。法线贴图通常由手绘的高度贴图创建,但也可以由其他纹理自动生成。

镜面贴图定义了每个像素对光线的反射程度(以及反射的颜色,如果镜面贴图包含颜色信息)。亮度值越高,纹理上指定位置的反射就越亮。镜面贴图通常以漫反射纹理为基础,通过手动编辑创建。

小技巧

如果你没有精灵的法线贴图或者镜面贴图,可以使用免费的开源工具 Laigter 来生成。

要在 2D 节点上设置法线贴图和/或镜面贴图,请为绘制节点纹理的属性创建一个新的 CanvasTexture 资源。例如,在 Sprite2D 节点上:

为 Sprite2D 节点创建 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“灯光”。

不过,与 2D 灯光相比,添加混合精灵有一些缺点:

  • 与“实际”二维光照相比,混合公式并不准确。这在光线充足的区域通常不是问题,但这使得添加混合精灵无法正确照亮完全黑暗的区域。

  • 添加混合精灵无法投射阴影,因为它们不是灯光。

  • 添加混合精灵会忽略其他精灵上使用的法线贴图和镜面贴图。

要显示一个采用添加混合的精灵,请创建一个 Sprite2D 节点并为其分配一个纹理。在检查器中,向下滚动到 CanvasItem > Material 部分,展开它并点击 Material 属性旁边的下拉按钮。选择 New CanvasItemMaterial,点击新创建的材质进行编辑,然后将 Blend Mode 设置为 Add