高斯泼溅 (Gaussian Splatting) 的 Three.js 实现
「33 高斯泼溅 (Gaussian Splatting) 的 Three.js 实现」
/~825b3YKMDE~:/
链接:https://pan.quark.cn/s/561494312803
这是一个基于 Three.js 的 3D 高斯泼溅渲染器实现,该技术源自论文《3D Gaussian Splatting for Real-Time Radiance Field Rendering》,用于从 2D 图像生成 3D 场景。原项目基于 CUDA 并需要在本地机器上运行,而我的目标是构建一个可通过网络访问的查看器。3D 场景以类似点云的格式存储,可以实时查看、导航和交互。此渲染器兼容 INRIA 项目生成的 .ply 文件、标准 .splat 文件,以及我自定义的 .ksplat 文件(这是原始 .ply 文件的精简和压缩版本)。
当我开始这个项目时,已经有基于 Web 的查看器可用------来自 antimatter15 的 WebGL 查看器和来自 cvlab-epfl 的 WebGPU 查看器------但当时还没有 Three.js 版本。我以这些版本作为我初始实现的起点,但截至目前,本项目包含的所有代码均为我独立编写。
⚠️ 正如大家注意到的,此仓库目前没有积极的开发活动。它更像是一个副业项目,我目前没有足够的时间投入有意义的开发。我建议您将 Spark 作为替代方案------它由 World Labs 的才华横溢的团队和开源社区积极开发,并包含许多高级特性和功能。
亮点
渲染完全通过 Three.js 完成
代码组织为现代 ES 模块
内置查看器自成一体,加载和查看场景只需极少代码
查看器可导入 .ply 文件、.splat 文件或我自定义的压缩 .ksplat 文件
用户可将 .ply 或 .splat 文件转换为 .ksplat 文件格式
允许与 Three.js 场景或物体组一同渲染
内置 WebXR 支持
支持用于视点相关效果(view-dependent effects)的 1 阶和 2 阶球谐函数
专注于优化:
使用自定义八叉树在排序和渲染前剔除泼溅点
WASM 泼溅点排序:使用 WASM SIMD 指令的 C++ 实现
部分 GPU 加速的泼溅点排序:使用变换反馈(transform feedback)预计算泼溅点距离
提示
渐进式加载的 .ply 和 .splat 文件不会应用某些优化,例如缓存优化的泼溅点排序。为了获得最佳性能,请将这些文件类型转换为 .ksplat 或非渐进式加载。
将场景转换为 .ksplat 会获得最快的加载时间,因为其格式与泼溅点数据的内部格式匹配。
具有大尺寸或高泼溅点密度的场景可能会导致默认设置出现问题。对于这些场景,您可以尝试以下方法:
将查看器参数
integerBasedSort设置为false以强制使用较慢的、基于浮点数的泼溅点排序。尝试为查看器参数
splatSortDistanceMapPrecision设置更大的值,以调整泼溅点排序中距离图的精度。更大的精度值会导致性能下降,但通常可以缓解精度过低时出现的视觉伪影。已知问题
泼溅点排序在 CPU 上运行------希望能找到一种基于 GPU 的方法
移动或旋转过快时会出现视觉伪影(由于基于 CPU 的泼溅点排序)
移动设备上性能欠佳
自定义的 .ksplat 文件格式仍需改进,特别是在压缩方面
尺寸非常大的场景可能会崩溃(通常出现泼溅点排序的"索引越界"错误)。在这些情况下,更改
splatSortDistanceMapPrecision或integerBasedSort可能没有帮助。
限制
目前可渲染的泼溅点数量存在限制,这些限制主要取决于所需的球谐函数阶数。限制如下:
球谐函数阶数 最大泼溅点数量 0 ~ 16,000,000 1 ~ 11,000,000 2 ~ 8,000,000 未来的工作将包括优化泼溅点数据打包到数据纹理的方式,这将有助于提高这些限制。
未来工作
这仍然是一个进行中的项目!仍有一些事情需要完成:
改进泼溅点数据打包到数据纹理的方式
继续优化基于 CPU 的泼溅点排序------也许可以尝试某种增量排序?
支持超大场景(流式加载区块和 LOD)
在线演示
https://projects.markkellogg.org/threejs/demo_gaussian_splats_3d.php
控制
鼠标
左键点击 设置焦点
左键点击并拖动 环绕焦点旋转
右键点击并拖动 平移相机和焦点
键盘
C 切换网格光标,显示鼠标投影射线与泼溅网格的交点
I 切换信息面板,显示调试信息:
相机位置
相机焦点/观察点
相机上向量
网格光标位置
当前 FPS
渲染器窗口大小
已渲染泼溅点与总泼溅点的比例
上次泼溅点排序耗时
U 切换调试对象,显示相机控制的方向。它包括一个代表相机轨道轴的绿色箭头和一个代表相机仰角为 0 的平面的白色方块。
左箭头 将相机的上向量逆时针旋转
右箭头 将相机的上向量顺时针旋转
P 切换点云模式,其中每个泼溅点被渲染为一个实心圆
**=** 增加泼溅点比例
**-** 减小泼溅点比例
O 切换正交模式
从源代码构建并本地运行
进入代码目录并运行:
npm install接下来运行构建。对于 Linux 和 Mac OS 系统,运行:
npm run build对于 Windows,我添加了一个兼容 Windows 的构建命令:
npm run build-windows要在本地运行演示场景,运行:
npm run demo演示将在本地访问:http://127.0.0.1:8080/index.html。您需要下载演示场景的数据并将其解压到:
<代码目录>/build/demo/assets/data演示场景数据可在此处下载:https://projects.markkellogg.org/downloads/gaussian_splat_data.zip
作为 NPM 包安装
如果您不想从源代码构建库,它也可以作为 NPM 包使用。NPM 包不包含源代码存储库中的源代码和演示。要安装,运行以下命令:
npm install @mkkellogg/gaussian-splats-3d基本用法
要运行内置查看器:
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('<.ply、.ksplat 或 .splat 文件的路径>', { 'splatAlphaRemovalThreshold': 5, 'showLoadingUI': true, 'position': [0, 1, 0], 'rotation': [0, 0, 0, 1], 'scale': [1.5, 1.5, 1.5] }) .then(() => { viewer.start(); });查看器参数
参数 用途 cameraUp查看场景的固有"上"向量(仅在与轨道控制一起使用且查看器使用其自身相机时有效)。用作相机将围绕其旋转的轴,并用于确定场景相对于相机的方向。 initialCameraPosition相机的初始位置(仅当查看器使用其自身相机时使用)。 initialCameraLookAt相机的初始焦点和相机轨道的中心(仅当查看器使用其自身相机时使用)。
addSplatScene()的参数
参数 用途 format强制加载器在加载泼溅场景时假定指定的文件格式。这在从没有文件扩展名的 URL 加载时很有用。有效值在 SceneFormat枚举中定义:Ply、Splat和KSplat。splatAlphaRemovalThreshold告诉 addSplatScene()忽略 Alpha 值小于指定值的任何泼溅点(有效范围:0 - 255)。默认为 1。showLoadingUI在场景加载时显示加载旋转器和/或加载进度条。默认为 true。position场景的位置,作为其默认位置的偏移量。默认为 [0, 0, 0]。rotation以四元数表示的场景旋转,默认为 [0, 0, 0, 1](单位四元数)。scale场景的比例,默认为 [1, 1, 1]。progressiveLoad渐进式加载场景的泼溅点数据,并允许在泼溅点加载时渲染和查看场景。此选项仅对 addSplatScene()有效,对addSplatScenes()无效。查看器还可以通过
addSplatScenes()函数同时加载多个场景:
import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d'; viewer.addSplatScenes([{ 'path': '<第一个 .ply、.ksplat 或 .splat 文件的路径>', 'splatAlphaRemovalThreshold': 20 }, { 'path': '<第二个 .ply、.ksplat 或 .splat 文件的路径>', 'rotation': [-0.14724434, -0.0761755, 0.1410657, 0.976020], 'scale': [1.5, 1.5, 1.5], 'position': [-3, -2, -3.2] } ]) .then(() => { viewer.start(); });
addSplatScene()和addSplatScenes()方法将接受原始的 .ply 文件、标准的 .splat 文件以及我自定义的 .ksplat 文件。集成 THREE.js 场景
如果您希望由查看器处理渲染,可以将您自己的 Three.js 场景集成到查看器中。只需将 Three.js 场景对象作为
threeScene参数传递给构造函数:
import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d'; import * as THREE from 'three'; const threeScene = new THREE.Scene(); const boxColor = 0xBBBBBB; const boxGeometry = new THREE.BoxGeometry(2, 2, 2); const boxMesh = new THREE.Mesh(boxGeometry, new THREE.MeshBasicMaterial({'color': boxColor})); boxMesh.position.set(3, 2, 2); threeScene.add(boxMesh); const viewer = new GaussianSplats3D.Viewer({ 'threeScene': threeScene, }); viewer.addSplatScene('<.ply、.ksplat 或 .splat 文件的路径>') .then(() => { viewer.start(); });目前,这仅适用于写入深度缓冲区的对象(例如标准不透明物体)。支持透明物体将更具挑战性 :)
还支持查看器的"即插即用"模式。
DropInViewer类封装了Viewer,可以像任何其他可渲染对象一样添加到 Three.js 场景中:
import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d'; import * as THREE from 'three'; const threeScene = new THREE.Scene(); const viewer = new GaussianSplats3D.DropInViewer({ 'gpuAcceleratedSort': true }); viewer.addSplatScenes([{ 'path': '<.ply、.ksplat 或 .splat 文件的路径>' 'splatAlphaRemovalThreshold': 5 }, { 'path': '<.ply、.ksplat 或 .splat 文件的路径>', 'rotation': [0, -0.857, -0.514495, 6.123233995736766e-17], 'scale': [1.5, 1.5, 1.5], 'position': [0, -2, -1.2] } ]); threeScene.add(viewer);

