2D 灯光和阴影

简介

本教程以 灯与影 演示项目解释了2D灯光的工作原理。 本节首先简要介绍了最终demo所使用的资源,然后介绍了如何逐步实现类似demo的场景。

../../_images/light_shadow_main.png

本教程的所有资源都可以在github上的 官方演示存储库 中找到。 我建议您在开始之前下载它。 或者,也可以在项目管理器中下载。 启动Godot并在顶部栏中选择 “Templates” 并搜索 “2D Lights and Shadows Demo”。

场景布置

对于这个演示,我们使用四种纹理:两种用于灯光,一种用于那些会产生阴影的物品,一种用于背景。 如果想单独下载它们,我在这里列出了所有链接。

第一个是演示中使用的背景图像 (bg.png ) 。 你不一定需要背景,但我们还是在演示中用了一个。

第二个是用作影子的纯黑色图片 (caster.png ) 。 对跳跃游戏,它可能是墙或任何其他投射阴影的物体。

接下来是灯本身 (light.png ) 。 如果单击该链接,您会注意到它有多大。 用于灯光的图像应覆盖您希望灯光覆盖的区域。 此图像为1024x1024像素,因此您应该使用它来覆盖游戏中的1024x1024像素。

最后,我们有聚光灯图像 (spot.png ) 。 该演示使用灯泡来显示灯光的位置,使用较大的灯光图片来显示灯光对场景其余部分的影响。

节点

该演示使用了四个不同类型的节点:
  • CanvasModulate
  • Sprite
  • Light2D
  • LightOccluder2D

CanvasModulate 用于使场景变暗。

Sprites 用于显示灯泡,背景和产生阴影的物体的纹理。

Light2Ds 用于点亮场景。 光通常的工作方式是在场景的其余部分添加选定的纹理以模拟光照。 但它可以以其他方式使用,例如屏蔽部分场景。

LightOccluder2Ds 用于告诉着色器, 场景的哪些部分投射阴影。 阴影仅出现在 Light2D 所覆盖的区域,它们的方向基于 Light 的中心。

灯光

Lights 覆盖各自纹理的整个范围。 它们使用Additive Blending将其纹理颜色添加到场景中。

../../_images/light_shadow_light.png

Lights 有四个 模式: add, sub, mix, 和 mask

Add 将光纹理的颜色添加到场景中。 它会照亮灯光下的区域。

Sub 从场景中减去光的颜色。 它使灯光下的区域变暗。

Mix 混合了灯光的颜色和底层场景。 产生的亮度介于灯光颜色和下方颜色之间。

Mask 用于遮盖灯光覆盖的区域。 根据光的颜色隐藏或显示遮盖区域。

对于这个demo,灯光有两个组件, Light 本身(这是灯光的效果),和 Sprite 灯泡(一个显示光源位置的图片)。 子节点 Sprite 不是让 Light 工作的必要条件。

../../_images/light_shadow_light_blob.png

阴影

阴影是通过让 LightLightOccluder2D 的范围相交来制作的。

默认情况下,阴影处于关闭状态。 要打开它们,请单击 Light 并在Shadows部分下面选中 启用

在演示中我们使用带有纹理的 Sprite 来制作“投射影子的物体”,但实际上您只需要几个 LightOccluder2DsLightOccluder2D 本身看起来像一个黑点,在这个演示中 Sprite 只是一个黑色方块。

渐进式教程

既然我们已经介绍了需用到的节点的基础知识,那就可以开始逐步实现demo中的场景了。

首先添加一个 Sprite 并将其纹理设置为 background image 。 在你的游戏中这可以是你选择的任何背景。 对于我们这种风格的阴影, 这张图最可能是地板的纹理。

../../_images/light_shadow_background.png

接下来创建三个 Light2D’s 并将它们的纹理设置为 light image 。 你可以在上面的栏目中更改颜色。 默认情况下,阴影关闭并且 mode 设置为 add 。 这意味着每个灯光都会将自己的颜色添加到下面的任何颜色中。

../../_images/light_shadow_all_lights_no_blob.png

接下来为每个 Light 节点添加一个 Sprite 子节点,并将 Sprite’s 纹理设置为 blob image 。 他们都应该保持在 Light 节点的中心。blob是灯本身的图像,而 Light 显示灯光对场景的影响。 LightOccluder2D’s 会将光的位置视为 Light 节点的中心,这就是为什么我们希望blob位于父节点 Light 的中心。

../../_images/light_shadow_all_lights.png

注解

在撰写本文时,3.0是稳定版本。 3.1开发版本已经对动画系统的做了许多更改,因此此处不会介绍demo中的动画。 请参阅 2D动画功能简介 以了解更多信息。

现在场景看起来太亮了。 这是因为三个灯都在为场景添加颜色。 这就是演示场景使用 CanvasModulate 的原因。 CanvasModulate 将整个视区乘以特定颜色。

