Rerun 2D 图层叠放控制指南:DrawOrder 组件的语义、编码与渲染原理
2026/9/17 0:40:58 网站建设 项目流程

Rerun 2D 图层叠放控制指南:DrawOrder 组件的语义、编码与渲染原理

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

导读

在 Rerun 的二维视图中(如Spatial2DView),多个实体常常会互相遮挡:标注框要盖在图像上、点云要浮在底图上、分割结果要与原图叠加。DrawOrder组件就是 Rerun 为这类 2D 场景提供的"层叠顺序"控制机制——值越大,绘制得越靠上。本文以 Rerun 官方类型文档 draw_order.md 为主体,结合仓库中的类型定义、渲染上下文系统与测试用例,系统讲解该组件的语义约定、数据编码、三语言 SDK 用法以及从DrawOrder到最终深度偏移(DepthOffset)的底层实现原理,帮助你精确编排 2D 可视化中的图层关系。

核心语义:数值越大越靠上层

DrawOrder的官方定义非常明确:

2D 元素的绘制顺序。值越高的元素绘制在值越低的元素之上(Draw order of 2D elements. Higher values are drawn on top of lower values.)

在 crates/build/re_type_definitions/rerun/components/draw_order.def.rs 的类型定义文件中,这一段语义被原样保留,并额外标注了三条重要的使用约束:

  1. 一个实体只能有一个 draw order 组件(An entity can have only a single draw order component);
  2. 实体内部(即同一实体内多个可视化层之间)的绘制顺序由组件顺序决定(Within an entity draw order is governed by the order of the components);
  3. 拥有相同 draw order 值的实体之间,先后顺序一般是未定义的(Draw order for entities with the same draw order is generally undefined)。

这三点意味着:DrawOrder适合用来做实体之间(不同 Entity Path 之间)的明确分层;如果希望精确控制同层实体的遮挡关系,需要为它们分配不同的数值,而不能依赖相等值时的内部顺序。

类型定义与数据编码

Rerun 编码:Float32

DrawOrder在 Rerun 侧的数据编码为单精度 32 位 IEEE 754 浮点数(Float32),即一个普通的f32标量。在 Arrow 序列化层面,其 datatype 同样直接映射为:

Float32

三语言绑定:同一份定义,三种生成产物

该组件不是手写代码,而是由re_types_builderdraw_order.def.rs这一个"类型定义源"自动生成 Rust、Python 与 C++ 三套绑定(def 文件头部注释明确说明:"It is parsed byre_types_builderto generate the Rust, Python and C++ bindings")。

  • Rust:在 crates/store/re_sdk_types/src/components/draw_order.rs 中,DrawOrder被实现为一个#[repr(transparent)]的元组结构体pub struct DrawOrder(pub crate::encodings::Float32),组件类型名为"rerun.components.DrawOrder"。它实现了Deref/DerefMut(可直接当作Float32使用)以及From<T: Into<Float32>>,因此可以用任意可转换为Float32的值直接构造。
  • Python:在 rerun_py/rerun_sdk/rerun/components/draw_order.py 中,DrawOrder直接继承自encodings.Float32,并组合ComponentMixin;类型定义中的#[python(aliases = "float")]#[python(array_aliases = "float | npt.NDArray[np.float32]")]注解(见 draw_order.def.rs)意味着 Python 侧既可以传普通float,也可以传numpyfloat32数组。
  • C++:同样由该 def 文件生成rerun::components::DrawOrder,用法与 Rust 侧一致,通过构造器传入一个浮点值。

另外,def 文件中标注了#[rerun(state = "stable")],说明DrawOrder属于稳定 API,可以放心在长期项目中依赖。

支持 DrawOrder 的 2D 原型(Archetype)全览

根据原文档的 "Used by" 列表,共有 13 个 archetype 支持DrawOrder(在各自文档中均作为可选字段draw_order出现,例如 Image、Points2D、Boxes2D):

Archetype说明典型叠加场景
Image单色/彩色图像作为底图,值最小
EncodedImage压缩图像(JPEG/PNG)作为底图或覆盖层
DepthImage/EncodedDepthImage深度图像与其他 2D 标注叠加
SegmentationImage分割图像半透明叠加在底图上
GridMap栅格地图数据地图与路径/点位叠加
Points2D2D 点云标注点盖在图像之上
Boxes2D2D 矩形框检测框盖在图像/点上
LineStrips2D2D 折线轨迹线覆盖底图
Ellipses2D2D 椭圆高亮/区域标注
Arrows2D2D 箭头方向标注(如光流)
VideoFrameReference/VideoStream视频帧引用与视频流视频画面上叠加标注

需要注意的是,DrawOrder只对2D 元素生效(文档措辞为 "Draw order of 2D elements")。虽然在Spatial3DView中这些 2D archetype 若挂载在投影(projection)下也可以显示(见各 archetype 文档的 "Can be shown in"),但层叠优先级主要由DepthOffset机制换算处理,详见下文。

实战:在 SDK 中设置 draw_order

Python 示例

在 Python SDK 中,draw_order是各 archetype 构造器的关键字参数。以 Points2DExt 为例,其参数文档明确写着:"An optional floating point value that specifies the 2D drawing order. Objects with higher values are drawn on top of those with lower values."(可选的浮点值,用于指定 2D 绘制顺序;值越高的对象绘制在值越低的对象之上。)

import rerun as rr rr.init("draw_order_demo", spawn=True) # 1) 底层:一张底图,draw_order = 0.0 rr.log("scene/background", rr.Image(..., draw_order=0.0)) # 2) 中层:检测框,draw_order = 10.0,盖在底图之上 rr.log( "scene/detections", rr.Boxes2D( centers=[(320.0, 240.0), (100.0, 100.0)], half_sizes=[(120.0, 60.0), (40.0, 40.0)], draw_order=10.0, # 比底图大即可 ), ) # 3) 顶层:关键点,draw_order = 20.0,盖在框之上 rr.log( "scene/keypoints", rr.Points2D( positions=[(320.0, 240.0)], colors=[(255, 0, 0)], draw_order=20.0, ), )

