Rerun 中的 SphericalHarmonics3Rgb:3D 高斯溅射视角相关颜色的球谐系数编码解析 📅 发布时间:2026/9/17 3:24:55 👁 浏览次数: Rerun 中的 SphericalHarmonics3Rgb3D 高斯溅射视角相关颜色的球谐系数编码解析【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun导读本文深入解析 Rerun 类型系统中用于 3D 高斯溅射3D Gaussian Splatting视角相关颜色编码的SphericalHarmonics3Rgb类型。你将掌握其 15 个半精度 RGB 球谐系数的排列顺序系数主序、度 1–3 的分段规则、它与 3DGS PLY 文件f_rest_*属性的差异及导入时的转置要求、DC 项度 0的分离表示、低阶数据零填充与高阶截断的数学依据并顺着源码链路理解从 PLY 导入、Arrow 序列化到 GPU 渲染的完整实现。一、类型概览什么是 SphericalHarmonics3RgbSphericalHarmonics3Rgb是 Rerun 中的一个encoding编码类型用于表示度数 1 到 3的 RGB 球谐系数共15 个半精度Float16RGB 三元组。它被 GaussianSplats3D archetype 用作视角相关颜色view-dependent color的载体每个高斯溅射除了有一个与视角无关的基础颜色外还可以携带一组球谐系数在渲染时根据视线方向重建随视角变化的颜色。需要特别注意的是Rerun 官方将该类型标记为unstable不稳定⚠️This type isunstableand may change significantly in a way that the data wont be backwards compatible.即该类型可能在未来发生不保证向后兼容的显著变化跨版本迁移数据时需要留意这一点。1.1 在类型体系中的位置Rerun 的类型体系包含三层archetype原型→ component组件→ encoding编码。SphericalHarmonics3Rgb位于最底层的 encoding 层定义文件为 crates/build/re_type_definitions/rerun/encodings/spherical_harmonics3rgb.def.rs并由re_types_builder自动生成各语言绑定。围绕它有两条主要链路组件层SphericalHarmonics3Rgb组件rerun.components.SphericalHarmonics3Rgb直接包装该 encodingArchetype 层GaussianSplats3D通过with_sh_coefficients()方法接收该组件作为可选的高阶球谐系数字段见 gaussian_splats3d.def.rs。1.2 官方定义原文Spherical harmonics coefficients of degrees 1 through 3 for RGB, as 15 half-precision RGB triples.The coefficients are stored coefficient-major:[c1.rgb, c2.rgb, …, c15.rgb].The 15 coefficientsc1…c15are ordered by ascending degreel 1, 2, 3, and within each degree by ascending orderm -l … l: degree 1 isc1…c3, degree 2 isc4…c8, and degree 3 isc9…c15.二、系数排列顺序系数主序与度结构理解该类型的关键是系数在内存中的排列顺序。15 个系数采用coefficient-major系数主序存储即一个系数的 RGB 三个通道紧挨在一起[c1.rgb, c2.rgb, c3.rgb, …, c15.rgb]其中每个cN.rgb是一个由 R、G、B 三个f16组成的半精度三元组。c1…c15的编号规则如下按度数degree升序排列l 1, 2, 3在同一度数内按阶数order升序排列m -l … l。由此得出各度数对应的系数区间度数 l系数编号系数个数对应 m 取值范围1c1 … c33-1, 0, 12c4 … c85-2, -1, 0, 1, 23c9 … c157-3, -2, -1, 0, 1, 2, 3因为每个度数包含的系数个数不同3、5、7Rerun不将不同度数暴露为独立的分组——数据就是一份扁平的 15 个三元组度数边界需要调用方自行掌握边界位于第 3 个和第 8 个三元组之后。2.1 Rust 中的底层表示在 Rust 绑定中该 encoding 被定义为#[repr(transparent)]的元组结构体spherical_harmonics3rgb.rs#[derive(Clone, Debug, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable, SizeBytes)] #[repr(transparent)] pub struct SphericalHarmonics3Rgb( /// Spherical harmonics coefficients of degrees 1 through 3, coefficient-major. pub [[half::f16; 3usize]; 15usize], );外层[T; 15]对应 15 个系数内层[f16; 3]对应每个系数的 RGB 三通道f16即半精度浮点Rust 侧使用halfcratebytemuck::Pod/Zeroable表明该类型的内存布局是纯数据、可零初始化这也让后续从 Arrow 缓冲区直接bytemuck::try_cast_slice零拷贝式反序列化成为可能。在扩展实现 spherical_harmonics3rgb_ext.rs 中还提供了两个关键成员impl SphericalHarmonics3Rgb { /// The number of coefficients of degrees 1 through 3, i.e. the number of RGB triples. pub const NUM_COEFFICIENTS: usize 15; /// Create from 15 RGB coefficient triples of f32, converting each to half-precision. #[inline] pub fn from_f32(coefficients: [[f32; 3]; Self::NUM_COEFFICIENTS]) - Self { Self(coefficients.map(|rgb| rgb.map(f16::from_f32))) } }其中NUM_COEFFICIENTS 15是全仓库统一引用的常量from_f32接收[[f32; 3]; 15]并逐元素降精度为f16方便用户用f32表达系数。文件头部还有一个编译期const _: () assert!(...)校验保证结构体字节大小严格等于15 × 3 × size_of::f16()防止布局漂移。三、与 3DGS PLY 文件的对应关系通道主序 vs 系数主序该类型的系数排列与3D Gaussian SplattingKerbl et al., 2023参考 INRIA 实现使用的 PLY 文件中的f_rest_*属性在系数顺序上一一对应但布局约定不同Rerun 的SphericalHarmonics3Rgb系数主序即[c1.rgb, c2.rgb, …, c15.rgb]每个系数的三个通道相邻3DGS PLY 文件通道主序channel-major即先存 R 通道的全部 15 个系数再存 G 通道的全部 15 个最后是 B 通道的全部 15 个[R1 R2 … R15 | G1 G2 … G15 | B1 B2 … B15]。因此在导入 PLY 时必须进行转置transpose。这条转换规则同时适用于度数的增补如果文件只包含较低度数如度 1每通道 3 个系数共 9 个f_rest_*属性则导入时需要零填充到 15 个三元组如果文件包含更高度数如度 4每通道 24 个系数则只保留前 15 个并按度 3 截断。3.1 导入器中的转置实现转置逻辑位于 gaussian_splats3d_ext.rs 的slot_of函数中。它把 PLY 的属性名解析为Splat.values中的目标槽位fn slot_of(name: str, num_sh_per_channel: usize) - Optionusize { if let Some(index) name.strip_prefix(prop::F_REST_PREFIX) { if num_sh_per_channel 0 { return None; } let index index.parse::usize().ok()?; let channel index / num_sh_per_channel; let coefficient index % num_sh_per_channel; (channel 3 coefficient NUM_SH_COEFFICIENTS) .then(|| slot::SH 3 * coefficient channel) } else if let Some(i) prop::F_DC.iter().position(|p| *p name) { Some(slot::F_DC i) } else if let Some(i) prop::SCALE.iter().position(|p| *p name) { Some(slot::SCALE i) } else if let Some(i) prop::ROT.iter().position(|p| *p name) { Some(slot::ROT i) } else { match name { prop::X Some(slot::X), prop::Y Some(slot::Y), prop::Z Some(slot::Z), prop::OPACITY Some(slot::OPACITY), _ None, } } }其中prop::F_REST_PREFIX f_rest_、slot::SH 14。对每个形如f_rest_k的属性由num_sh_per_channel文件每通道实际存储的系数个数由f_rest_*属性总数除以 3 得到反解出其所属通道channel k / num_sh_per_channel和系数序号coefficient k % num_sh_per_channel映射到系数主序的槽位slot::SH 3 * coefficient channel从而在解析阶段就完成了转置若coefficient 15更高度数该属性被丢弃——这正是截断到度 3的实现方式。源码注释明确指出这种一次性把属性名重命名为槽位索引的做法read_plan让每个顶点每次写入只做一次整数解析避免了在大文件数亿次调用上的字符串比较开销同时零填充由Splat::default()的全零初始化天然完成gaussian_splats3d_ext.rs。最终Splat::sh_coefficients()把槽位中的f32值转为f16三元组数组并包装成 encodinggaussian_splats3d_ext.rsfn sh_coefficients(self) - SphericalHarmonics3Rgb { let coefficients std::array::from_fn(|coefficient| { std::array::from_fn(|channel| { f16::from_f32(self.values[slot::SH 3 * coefficient channel]) }) }); crate::encodings::SphericalHarmonics3Rgb(coefficients).into() }3.2 测试验证转置与零填充仓库在 gaussian_splats3d_ext.rs 内嵌了针对性单元测试直接验证转置、零填充与截断行为sh_transposed_and_zero_padded构造一个度 1 的 PLY每通道 3 个系数通道主序[R1 R2 R3 | G1 G2 G3 | B1 B2 B3]值为1..9断言导入结果等于系数主序[[R1 G1 B1], [R2 G2 B2], [R3 G3 B3], [0,0,0], …]即[1,4,7, 2,5,8, 3,6,9, 0…]full_degree_3每通道 15 个系数的完整度 3 文件逐系数逐通道校验值channel * 15 coefficienthigher_degree_is_truncated度 4 文件每通道 24 个系数R0..24, G24..48, B48..72断言只保留前 15 个且仍按正确的跨通道步长读取不会被错误步长打乱ragged_f_rest_drops_shf_rest_*总数不是 3 的倍数时如 10 个通道布局不可知直接丢弃全部球谐系数。此外crates/data_flow/re_importer/tests/test_ply_importer.rs 提供了端到端测试用真实的cactus.ply139,410 个高斯走完文件 →ArchetypeImporter→ chunk全链路并断言sh_coefficients、scales、quaternions、colors每个组件都恰好有一个值对应每个高斯且该文件不会被误判为普通点云Points3D组件为空。四、DC 项度 0的分离表示SphericalHarmonics3Rgb不包含度 0DC直流项。度 0 项是与视角无关的基色在 Rerun 中单独表示为encodings.Rgba32颜色即GaussianSplats3D的colors组件。这样设计的好处是基础颜色可以作为普通 RGBA 直接消费而无需对每个高斯都做一次球谐求值。在 3DGS PLY 中DC 项对应f_dc_0 / f_dc_1 / f_dc_2三个属性每通道一个。导入器在Splat::color()中通过求值度 0 球谐得到 RGB// See http://en.wikipedia.org/wiki/Table_of_spherical_harmonics let sh_c0 0.5 * (1.0 / std::f32::consts::PI).sqrt(); let [r, g, b] std::array::from_fn::_, 3, _(|i| to_u8(0.5 sh_c0 * self.values[slot::F_DC i])); // The opacity is stored as a logit; convert it to alpha via the sigmoid. let a to_u8(1.0 / (1.0 (-self.values[slot::OPACITY]).exp())); Color::new((r, g, b, a))即color_channel 0.5 c0 × f_dc_channel其中c0 0.5 × sqrt(1/π)to_u8会 clamp 到[0,1]后量化到u8不透明度则对 PLY 中以 logit 存储的opacity施加 sigmoid。这也再次印证了 gaussian_splats3d.def.rs 中colors字段的文档说明RGB 部分是视角无关的基色即球谐的度 0 (DC) 项alpha 部分是高斯峰值不透明度。五、零填充与截断正交基下的数学性质文档给出了两条关于度数转换的重要结论其依据是球谐基的正交归一性低阶数据零填充当数据只包含较低度数如度 1 或 2的系数时应当把缺失的高阶系数填充为零。由于球谐基是正交归一的零填充后的 15 个系数所表示的函数与原函数完全相同——高阶系数为零意味着这些基函数对信号的贡献为零不会引入任何偏差。高阶数据按度边界截断反过来如果源数据包含更高阶如度 4系数则在度边界处第3个和第8个三元组之后截断尾部系数得到的是低一阶函数对原函数的最优最小二乘近似。这正是 3DGS PLY 导入器保留每通道前 15 个系数、丢弃更高度数这一行为见上文slot_of中coefficient NUM_SH_COEFFICIENTS的判断的数学依据。用大白话概括零填充是无损的截断是最优有损的。这也意味着同一份数据以不同度数存储/传输不会改变其语义只是精度与体积的权衡。六、Arrow 数据类型与内存布局SphericalHarmonics3Rgb对应的 Arrow 数据类型ArrowDataType为FixedSizeList(15 x non-null FixedSizeList(3 x non-null Float16))从 Rust 实现的 spherical_harmonics3rgb.rs 可以看到其构造impl ::re_types_core::ArrowDataType for SphericalHarmonics3Rgb { #[inline] fn arrow_data_type() - arrow::datatypes::DataType { DataType::FixedSizeList( Arc::new(Field::new( item, DataType::FixedSizeList( Arc::new(Field::new(item, DataType::Float16, false)), 3, ), false, )), 15, ) } }外层FixedSizeList(15)15 个球谐系数内层FixedSizeList(3)每个系数的 R、G、B 三个通道最底层Float16半精度浮点非空non-null。to_arrow序列化时会把[[f16; 3]; 15]展平写入连续的Float16Array缓冲区期间内层三元组也被展平最终得到 45 个f16值from_arrow反序列化则通过bytemuck::try_cast_slice直接从缓冲区切片强转并校验外层value_length() 15、内层value_length() 3不匹配即返回datatype_mismatch错误。固定大小的嵌套列表保证了每个值严格占用45 × 2 90字节利于批量上传与 GPU 端对齐。七、组件包装与在 GaussianSplats3D 中的用法7.1 组件层包装组件 spherical_harmonics3rgb.rs 是对 encoding 的透明包装#[repr(transparent)] pub struct SphericalHarmonics3Rgb(pub crate::encodings::SphericalHarmonics3Rgb); impl ::re_types_core::WrapperComponent for SphericalHarmonics3Rgb { type Encoding crate::encodings::SphericalHarmonics3Rgb; #[inline] fn name() - ComponentType { rerun.components.SphericalHarmonics3Rgb.into() } ... }其语义为视角相关颜色以度数 1 到 3 的球谐系数表示视角无关度 0的基础颜色由独立的components::Color表示。组件与 encoding 之间通过Deref/From无缝互转用户在 SDK 中几乎感觉不到这层隔阂。7.2 Archetype 中的字段与用法GaussianSplats3Darchetype定义见 gaussian_splats3d.def.rs生成代码见 gaussian_splats3d.rs共有 6 个字段其中与本文直接相关的是两个可选组件字段组件说明sh_coefficients可选SphericalHarmonics3Rgb高阶球谐系数度 1–3用于视角相关颜色spherical_harmonics_degree可选SphericalHarmonicsDegree渲染时评估的最高度数0–3未设置时默认为 3Rust 官方示例gaussian_splats3d.rs展示了如何用 15 个系数直接构造并记录高斯溅射rec.log( gaussians, rerun::GaussianSplats3D::new([ (0.0, 0.0, 0.0), (2.0, 0.0, 0.0), (4.0, 0.0, 0.0), ]) .with_scales([(1.0, 0.5, 0.25), (0.5, 1.0, 0.5), (0.25, 0.5, 1.0)]) .with_quaternions([ rerun::Quaternion::IDENTITY, rerun::Quaternion::from_xyzw([0.0, 0.0, 0.382683, 0.923880]), // 45 degrees around Z rerun::Quaternion::IDENTITY, ]) .with_colors([ rerun::Color::from_unmultiplied_rgba(255, 0, 0, 128), rerun::Color::from_unmultiplied_rgba(0, 255, 0, 200), rerun::Color::from_unmultiplied_rgba(0, 0, 255, 255), ]) // 15 view-dependent RGB coefficients (degrees 1-3) per splat, coefficient-major: .with_sh_coefficients([ [[0.5, 0.0, 0.0]; 15], [[0.0, 0.5, 0.0]; 15], [[0.0, 0.0, 0.5]; 15], ]), )?;注意with_sh_coefficients接受每高斯 15 个[f32; 3]三元组排列方式正是前文介绍的系数主序。除了手工构造更常见的做法是直接导入 3DGS 训练产出的 PLY 文件。同文件内的第二个示例使用RecordingStream::log_file_from_path一条语句完成整个导入内部走的就是上文分析的 PLY 导入器自动完成转置、零填充/截断、DC 项与 opacity 转换// No entity-path prefix, and log the data as temporal (not static): rec.log_file_from_path(path, None, false)?;7.3 渲染时的度数控制SphericalHarmonicsDegree组件见 spherical_harmonics_degree.rs控制渲染端实际评估的度数取值范围 0–3默认 30renders the view-independent base color only, and is the fastest. Each higher degree brings in more view-dependent detail, at the cost of fetching and evaluating more coefficients: 3 of them for degree 1, 8 for degree 2, and all 15 for degree 3. Lowering this in the blueprint can make the rendering a lot faster.即度 0 只渲染基色最快度 1 需要 3 个系数度 2 需要 8 个度 3 需要全部 15 个。在 blueprint 中降低该值可以显著提升渲染速度——这与本文第五节的截断 最优近似性质一脉相承也是性能与画质之间可随时调节的旋钮。八、渲染链路从组件到 GPU 纹理在渲染端re_view_spatial/src/visualizers/gaussian_splats3d.rs 负责把SphericalHarmonics3Rgb组件数据打包进渲染器。其中的关键一步是把每个 RGB 系数三元组扩展为 RGBA使其可以直接 memcpy 进 GPU 端的半精度 SH 纹理Rgba16Float// Widen each RGB coefficient to RGBA so the renderer can memcpy it into its texture. let sh_coefficients: Vec[GaussianShCoefficient; 15] { re_tracing::profile_scope_if!(100_000 num_instances, sh_coefficients); data.sh_coefficients .iter() .map(|sh| std::array::from_fn(|i| GaussianShCoefficient::from_rgb(sh.0.0[i]))) .collect() };由此可见从 SDK 侧的[[f16; 3]; 15]、到 Arrow 的嵌套FixedSizeList、再到渲染器的[GaussianShCoefficient; 15]15 个系数 系数主序的约定贯穿了导入 → 存储 → 渲染整条流水线。若某一环违反该约定例如按 PLY 的通道主序直接上传视角相关颜色就会完全错乱——这也是为何文档与导入器都如此强调必须转置。九、实践要点小结排列约定始终以系数主序提供 15 个[R,G,B]三元组度 1 前 3 个、度 2 第 4–8 个、度 3 第 9–15 个。DC 项分离度 0 基色不放进SphericalHarmonics3Rgb请使用colorsRGBA组件。导入 PLY优先使用log_file_from_path或GaussianSplats3D::from_ply_file_contents导入器会自动完成通道主序 → 系数主序的转置以及对低阶文件的零填充、对高阶文件的截断。性能调节通过spherical_harmonics_degree0–3控制渲染评估度数度越低越快未设置时默认取全部系数度 3。类型稳定性该类型标记为 unstable升级 Rerun 版本时注意数据兼容性。延伸阅读如需了解与之配套的类型可继续阅读 SphericalHarmonics3Rgb 组件文档、SphericalHarmonicsDegree 组件文档以及 GaussianSplats3D archetype 文档PLY 导入的完整实现与测试位于 gaussian_splats3d_ext.rs 和 test_ply_importer.rs。【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考