上个月我把一组老建筑的照片丢进 3D Gaussian Splatting 管线里跑重建模型出来以后远看效果不错镜头一拉近窗棂边缘就开始糊细看还有色彩漂移。这种问题玩 3DGS 的人应该都遇见过很多人第一反应是加迭代次数、调密度阈值但 SuperGaussians 这篇论文给出的答案不太一样瓶颈很可能出在每个高斯原语的颜色表达能力上。它把原本绑定在高斯中心、只随视角变化的球谐颜色改造成可以在空间上连续变化的颜色函数让一颗高斯也能承载纹理信息。这篇文章我会把论文的核心动机、四种空间变化颜色Spatially Varying Colors实现、光栅化器改动、训练稳定性设计以及复现时值得注意的工程细节一次讲清楚适合已经跑通过 3DGS、现在想深入理解渲染管线或者动手改代码的读者。1. 3DGS 的颜色表示为什么是性能瓶颈1.1 一颗高斯球48 个浮点数SH 在 3DGS 里到底怎么工作要理解 SuperGaussians 做了什么先要搞明白传统 3DGS 里的颜色是怎么表示的。一个 3D 高斯原语通常包含位置、旋转矩阵、缩放向量、不透明度还有一组球谐系数。球谐函数的思路是每一个高斯椭球在空间中占据一定体积这个体积从不同方向看过去应该呈现不同的颜色——比如高光、镜面反射、视角相关的颜色变化——所以用一个定义在球面上的函数来描述它的外观。原版 3DGS 默认的球谐阶数一般是 degree3也就是每个颜色通道用 16 个系数来表达方向相关的颜色变化RGB 三通道加起来就是 48 个浮点数。光栅化的时候GPU 拿到每个像素对应的视线方向把方向输入球谐基函数和这 48 个系数做一次求值就得到了该视角下的颜色再和不透明度、深度一起做 alpha blending得到最终像素。这个思路在最初 3DGS 论文里是合理的因为球谐函数确实能表达低频到中频的视角变化而且求值开销很低非常适合 GPU 并行光栅化。但问题恰恰出在“全局绑定”上这 48 个系数描述的是这一整颗高斯椭球在所有视角下的外观而不是椭球内部不同位置的外观。1.2 SH 颜色的“空间盲区”为什么纹理密集区域总是糊如果你把一个高斯椭球想象成一块橡皮泥球谐系数描述的是“从各个方向看这块橡皮泥分别是什么颜色”但它不能描述“橡皮泥左半边是红色、右半边是蓝色”——因为所有系数都是全局变量没有空间坐标的输入。而真实世界的表面尤其是建筑立面、树木草丛、布料纹理颜色几乎总是随表面位置变化的。栅栏、百叶窗、树叶、墙面砖缝这类高频纹理恰恰是 3DGS 最容易糊掉的地方。原因就在这里单颗高斯只能提供一个平均颜色如果场景细节比单个高斯的覆盖范围更细就只能靠无数颗高斯密集堆叠硬凑。所以你会看到很多 3DGS 重建结果里几何结构已经成型了但纹理区域高斯数量爆炸显存占用居高不下训练时间也大幅拉长。这其实可以理解成一个“表达效率”问题不是场景几何真的复杂到需要那么多原语而是每个原语的颜色维度太低导致单个原语没有能力覆盖一个稍大面积的纹理块。SuperGaussians 的作者正是从这个角度切入把颜色函数从“只依赖视角方向”扩展成“同时依赖局部空间坐标和视角方向”。1.3“不够用”的真正代价场景里几百万颗高斯的由来传统 3DGS 在纹理复杂场景里动辄生成上百万甚至上千万颗高斯很多人把这归结于密度化策略调得不好。但我看下来更本质的原因是颜色表达能力不够密度化被迫替颜色模块“背锅”。当一颗高斯无法用一个颜色覆盖它所占据的区域优化器就只能把它拆成更多更小的高斯让每颗高斯覆盖更小的面积从而“近似”出空间颜色变化。代价也是连锁的原语变多后排序和光栅化的开销都跟着涨更重要的是大量小高斯之间缺乏约束会出现飞点、重叠伪影这类问题。所以如果你只在密度化阈值上做文章往往治标不治本。SuperGaussians 的做法是直接提高单个原语的颜色维度让它们在更大空间范围内保持颜色表达能力从而在相同视觉质量下明显减少所需的高斯数量。下面这个表格能比较直观地看出传统原语和 SuperGaussians 原语的差异属性传统 3DGS 高斯原语SuperGaussians 原语颜色输入仅视角方向局部空间坐标 视角方向空间位置敏感否是纹理表达方式靠大量高斯密集堆叠单个原语内部颜色连续变化参数形式固定维度 SH 系数可选的 SGC 实现CLIP 映射/网格/MLP/soft voxel相同质量下原语数量高通常明显降低2. SuperGaussians 如何给高斯内部引入空间变化颜色2.1 核心思路颜色函数从“中心方向”变成“局部坐标方向”SuperGaussians 的核心修改其实很直接在每个高斯的局部坐标系里定义一个空间变化的颜色函数。正常渲染流程中某颗高斯覆盖屏幕上的若干像素对于其中的任意一个像素光栅化器除了要知道视线方向还需要知道这个像素在这颗高斯局部坐标系里的位置坐标。有了这个局部坐标颜色函数就能给出一个属于该位置的颜色。这个局部坐标怎么来一个自然的做法是借助重心坐标。光栅化时每个高斯通常用一个屏幕空间包围盒或者三角片来近似覆盖区域三角形的三个顶点经过坐标变换后落在高斯局部坐标系的已知位置。于是任意像素的重心坐标可以把屏幕坐标映射到局部坐标系里得到类似“这颗高斯内部的位置”信息。这样一来同一颗高斯覆盖的不同像素可能得到完全不同的颜色。举个例子一颗覆盖了窗框一部分的高斯可以在它的一侧表达窗框边缘的深色在另一侧表达玻璃的浅色而不是像原来那样整个区域取一个折中颜色。这就是论文里说的空间变化颜色颜色不再绑定高斯中心而是绑定高斯内部的局部坐标。2.2 四种 SGC 实现对比CLIP 映射、颜色网格、MLP、soft voxel有了“局部坐标驱动颜色”这个框架剩下的问题是怎么把这个颜色函数 F(x_local) 实现出来。论文实际尝试了四种方案各有各的定位和适用场景理解它们的取舍对复现和二次开发非常有帮助。第一种是 CLIP 映射。它为每个高斯原语维护一个特征向量然后在光栅化阶段根据局部坐标查询最接近的特征点用这些特征点的信息映射出颜色。这种方式参数少训练快早期迭代时很稳定。但它的连续性有限当局部坐标落在两个采样点之间时需要依赖插值分辨率不足时容易出现不自然的色块。第二种是颜色网格。做法很直观每颗高斯内部放置一个小尺寸的显式颜色网格通常是二维的尺寸可以是 8x8 或 16x16 这样的规模。渲染时根据局部坐标对网格做双线性或三线性插值得到这个位置的颜色。它的可解释性最强——你几乎可以把这看成把一张微型纹理贴进了一颗高斯里。显存开销比 CLIP 映射稍高但换来的是更清晰的空间变化效果。第三种是 MLP。用一个多层感知器接收局部坐标输出 RGB 颜色。MLP 的好处是天然连续具备很强的表达能力不需要担心分辨率问题理论上可以让一颗高斯表达非常复杂的颜色分布。代价则是计算开销相对更高训练时如果不加约束容易过拟合出一些高频噪声。第四种是 soft voxel可以理解成一个更宽松的体素版本把颜色特征存放在一个连续定义的体素空间里用软权重做插值聚合。它的思路更接近网格类神经渲染空间连续性更好实现复杂度也更高。从我实际测试的体感来说如果只想快速验证这个框架是否有效颜色网格是最容易起步的选择如果追求更高精度且不介意调参成本MLP 版本更值得投入精力。2.3 光栅化器改造的两个关键点重心坐标与前向/反向传播把颜色函数从 SH 系数换成 SGC 之后光栅化器必须跟着改。原版渲染时每个像素拿到的是该像素对应的高斯索引、深度、不透明度以及视角方向然后直接用 SH 求值。改造后光栅化器还要额外输出一个局部坐标——通常就是重心坐标——给颜色函数使用。前向传播的逻辑可以简单表达成屏幕像素坐标 p → 计算三角形重心坐标 λ (λ0, λ1, λ2) 局部坐标 x_local λ0 * V0 λ1 * V1 λ2 * V2 颜色 c F(x_local; θ) 最终像素值 alpha_blending(c, α, depth)其中 V0、V1、V2 是当前高斯覆盖三角片的三个顶点在高斯局部坐标系中的位置θ 是颜色函数自身的参数。反向传播则要更小心。颜色函数的参数要拿到梯度这是废话但同时由于 x_local 是通过重心坐标从像素位置和高斯几何属性推导来的梯度还必须通过 x_local 传回给高斯的旋转、缩放和中心位置。也就是说颜色函数不只是在“被动地给颜色”它参与到了高斯几何参数的优化中。论文里对裁剪平面和包围盒做了一些处理保证重心坐标落在合法范围内避免在边界外采样到不存在的颜色。这里我建议你在读代码的时候重点关注两块一是前向 kernel 里有没有正确输出重心坐标二是反向 kernel 里有没有对旋转和缩放矩阵求 Jacobian。很多粗暴实现会把颜色函数当成一个“只读模块”梯度断在颜色输入处这样改出来的系统虽然能跑但几何参数学不到颜色边界带来的梯度信号效果会明显打折扣。3. 提升稳定性论文里容易被忽略的训练细节3.1 透视抖动重采样处理新视角下的颜色闪烁空间变化颜色带来一个直接问题如果颜色函数对局部坐标非常敏感那么训练时见过的视角和测试时的新视角之间哪怕只有微小的采样位置差异也可能导致颜色出现明显跳跃视觉上就是高频闪烁。这个问题在原版 3DGS 里不明显因为 SH 颜色只依赖于视角方向对空间坐标不敏感但一旦引入局部坐标就等于在渲染链路里加入了一个对空间位置高度敏感的模块。SuperGaussians 借鉴了神经辐射场领域常用的抖动策略思路不算复杂训练时对采样坐标施加一个小幅度的扰动再通过重采样让颜色函数学习到邻域内的一致性。这样颜色函数被迫变得平滑不会对某个精确的采样位置过度过拟合测试时遇到新视角也更从容。从实际效果看这一项对视觉稳定性影响很大。我见过一些没有加抖动重采样的复现版本训练 PSNR 很高但转视角时纹理区域像在“爬动”就是这个原因。3.2 像素邻域损失与颜色裁剪把高频噪声压下去空间变化颜色还容易带来另一个问题相邻像素的颜色突变。真实世界的纹理虽然高频但通常还是具有局部相关性一片树叶从左边到右边颜色是渐变的但如果颜色函数太自由它可能会在相邻像素间引入不自然的跳变优化器为了拟合一张训练图里的噪声把这股噪声学成了纹理。为了解决这个问题论文在损失函数里加入了像素邻域约束。具体做法是不只看单像素的重建误差还让渲染图中相邻两个像素的颜色差尽量接近真实参考图中对应位置两个像素的颜色差。这个约束本质上是一个局部平滑先验你可以有纹理但纹理的变化模式要符合真实图像的局部结构。另外论文还提到了对颜色输出做裁剪或者限制避免颜色函数在几何不明确、透明度很低的区域学到极端数值。这种区域的像素本来对最终画面贡献很小如果颜色函数在那里放飞出一些巨大的值一方面浪费参数容量另一方面可能在边界处产生难看的 halo 效应。3.3 低分辨率到高分辨率的过渡特征与光栅化的尺度匹配CLIP 映射这类基于特征索引的实现还有一个特有的麻烦它存在一个内部分辨率参数。如果训练时先在小分辨率上运行特征网格学到的是低频结构当切换到高分辨率光栅化时原本细密的局部坐标会让特征采样点之间出现空洞或者插值痕迹画面看起来像蒙了一层噪点。论文的做法是采用多阶段策略先在较低分辨率上训练让颜色函数建立整体结构再切到高分辨率阶段补充细节。但切换阶段时优化器里的动量估计、学习率调度都还在如果处理不好loss 会出现一次明显震荡。如果你准备复现这里有个小提醒切换分辨率时建议把学习率适当调低一些给颜色函数一个缓冲期同时在切换后的前几百步可以把像素邻域损失的权重稍微加大一点帮助新分辨率下的颜色分布稳定下来。4. 实验结论与复现阶段的三点提醒4.1 论文报告了哪些提升指标背后的意义是什么论文在常见的新视角合成基准数据集上做了充分实验。这些数据集的共同特点是包含大量植被、窗户、纹理墙面这类高频区域正好是 SH 颜色表达最容易翻车的地方。从报告的结果来看SuperGaussians 在 PSNR、SSIM 和 LPIPS 上都能稳定超过原版 3DGS其中 LPIPS 的提升往往更明显。这其实很好理解——PSNR 和 SSIM 更偏整体像素层面的误差而 LPIPS 更关注感知层面的结构相似性。空间变化颜色让单颗高斯表达纹理的能力变强最直接收益的就是纹理结构的可感知质量。你在论文里会看到相比原版SuperGaussians 在相同高斯数量下渲染出的窗棂、树叶、织物等细节明显更接近真实照片。还有一个更值得关注的维度是原语数量。论文指出在达到相近渲染质量时SuperGaussians 可以用更低的高斯原语数量完成重建。我印象比较深的是在部分场景里用不到原来一半甚至更少的高斯就能维持相似甚至更好的视觉结果。当然这个幅度因场景而异具体数字还是要以原文各场景的表格为准。4.2 和同类 3DGS 增强方法对比时要注意的公平性3DGS 的增强工作在最近一两年里层出不穷但很多改进发生在几何层面——比如换旋转表示、改密度化策略、加正则化项。SuperGaussians 属于在原语内部增加表达能力的路线和几何层面的改进并不互斥甚至可以叠加使用。因此在拿它和别的方法对比时要特别注意控制变量。一个常见的坑是基线模型已经用了更精细的密度化策略而对比模型没有同步调整结果把颜色函数的收益和密度化策略的差异混在一起得出并不公平的结论。复现时我的建议是先把除颜色函数外的所有组件固定成原版 3DGS 的状态先验证 SGC 本身带来的提升确认收益后再把其它增强组件一项一项叠加上去这样每一步的贡献都能被清晰拆开。另外要提一下SuperGaussians 虽然让单颗高斯的颜色表达能力变强但它并没有改变高斯的几何基元本身所以场景的几何重建能力、大尺度结构拟合能力仍然取决于密度化策略和初始化的质量。把它理解成一个“颜色表达层的替换升级”会更准确。4.3 从工程角度看改造成本和主要风险点如果你打算在原版 3DGS 代码库上复现这套思路有几个工程层面的事情值得提前想清楚。首先是光栅化器改动。最核心的是让 CUDA kernel 输出逐像素的重心坐标并且反向传播时要正确处理坐标相关的 Jacobian。这一步是整个改造里最容易出错的地方很多人改了前向之后测试 loss 能下降但可视化几何时发现高斯肆意生长多半就是反向梯度传错了。然后是显存规划。CLIP 映射和高分辨率颜色网格在特征数量上来之后会非常吃显存MLP 版本反向传播的开销也不小。如果显存有限可以先从低分辨率的颜色网格入手确认整个管线没有大问题之后再去挑战 MLP 版本。最后是学习率设置。不要把所有参数放在同一个 learning rate 下优化。颜色函数的参数和坐标、缩放参数对梯度的敏感度差别很大建议分开设置。我的经验是颜色函数部分用比几何参数更小一些的学习率能有效避免训练早期出现“几何乱飞、颜色跟不上”的场面。从我个人复现的体会来说SuperGaussians 最值得借鉴的并不是某个具体模块而是它的思考方式当渲染质量上不去的时候先想清楚是几何数量不够还是单个原语表达能力不足。这个问题想明白了很多 3DGS 后续的定制开发都会更顺手。如果你已经跑通了基础管线不妨从颜色网格这一最简单变体起步把整个 SGC 训练流程跑顺再切换到 MLP 或者其它方向大概率会少踩很多坑。