Three.js教程
3D建模
gaussian-splats-3d
gaussian-splats-3d

打开这个 Demo,镜头缓缓推进,是一间真实拍摄过的房间——灯光、阴影、玻璃反射都在,可以用鼠标自由旋转。但这个场景里没有任何模型文件,没有贴图,没有 .glb,只有几百万个半透明的椭球体。

这是 3D Gaussian Splatting,一种 2023 年从学术界破圈的渲染技术。GaussianSplats3D 把它搬进了 Three.js,让这件事可以直接在浏览器里跑。

它和 NeRF、传统建模的区别

传统 3D 工作流是人工建模:艺术家画出网格、刷上贴图,渲染器计算光照。这条路质量可控,但成本高。

NeRF(Neural Radiance Field)是另一条路:用神经网络从一批照片里学出一个隐式场,推理时对每个像素射出光线,沿途采样颜色和密度再合成。质量不错,但慢——早期版本训练要几天,渲染一帧要几秒,实时完全没法做。

3D Gaussian Splatting(3DGS)解决的正是速度问题。它的核心思路:

  • 从照片的稀疏点云出发,在空间中撒出大量三维高斯椭球
  • 每个椭球有位置、旋转、缩放、不透明度、颜色(含视角相关的球谐系数)
  • 渲染时把所有椭球投影到 2D,按深度排序后从后往前 alpha 合成

整个过程走的是 GPU 光栅化,不是光线步进。训练 15 分钟,实时跑 90fps——这就是它比 NeRF 快的原因。

在 Three.js 里跑起来

npm install @mkkellogg/gaussian-splats-3d

最简单的用法,加载一个 .splat.ply 文件:

import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
 
const viewer = new GaussianSplats3D.Viewer({
    cameraUp: [0, -1, -0.6],
    initialCameraPosition: [-1, -4, 6],
    initialCameraLookAt: [0, 4, 0]
});
 
viewer.addSplatScene('/scenes/room.ksplat', {
    splatAlphaRemovalThreshold: 5,  // 过滤掉 alpha < 5 的透明椭球
    showLoadingUI: true,
    position: [0, 1, 0]
})
.then(() => viewer.start());

cameraUp 需要根据你的场景调整——不同的 3DGS 训练工具导出的坐标轴方向不一样,直接用 [0, 1, 0] 很可能场景是歪的。

嵌进已有的 Three.js 场景

GaussianSplats3D 支持接管已有的 Three.js 场景,把 Splat 渲染器当作场景里的一个对象:

import * as THREE from 'three';
import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
 
const renderer = new THREE.WebGLRenderer({ antialias: true });
const camera = new THREE.PerspectiveCamera(65, width / height, 0.1, 500);
const scene = new THREE.Scene();
 
// 加其他 Three.js 对象
const light = new THREE.DirectionalLight(0xffffff, 1);
scene.add(light);
 
// 把 Viewer 挂在已有的 renderer / camera 上
const viewer = new GaussianSplats3D.Viewer({
    selfDrivenMode: false,   // 手动控制渲染循环
    renderer,
    camera,
    useBuiltInControls: false,
    threeScene: scene
});
 
viewer.addSplatScene('/scenes/garden.ksplat').then(() => {
    function animate() {
        requestAnimationFrame(animate);
        viewer.update();
        viewer.render();
    }
    animate();
});

selfDrivenMode: false 是关键,它让你保留自己的 requestAnimationFrame 循环,Splat 渲染器只在你需要的时候更新。

球谐函数和视角相关颜色

初始化时有个参数 sphericalHarmonicsDegree,默认是 0。

这个参数控制颜色是否随视角变化。度数越高,材质的反射、金属感越真实,但单个 Splat 占的内存更多,能加载的总数量就少:

球谐次数最大 Splat 数效果
0~1600 万固定颜色
1~1100 万低频视角变化
2~800 万更完整的视角相关反射

对一般的室内场景,degree 0 已经够用。只有场景里有强反射材质(玻璃、金属)才值得开到 degree 1 或 2。

文件格式和 WASM 排序

3DGS 渲染的性能瓶颈在排序:每一帧都要把所有椭球按相机距离从远到近排好,才能正确做 alpha 合成。

GaussianSplats3D 的排序跑在 CPU 上,用 WebAssembly + SIMD 加速。.ksplat 是它自己的压缩格式,把 32 位浮点压成 16 位,文件体积减半,加载更快:

# 把 .ply 转成 .ksplat,压缩级别 1(32→16bit),过滤 alpha<5 的点
node util/create-ksplat.js input.ply output.ksplat 1 5

压缩级别:

  • 0 — 不压缩
  • 1 — 位置和缩放压到 16 位
  • 2 — 球谐系数额外压到 8 位

现状和生态

这个仓库已经停止维护了。作者在 README 里直接说了,推荐去看 Spark (opens in a new tab)(World Labs 出的继任项目)。

但 GaussianSplats3D 的代码仍然可用,2.8k stars 的积累说明它在社区跑了很长时间。如果只是想在项目里嵌入一个 Splat 场景看效果,它是目前文档最完整的 Three.js 方案。

想要可视化编辑 Splat 场景(裁剪、调整、导出),PlayCanvas 的 Supersplat (opens in a new tab) 是目前最活跃的工具,8.5k stars,每周还在更新。


相关资源