Slate 裁剪系统

虚幻引擎 4 中 Slate 裁剪系统的总览。

Windows
MacOS
Linux
See Also

虚幻引擎 4.17 版本发布后,全新的 Slate 裁剪系统打破了对 4.16 及更老版本的反向兼容。请参考此页面的 转换指南 部分更新项目。

虚幻引擎 4(UE4)的 Slate 裁剪系统可将图像或文本限制到画面的一个区域中。其实现方式是围绕创建的物体和面板创建一个裁剪矩形(或称裁剪 rect)。编辑器和 UMG UI 设计器工具均使用该系统。用户可根据需求利用 Slate 在编辑器中移动面板并调整其大小,裁剪系统则将文字和图像限制在裁剪 rect 中。结合 UMG 可实现更佳的渲染变形,对 UI 进行适当的裁剪。

全新内容

虚幻引擎 4.17 版本对裁剪系统进行了极大修改,比旧版本更灵活。这也创造了给更多可能,使 UMG 之类的工具系统能够添加更多渲染效果,用于项目的 UI 中。旧版本中的裁剪系统功能有限,布局空间仅为轴对齐,会导致难以渲染变形(常围绕边缘),如下例所示:

OldClippingIssue1.png

OldClippingIssue2.png

在上图中,控件已旋转,但裁剪 rect(白点)却并未旋转。这样一来,即使图像位于 Canvas 面板边界中,也无法被渲染。

以下列表说明了裁剪系统 1.0 和 2.0 版本之间的区别:

Clipping 1.0(4.16 及更旧版本)

Clipping 2.0(4.17 及更新版本)

  • 轴对齐裁剪 - 只在布局空间中发挥作用

  • 每个控件均被裁剪

  • 所有裁剪均在像素着色器中完成(要求每个顶点六个浮点)

  • 允许对多个裁剪 rect 进行批处理,因为这些 rect 是批数据的一部分

  • 渲染变形较为"特殊",因为它们允许在父项所提供的裁剪 rect 之外进行渲染,而布局 rect 被裁剪掉后会存在不良后果

  • 任意四边形的裁剪

    • 使用 Scissor Rects 进行裁剪的轴对齐裁剪区,可避免不必要的像素着色器运算

    • 复杂的裁剪区使用模板缓存将裁剪区的任意堆叠组成模板缓存,然后用于裁剪绘制调用

  • 只有少数几个控件会(默认)进行裁剪,主要是滚动面板和可编辑文本域

    • 渲染变形不再"特殊",只要变形不进行裁剪,便允许用户在另一个控件之外进行渲染。

  • Slate 无法对裁剪区进行批处理。这使得 Slate 顶点格式中不要求 6 个浮点,不再需要在像素着色器中进行裁剪

新系统支持在控件的渲染空间中进行裁剪,不需要轴对齐;使用系统时不再需要担心裁剪问题(左图),而是会直接裁剪掉最外侧的控件。新版本中,应用到内部控件的变形将在边缘被正确裁剪(右图)。

ClippingOld.png

ClippingNew.png

4.16 与更旧版本中的裁剪和变形。

在 4.17 与更新版本中应用到边缘上的裁剪

以及子项的变形。

多数情况下您都无需担心控件裁剪状态的变化。以游戏 UI 为例,您无需调整 UMG 中的控件,而多数情况下都需要对其进行修改,如滚动面板或编辑器,在这些区域中用户无法控制文本长度,需要对其进行裁剪,调整窗口或面板某个部分的大小。

ClippingExamples.png

此处的几个例子说明了不同裁剪设置的效果:

  • 左图 - 按键或文本上未启用裁剪。

  • 中图 - 文本上已启用裁剪。

  • 右图 - 按键上已启用裁剪。

请参考裁剪应用到不同对象上时的变化。因为系统裁剪到边界,所以填补之类的因素此处无需考虑。如果按键启用裁剪(右图),那么内容将在切断发生之前向边缘移动。如果文本负责处理裁剪(中图),那么文本将基于按键内容填补之类进行裁剪,缩减文本填入的空间。

除简单的轴对齐裁剪外,系统还支持相互堆叠的任意裁剪四边形。

TransformClipping.png

举一个较为极端的例子,组合多个任意矩形是使用 3D 变形和投射的先决条件,因为被投射裁剪的控件需要在投射空间中执行裁剪。

从 4.16 及更旧版本转换到 4.17 及更新版本的指南

4.17 版本中对 Slate 裁剪系统进行的修改会破坏对旧版本的兼容性,因此您需要根据以下部分的说明来更新项目,避免出现转换问题,同时了解在项目中对控件进行调试的新方法。

启用控件裁剪

对于所有 UMG 控件而言,用户可以调整所选控件 Details 面板中的 Clipping 属性。

UMGClippingProperty.png

您可在此了解关于 用 UMG UI 设计器进行裁剪 的更多内容。

如需在代码中启用裁剪,需要将 EWidgetClipping 的裁剪属性设为以下其中一个状态:

  • Inherit

  • ClipToBounds

  • ClipToBoundsWithoutIntersecting

  • ClipToBoundsAlways

  • OnDemand

请参考以下代码范例:

SNew( SBorder )
.Clipping(EWidgetClipping::ClipToBounds)
[
    ...
]