由于DrawOrderFloat32浮点标量,取值可以任意(正负、小数均可),只要相互之间有大小区分即可;为便于维护,建议按 10 的倍数预留档位,方便日后插入新图层。

Rust 示例

Rust 侧使用.with_draw_order(...)构造器方法,这与仓库中的官方测试写法完全一致(见下文测试章节)。例如:

use re_sdk_types::archetypes::Image; let image = Image::from_color_model_and_tensor( re_sdk_types::encodings::ColorModel::RGB, tensor_data, )?; // 指定该实体绘制在 draw_order 更大的实体之下 let image = image.with_draw_order(0.0);

渲染原理:从 DrawOrder 到 DepthOffset

DrawOrder并不会直接作为渲染参数,而是由空间视图(re_view_spatial)中的视图上下文系统EntityDepthOffsets换算为渲染器可用的深度偏移(re_renderer::DepthOffset)。其核心实现在 crates/views/re_view_spatial/src/contexts/depth_offsets.rs:

  1. 收集collect_draw_order_per_visualizer遍历所有"处理 draw order 的可视化器"(visualizers_processing_draw_order()),对每个实体的可视化指令执行latest_at查询,读取DrawOrder组件;若实体未显式设置,则通过typed_fallback_for惰性计算一个默认值(determine_default_draworder,位于同文件)。
  2. 排序:所有(可视化器, 实体路径哈希)对按DrawOrder值放入BTreeMap(天然按值升序),每个值对应一个BTreeSet的实体集合。
  3. 换算:为保证深度偏移尽量紧贴 0,起始偏移被设为-(实体总数 / 2),然后为每个实体分配一个连续递增的DepthOffset

源码中有两处值得注意的实现细节(可直接从 depth_offsets.rs 确认):

  • 相同 DrawOrder 也会被拆开:注释明确写道 "We give objects with the sameDrawOrderstill a different depth offset in order to avoid z-fighting artifacts when rendering in 3D. (for pure 2D this isn't necessary)"——即为了避免 3D 渲染下的 z-fighting(深度冲突)伪影,相同DrawOrder的实体仍会被分配不同但相邻的深度偏移。这与文档中"相同 draw order 的实体顺序一般未定义"的语义并不冲突:偏移不同是为了消除伪影,而非承诺绘制先后。
  • 默认值兜底:未设置draw_order的实体也会走同一套机制获得默认层级,因此新旧数据混用时不会出现层级断裂。

官方测试用例:一份可复现的"分层示范"

仓库在 crates/views/re_view_spatial/tests/draw_order.rs 中提供了test_draw_order集成测试,用 8 个实体直观演示了完整的层叠效果(每个实体都在2d_layering前缀下):

实体路径Archetypedraw_order视觉效果
2d_layering/backgroundImage(512×256 灰色)0.0最底层背景
2d_layering/middle_gradientImage(256×256 渐变)1.0中间层
2d_layering/middle_blueImage(192×192 蓝色)1.1略高于渐变层
2d_layering/arrow2d_betweenArrows2D1.12夹在中间层之间
2d_layering/lines_behind_rectLineStrips2D1.25在矩形之下
2d_layering/rect_between_top_and_middleBoxes2D1.5中上层
2d_layering/points_between_top_and_middlePoints2D1.51紧贴矩形之上
2d_layering/topImage(128×128 白色)2.0最顶层

该测试通过TestContext将上述实体依次写入SpatialView2D,并生成名为draw_order的渲染快照。它同时印证了两点:一是with_draw_order是 Rust API 的标准链式方法(各 archetype 均可用);二是小数粒度完全受支持(如1.11.121.51),可以在相邻图层之间精确插入新层,这正是Float32编码带来的灵活性。

注意事项与最佳实践

综合原文档语义与源码实现,在实际使用DrawOrder时建议遵循以下规则:

  1. 跨实体分层靠 DrawOrder,实体内分层靠组件顺序:一个实体只能携带一个DrawOrder值;若需要在一个实体路径下表达多层,应通过组件排列顺序控制,或拆分为多个实体。
  2. 避免依赖相等值:相同DrawOrder的实体之间先后顺序"一般未定义";虽然在 3D 投影场景下渲染器会为它们分配不同的深度偏移以避免 z-fighting,但这不能作为可靠的分层依据。需要明确遮挡关系时,请使用不同的值。
  3. 数值只论大小、不论绝对值:任意浮点值均可(正、负、小数),系统内部会自动将收集到的值映射为紧贴 0 的连续深度偏移,因此无需刻意从 0 开始计数。
  4. 未设置时自动兜底:未显式指定draw_order的实体会获得默认层级(由typed_fallback_for提供),与显式设置的值混用是安全的。
  5. 仅作用于 2D 元素DrawOrder的语义限定在 2D 元素(图像、点、框、线、箭头等)的叠放;三维物体的遮挡由常规深度测试决定,不受该组件控制。

延伸阅读

  • 组件类型定义:draw_order.def.rs
  • Rust 生成绑定:crates/store/re_sdk_types/src/components/draw_order.rs
  • Python 生成绑定:rerun_py/rerun_sdk/rerun/components/draw_order.py
  • 渲染换算实现:depth_offsets.rs
  • 层叠效果集成测试:tests/draw_order.rs
  • 编码类型说明:Float32
  • 相关 archetype:以 Points2D、Boxes2D、Image 为代表,其余见上文表格。

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询