2D 灯光和阴影
简介
本教程以 灯与影 演示项目解释了2D灯光的工作原理。 本节首先简要介绍了最终demo所使用的资源,然后介绍了如何逐步实现类似demo的场景。
All the resources for this tutorial can be found in the official demo repository on GitHub. I suggest you download it before starting. Alternatively, it can be downloaded from the Project Manager. Launch Godot and in the top bar select “Templates” and search for “2D Lights and Shadows Demo”.
场景布置
对于这个演示,我们使用四种纹理:两种用于灯光,一种用于那些会产生阴影的物品,一种用于背景。 如果想单独下载它们,我在这里列出了所有链接。
The first is the background image (background.png) used in the demo. You do not necessarily need a background, but we use one for the demo.
第二个是用作影子的纯黑色图片 (caster.png ) 。 对跳跃游戏,它可能是墙或任何其他投射阴影的物体。
接下来是灯本身 (light.png ) 。 如果单击该链接,您会注意到它有多大。 用于灯光的图像应覆盖您希望灯光覆盖的区域。 此图像为1024x1024像素,因此您应该使用它来覆盖游戏中的1024x1024像素。
最后,我们有聚光灯图像 (spot.png ) 。 该演示使用灯泡来显示灯光的位置,使用较大的灯光图片来显示灯光对场景其余部分的影响。
节点
该演示使用了四个不同类型的节点:
CanvasModulate 用于使场景变暗。
Sprites 用于显示灯泡,背景和产生阴影的物体的纹理。
Light2Ds 用于点亮场景。 光通常的工作方式是在场景的其余部分添加选定的纹理以模拟光照。 但它可以以其他方式使用,例如屏蔽部分场景。
LightOccluder2Ds 用于告诉着色器, 场景的哪些部分投射阴影。 阴影仅出现在 Light2D 所覆盖的区域,它们的方向基于 Light 的中心。
灯光
Lights 覆盖各自纹理的整个范围。 它们使用Additive Blending将其纹理颜色添加到场景中。
Lights 有四个 模式
: add
, sub
, mix
, 和 mask
。
Add
将光纹理的颜色添加到场景中。 它会照亮灯光下的区域。
Sub
从场景中减去光的颜色。 它使灯光下的区域变暗。
Mix
混合了灯光的颜色和底层场景。 产生的亮度介于灯光颜色和下方颜色之间。
Mask
用于遮盖灯光覆盖的区域。 根据光的颜色隐藏或显示遮盖区域。
对于这个demo,灯光有两个组件, Light 本身(这是灯光的效果),和 Sprite 灯泡(一个显示光源位置的图片)。 子节点 Sprite 不是让 Light 工作的必要条件。
阴影
阴影是通过让 Light 与 LightOccluder2D 的范围相交来制作的。
默认情况下,阴影处于关闭状态。 要打开它们,请单击 Light 并在Shadows部分下面选中 启用
。
在演示中我们使用带有纹理的 Sprite 来制作“投射影子的物体”,但实际上您只需要几个 LightOccluder2Ds 。 LightOccluder2D 本身看起来像一个黑点,在这个演示中 Sprite 只是一个黑色方块。
渐进式教程
既然我们已经介绍了需用到的节点的基础知识,那就可以开始逐步实现demo中的场景了。
First add a Sprite and set its texture to the background image. For your game this can be any background you choose. For this style of shadow it is most likely to be a floor texture.
接下来创建三个 Light2D’s 并将它们的纹理设置为 light image 。 你可以在上面的栏目中更改颜色。 默认情况下,阴影关闭并且 mode
设置为 add
。 这意味着每个灯光都会将自己的颜色添加到下面的任何颜色中。
接下来为每个 Light 节点添加一个 Sprite 子节点,并将 Sprite’s 纹理设置为 blob image 。 他们都应该保持在 Light 节点的中心。blob是灯本身的图像,而 Light 显示灯光对场景的影响。 LightOccluder2D’s 会将光的位置视为 Light 节点的中心,这就是为什么我们希望blob位于父节点 Light 的中心。
注解
在撰写本文时,3.0是稳定版本。 3.1开发版本已经对动画系统的做了许多更改,因此此处不会介绍demo中的动画。 请参阅 2D动画功能简介 以了解更多信息。
现在场景看起来太亮了。 这是因为三个灯都在为场景添加颜色。 这就是演示场景使用 CanvasModulate 的原因。 CanvasModulate 将整个视区乘以特定颜色。
在场景中添加 CanvasModulate 并将其颜色设置为 rgb(70, 70, 70)
。 这将使场景足够暗,以清楚地看到灯光的效果。
现在,我们来添加会投射阴影的物体。
该演示使用了一个名为“casters”的 Node 来组织投射阴影的物体。 在场景中添加 Node2D 。 它会被用来把所有投射阴影的物体变成一个大组。 这样我们就可以同时显示和隐藏它们。
每个阴影投射器都由具有 LightOccluder2D 子节点的 Sprite 组成。 在演示中, Sprite 的纹理设置为 caster image 。 子节点 LightOccluder2D 是所有神奇的事情发生的地方。 在游戏中 Sprite 可能不仅仅是一个黑色的方块; 它可以是任何可以投射阴影的物体的图片:一面墙,一个宝箱或其他任何东西。
LightOccluder2Ds 告诉游戏Occluder(遮光物)是什么形状。Occluder是一个多边形的容器 OccluderPolygon2D 。 对于这个演示,由于我们的墙是正方形,我们将 Polygon
设置为正方形。 其他设置保持为默认。
对于第一个设置, Closed
可以是 on
或 off
。 闭合的多边形会遮挡来自所有方向的光线。 开放多边形仅遮挡来自一个方向的光线
Cull Mode
让您选择剔除的方向。 默认值为“已禁用”,这意味着无论灯光在哪一侧,遮挡物都会投射阴影。 另外两个设置 Clockwise
和 Counter-Clockwise
指的是多边形顶点的弯曲顺序。 弯曲顺序用于确定光线的哪一侧在多边形内。 只有朝外的光线才投下阴影。
为了说明它们的差异,这张图片里是一个相应的 LightOccluder2D 的 Closed
设置为 ``off``的:ref:OccluderPolygon2D <class_OccluderPolygon2D> 。 这是为了可以看到多边形的轮廓:
注解
Cull Mode
设置为 Disabled
。 全部三条轮廓线都投下阴影。
注解
Cull Mode
设置为 Clockwise
。 只有顶部和右侧线条投射阴影。
注解
Cull Mode
设置为 Counter-Clockwise
。 只有底部的轮廓线投下阴影。 如果 Closed
设置为 on
,左边会有一条额外的垂直轮廓线也会投射阴影。
当你添加 LightOccluder2Ds 时,阴影仍不会出现。 你需要回到 Light2Ds 并在Shadow部分下设置 Enable
为 on
。 这将打开带有硬边缘的阴影,如下图所示。
为了给出看起来更好的柔和边缘阴影,我们设置变量 filter
, filter smooth
和 gradient length
。 Godot支持 Percentage Closer Filtering (PCF) ,它在像素周围提取阴影贴图的多个样本,并模糊它们以创建平滑的阴影效果。 样本数越多,阴影看起来越平滑但运行速度越慢。 这就是为什么Godot默认提供3-13个样本并让你可以自己选择用几个样本。 该演示使用PCF7。
注解
这是使用演示中设置渲染的阴影。 gradient length
设置为 1.3
, filter smooth
设置为 11.1
, filter
设置为 PCF7
。
注解
filter
设置为 PCF13
。 注意这里阴影变宽了,这是因为样本之间的距离是依赖于变量 filter smooth
的。
为了使用filtering(过滤),你需要设置 filter smooth
变量。 这决定了样品之间的距离。 如果您想让柔软区域延伸很远,您可以增加 filter smooth
的大小。 然而,在低样品和大过滤平滑的情况下,你会看到样本之间形成的线条。
注解
filter smooth
设置为 30
。
演示中的不同 Light 节点使用不同的filter smooth值。 玩玩它, 看看你喜欢什么样的。
注解
filter smooth
设置为 0
。
最后谈谈变量 gradient length
。 对于一些平滑阴影,最好不要在对象上立即开始阴影,因为这会产生硬边。 Gradient length(渐变长度)变量让阴影刚开始的时候平滑渐变,以减少硬边缘的影响。
注解
gradient length
设置为 0
。
注解
gradient length
设置为 10
。
您需要稍微调整设置以找到适合你项目的设置。 没有对每个人都正确的解决方案,这就是Godot为什么提供如此多灵活性。 只要记住``filter`` 值越高, 阴影的计算成本也越高就行了。