弃用的 API

以下 API 在 4.16 之后的版本中弃用。如果您的项目曾使用这些 API,则需要考虑现在是否仍然需要它们。并非每个控件都执行裁剪,因此您也许不再需要那些用于移动裁剪区的代码。

  • FSlateDrawElement::Make___(...) - 无需再将裁剪 rect 传入每个绘制调用,因此只需要移除函数调用即可自动使用新版本。

  • SScissorRectBox - 这已不再需要,因为每个轴对齐的裁剪 rect 现在均是 scissor rect。使其最直接的子控件启用裁剪,将这些内容删除并替换之前进行的任务。

自定义裁剪

在一些情况下您可能需要在控件内部进行裁剪。举例而言,SProgressBar 可能需要基于进度条的进度对任意位置的进度条绘制进行裁剪。添加裁剪需要在 OnPaint 中执行以下操作:

OutDrawElements.PushClip(FSlateClippingZone(AllottedGeometry));

//TODO 在此进行绘制,否则将调用子控件绘制。

OutDrawElements.PopClip();

FSlateClippingZone 是窗口空间中的任意裁剪区域,可使用数种方法开启,对已有代码进行直接转换。

如果还需要自定义裁剪影响 Hit Testing,则需要进行以下设置,将裁剪区推到 Hit Test Grid 上:

OutDrawElements.PushClip(FSlateClippingZone(AllottedGeometry));
Args.GetGrid().PushClip(FSlateClippingZone(GetCachedGeometry()));

//TODO 在此进行绘制,否则将调用子控件绘制。

OutDrawElements.PopClip();
Args.GetGrid().PopClip();

命中测试网格的裁剪 rect 使用缓存的几何体,而并非 AllotedGeometry。在 OnPaint 中,AllotedGeometry 处于窗口空间中,而 Hit Test Grid 处于桌面空间中,因此必须使用 Tick 中获得的几何体。

包裹裁剪状态

在一些情况下,拥有裁剪状态的控件不负责进行裁剪。例如 SScrollBox 能让用户公开修改和其他控件相似的裁剪状态,然而当 SScrollBox 构造后,它会把 bClippingProxy 设为 true。因此 Slate 渲染此控件时将忽略其裁剪状态。

在内部,SScrollBox 告知另一个嵌套控件其需要进行裁剪或执行被告知的裁剪状态。此外,用户修改裁剪状态时,SScrollBox 将覆盖名为 OnClippingChangedSWidget 函数,使其了解合适将新的裁剪状态镜像到嵌套私有控件上。

剔除修改

虽然裁剪在渲染空间中执行,引擎仍然使用简单的边界框执行剔除。然而该框是基于渲染变形裁剪区的边界框。此外,裁剪和剔除可能随时间出现更多细微差别。如果您有一个自定义面板,执行 MyClippingRect.IntersectionWith 识别并剔除无法被绘制的控件,则需要使用 SWidget 函数 IsChildWidgetCulled。请参考下例:

for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex)
{
    FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex];

    if (!IsChildWidgetCulled(MyCullingRect, CurWidget))
    {
        // 绘制此控件。
    }
}

MyClippingRect 纳入 OnPaint 调用之处都需要被重命名为 MyCullingRect

虽然裁剪系统有所修改,但 Slate 的剔除方法仍未改变。您需要注意,如果下例中的父控件(蓝色)在裁剪区(绿色)外被剔除,则子控件(黄色)也将被剔除。即使其拥有变形,能够在父项边界外进行整体渲染,也同样如此。裁剪属性 Clip to Bounds - Without Intersecting 启用时情况也是如此,因为标记只会被直接的父控件所勾选。

Widgets viewed outside | the clipping zone.

Widgets culled outside | the clipping zone.

调试裁剪

Widget Reflector 在编辑器中显示所选控件的裁剪状态。

WidgetReflector.png

使用 Widget Reflector 选中一个文本块,说明裁剪状态被设为 On Demand。

打开 Widget Reflector 的方法是:前往 File 菜单,选择 Window > Developer Tools > Widget Reflector

以下控制台变量可用于调试编辑器中任意控件的裁剪和剔除状态:

控制台变量

描述

Slate.ShowClipping

启用后,这将显示所有轴对齐裁剪 rect(Scissor Rects)的黄色轮廓,以及全部模板裁剪四边形的红色轮廓。

点击图片查看全图。

Slate.DebugCulling

启用后,这将有助于您更好地理解剔除在面板中的工作原理,以及其未能正常工作的原因。它在 GPU 上禁用裁剪,但一切均照常进行,因此您能够看到所有内容在裁剪区边界外渲染,并确定它们是否根据需求被剔除。

Slate.EnableDrawEvents

启用后,绘制事件将被启用。对 RenderDoc 或相似内容进行调试时便更容易理解批处理或裁剪状态转换。使用调试版本时此变量默认启用。

Select Skin
Light
Dark

Welcome to the new Unreal Engine 4 Documentation site!

We're working on lots of new features including a feedback system so you can tell us how we are doing. It's not quite ready for use in the wild yet, so head over to the Documentation Feedback forum to tell us about this page or call out any issues you are encountering in the meantime.

We'll be sure to let you know when the new system is up and running.

Post Feedback