在场景中添加 CanvasModulate 并将其颜色设置为 rgb(70, 70, 70) 。 这将使场景足够暗,以清楚地看到灯光的效果。

../../_images/light_shadow_ambient.png

现在,我们来添加会投射阴影的物体。

该演示使用了一个名为“casters”的 Node 来组织投射阴影的物体。 在场景中添加 Node2D 。 它会被用来把所有投射阴影的物体变成一个大组。 这样我们就可以同时显示和隐藏它们。

每个阴影投射器都由具有 LightOccluder2D 子节点的 Sprite 组成。 在演示中, Sprite 的纹理设置为 caster image 。 子节点 LightOccluder2D 是所有神奇的事情发生的地方。 在游戏中 Sprite 可能不仅仅是一个黑色的方块; 它可以是任何可以投射阴影的物体的图片:一面墙,一个宝箱或其他任何东西。

../../_images/light_shadow_sprites.png

LightOccluder2Ds 告诉游戏Occluder(遮光物)是什么形状。Occluder是一个多边形的容器 OccluderPolygon2D 。 对于这个演示,由于我们的墙是正方形,我们将 Polygon 设置为正方形。 其他设置保持为默认。

对于第一个设置, Closed 可以是 onoff 。 闭合的多边形会遮挡来自所有方向的光线。 开放多边形仅遮挡来自一个方向的光线

Cull Mode 让您选择剔除的方向。 默认值为“已禁用”,这意味着无论灯光在哪一侧,遮挡物都会投射阴影。 另外两个设置 ClockwiseCounter-Clockwise 指的是多边形顶点的弯曲顺序。 弯曲顺序用于确定光线的哪一侧在多边形内。 只有朝外的光线才投下阴影。

为了说明它们的差异,这张图片里是一个相应的 LightOccluder2DClosed 设置为 ``off``的:ref:OccluderPolygon2D <class_OccluderPolygon2D> 。 这是为了可以看到多边形的轮廓:

../../_images/light_shadow_cull_disabled.png

注解

Cull Mode 设置为 Disabled 。 全部三条轮廓线都投下阴影。

../../_images/light_shadow_cull_clockwise.png

注解

Cull Mode 设置为 Clockwise 。 只有顶部和右侧线条投射阴影。

../../_images/light_shadow_cull_counter_clockwise.png

注解

Cull Mode 设置为 Counter-Clockwise 。 只有底部的轮廓线投下阴影。 如果 Closed 设置为 on ,左边会有一条额外的垂直轮廓线也会投射阴影。

当你添加 LightOccluder2Ds 时,阴影仍不会出现。 你需要回到 Light2Ds 并在Shadow部分下设置 Enableon 。 这将打开带有硬边缘的阴影,如下图所示。

../../_images/light_shadow_filter0_pcf0.png

为了给出看起来更好的柔和边缘阴影,我们设置变量 filterfilter smoothgradient length 。 Godot支持 Percentage Closer Filtering (PCF) ,它在像素周围提取阴影贴图的多个样本,并模糊它们以创建平滑的阴影效果。 样本数越多,阴影看起来越平滑但运行速度越慢。 这就是为什么Godot默认提供3-13个样本并让你可以自己选择用几个样本。 该演示使用PCF7。

../../_images/light_shadow_normal.png

注解

这是使用演示中设置渲染的阴影。 gradient length 设置为 1.3filter smooth 设置为 11.1filter 设置为 PCF7

../../_images/light_shadow_pcf13.png

注解

filter 设置为 PCF13 。 注意这里阴影变宽了,这是因为样本之间的距离是依赖于变量 filter smooth 的。

为了使用filtering(过滤),你需要设置 filter smooth 变量。 这决定了样品之间的距离。 如果您想让柔软区域延伸很远,您可以增加 filter smooth 的大小。 然而,在低样品和大过滤平滑的情况下,你会看到样本之间形成的线条。

../../_images/light_shadow_filter30.png

注解

filter smooth 设置为 30

演示中的不同 Light 节点使用不同的filter smooth值。 玩玩它, 看看你喜欢什么样的。

../../_images/light_shadow_filter0.png

注解

filter smooth 设置为 0

最后谈谈变量 gradient length 。 对于一些平滑阴影,最好不要在对象上立即开始阴影,因为这会产生硬边。 Gradient length(渐变长度)变量让阴影刚开始的时候平滑渐变,以减少硬边缘的影响。

../../_images/light_shadow_grad0.png

注解

gradient length 设置为 0

../../_images/light_shadow_grad10.png

注解

gradient length 设置为 10

您需要稍微调整设置以找到适合你项目的设置。 没有对每个人都正确的解决方案,这就是Godot为什么提供如此多灵活性。 只要记住``filter`` 值越高, 阴影的计算成本也越高就行了。