2D 灯光和阴影

前言

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

../../_images/light_shadow_main.png

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

场景布置

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

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

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

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

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

节点

该演示使用了四个不同类型的节点:

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 来制作 “投射影子的物体”, 但实际上您只需要几个 LightOccluder2Ds . LightOccluder2D 本身看起来像一个黑点, 在这个演示中 Sprite 只是一个黑色方块.

渐进式教程

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

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

../../_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

备注

这里将不涉及演示中的动画。有关创建动画的信息,请参见 动画功能介绍

现在场景看起来太亮了. 这是因为三个灯都在为场景添加颜色. 这就是演示场景使用 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 设置为 offOccluderPolygon2D . 这是为了可以看到多边形的轮廓:

../../_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

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

../../_images/light_shadow_normal.png

备注

这是使用演示中设置渲染的阴影. gradient length 设置为 1.3 , filter smooth 设置成 11.1 , filter 设置为 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 值越高, 阴影的计算成本也越高就行了.