Project Icon

GaussianSplats3D

Three.js驱动的实时3D高斯点渲染引擎

GaussianSplats3D是基于Three.js的3D高斯点渲染引擎,能将2D图像转换为实时交互的3D场景。它支持.ply、.splat和.ksplat格式,集成了WebXR、球谐函数渲染和优化的剔除排序算法。该项目可独立运行或与Three.js场景集成,为3D渲染提供灵活解决方案。GaussianSplats3D适用于Web环境,支持大规模点云渲染,性能优化突出。

Three.js的3D高斯散射渲染

这是一个基于Three.js的3D高斯散射实时辐射场渲染的渲染器实现。原项目基于CUDA并需要在本地机器上运行,而我想构建一个可通过网络访问的查看器。

3D场景以类似点云的格式存储,可以实时查看、导航和交互。这个渲染器可以处理INRIA项目生成的.ply文件、标准的.splat文件,或者我自己的自定义.ksplat文件,后者是原始.ply文件的精简压缩版本。

当我开始时,已经有了基于网络的查看器 -- antimatter15的WebGL查看器和cvlab-epfl的WebGPU查看器 -- 但还没有Three.js版本。我以这些版本为起点进行了初步实现,但目前这个项目包含的都是我自己的代码。

亮点

  • 渲染完全通过Three.js完成
  • 代码组织为现代ES模块
  • 内置查看器是自包含的,因此加载和查看场景只需很少的代码
  • 查看器可以导入.ply文件、.splat文件或我的自定义压缩.ksplat文件
  • 用户可以将.ply.splat文件转换为.ksplat文件格式
  • 允许与散射点一起渲染Three.js场景或对象组
  • 内置WebXR支持
  • 支持1级和2级球谐函数以实现视角相关效果
  • 注重优化:
    • 在排序和渲染之前使用自定义八叉树对散射点进行剔除
    • WASM散射点排序:使用WASM SIMD指令在C++中实现
    • 部分GPU加速的散射点排序:使用变换反馈预计算散射点距离

已知问题

  • 散射点排序在CPU上运行 – 最好能找到基于GPU的方法
  • 移动或旋转太快时会出现伪影(由于基于CPU的散射点排序)
  • 移动设备上性能不佳
  • 自定义.ksplat文件格式仍需改进,尤其是压缩方面
  • 默认的基于整数的散射点排序对大型场景效果不佳。在这种情况下,可以将查看器参数integerBasedSort设为false,强制使用较慢的基于浮点数的排序

限制

目前可渲染的散射点数量有限制,主要取决于所需的球谐函数程度。这些限制是:

球谐函数程度最大散射点数
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枚举中定义:PlySplatKSplat
splatAlphaRemovalThreshold告诉addSplatScene()忽略任何alpha值小于指定值的散射点(有效范围:0 - 255)。默认为1
showLoadingUI在场景加载时显示加载旋转器和/或加载进度条。默认为true
position场景的位置,作为相对于其默认位置的偏移。默认为[0, 0, 0]
rotation场景的旋转,表示为四元数,默认为[0, 0, 0, 1](单位四元数)。
scale场景的缩放,默认为[1, 1, 1]
progressiveLoad渐进加载场景的散射点数据,并允许在加载散射点时渲染和查看场景。此选项仅对addSplatScene()有效,不适用于addSplatScenes()

Viewer还可以使用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();
});

目前这只适用于写入深度缓冲区的物体(例如标准不透明物体)。支持透明物体将更具挑战性:)

viewer还支持"即插即用"模式。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);


高级选项

viewer通过构造函数参数允许各种级别的自定义。你可以通过为selfDrivenMode参数传递false来控制何时调用其update()render()方法,然后在你认为合适的时候/地方调用这些方法。你还可以使用自己的相机控制器,以及自己的Three.js RendererCamera实例。以下示例展示了所有这些选项:

import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
import * as THREE from 'three';

const renderWidth = 800;
const renderHeight = 600;

const rootElement = document.createElement('div');
rootElement.style.width = renderWidth + 'px';
rootElement.style.height = renderHeight + 'px';
document.body.appendChild(rootElement);

const renderer = new THREE.WebGLRenderer({
    antialias: false
});
renderer.setSize(renderWidth, renderHeight);
rootElement.appendChild(renderer.domElement);

const camera = new THREE.PerspectiveCamera(65, renderWidth / renderHeight, 0.1, 500);
camera.position.copy(new THREE.Vector3().fromArray([-1, -4, 6]));
camera.up = new THREE.Vector3().fromArray([0, -1, -0.6]).normalize();
camera.lookAt(new THREE.Vector3().fromArray([0, 4, -0]));

const viewer = new GaussianSplats3D.Viewer({
    'selfDrivenMode': false,
    'renderer': renderer,
    'camera': camera,
    'useBuiltInControls': false,
    'ignoreDevicePixelRatio': false,
    'gpuAcceleratedSort': true,
    `enableSIMDInSort`: true,
    'sharedMemoryForWorkers': true,
    'integerBasedSort': true,
    'halfPrecisionCovariancesOnGPU': true,
    'dynamicScene': false,
    'webXRMode': GaussianSplats3D.WebXRMode.None,
    'renderMode': GaussianSplats3D.RenderMode.OnChange,
    'sceneRevealMode': GaussianSplats3D.SceneRevealMode.Instant,
    'antialiased': false,
    'focalAdjustment': 1.0,
    'logLevel': GaussianSplats3D.LogLevel.None,
    'sphericalHarmonicsDegree': 0,
    `enableOptionalEffects`: false,
    `plyInMemoryCompressionLevel`: 2
    `freeIntermediateSplatData`: false
});
viewer.addSplatScene('<.ply、.ksplat或.splat文件的路径>')
.then(() => {
    requestAnimationFrame(update);
});

由于selfDrivenMode为false,开发者需要自行调用Viewer类的update()render()方法:

function update() {
    requestAnimationFrame(update);
    viewer.update();
    viewer.render();
}

高级Viewer参数

参数用途
selfDrivenMode如果为false,告诉viewer你将手动调用其update()render()方法。默认为true
renderer向viewer传递Three.js Renderer的实例,否则它将创建自己的实例。默认为undefined
camera向viewer传递Three.js Camera的实例,否则它将创建自己的实例。默认为undefined
useBuiltInControls告诉viewer使用自己的相机控制器。默认为true
ignoreDevicePixelRatio告诉viewer假装设备像素比为1,这可以提高设备像素比较大的设备上的性能,但会略微降低视觉质量。默认为false
gpuAcceleratedSort告诉viewer使用部分GPU加速的方法来排序splats。目前这意味着在GPU上进行splat与相机距离的预计算。建议仅在sharedMemoryForWorkers也为true时将此设置为true。在移动设备上默认为false,其他设备上默认为true
enableSIMDInSort启用SIMD WebAssembly指令用于splat排序。默认为true
sharedMemoryForWorkers告诉viewer通过SharedArrayBuffer使用共享内存来与排序web worker进行数据传输。如果设置为false,建议也将gpuAcceleratedSort设置为false。默认为true
integerBasedSort告诉排序web worker使用相关数据的整数版本来计算splats与相机的距离。由于整数运算比浮点运算更快,这减少了排序时间。但在较大的场景中可能导致整数溢出,因此只应用于小型场景。默认为true
halfPrecisionCovariancesOnGPU告诉viewer在纹理中存储splat协方差数据时使用16位浮点值,而不是32位。默认为false
dynamicScene告诉viewer不要进行任何依赖于场景静态的优化。此外,从viewer的splat网格中检索的所有splat数据默认情况下不会应用其各自的场景变换。
webXRMode告诉viewer是否启用内置的Web VR或Web AR。有效值在WebXRMode枚举中定义:NoneVRAR。默认为None
renderMode控制viewer何时渲染场景。有效值在RenderMode枚举中定义:AlwaysOnChangeNever。默认为Always
sceneRevealMode控制场景加载时使用的淡入效果。有效值在SceneRevealMode枚举中定义:DefaultGradualInstantDefault为渐进加载的场景提供一个漂亮的缓慢淡入效果,为非渐进加载的场景提供快速淡入效果。Gradual将强制所有场景使用缓慢淡入效果。Instant将强制所有加载的场景数据立即可见。
antialiased当为true时,将在渲染过程中执行额外步骤,以解决由于在与训练时明显不同的分辨率下渲染高斯体而导致的伪影。这只适用于使用包含此补偿计算的过程训练的模型。更多详情:https://github.com/nerfstudio-project/gsplat/pull/117, https://github.com/graphdeco-inria/gaussian-splatting/issues/294#issuecomment-1772688093
focalAdjustment用于调整焦距相关计算的非科学参数。对于具有非常小的高斯体和细节的场景,增加此值可以帮助提高视觉质量。默认值为1.0。
logLevel控制台日志的详细程度。默认为GaussianSplats3D.LogLevel.None
sphericalHarmonicsDegree在渲染splats时使用的球谐函数程度(假设splat场景中存在数据)。有效值为0、1或2。默认值为0。
enableOptionalEffects当为true时,允许在渲染过程中使用额外的属性和特性来实现诸如不透明度调整等效果。出于性能考虑,默认为false。这些属性与由dynamicScene参数启用的变换属性(缩放、旋转、位置)是分开的。
plyInMemoryCompressionLevel加载.ply文件进行直接渲染(不导出为.ksplat)时的压缩级别。有效值与.ksplat压缩级别相同(0、1或2)。默认为2。
freeIntermediateSplatData当为true时,将释放解压splat缓冲区后用于填充数据纹理的中间splat数据。这将减少内存使用,但如果需要修改该数据,则需要从splat缓冲区重新填充。默认为false
splatRenderMode确定要启用哪种splat渲染模式。有效值在SplatRenderMode枚举中定义:ThreeDTwoDThreeD是原始/传统模式,TwoD是这里描述的新模式:https://surfsplatting.github.io/

### 创建KSPLAT文件 要将`.ply`或`.splat`文件转换为精简压缩的`.ksplat`格式,有几种选择。最简单的方法是使用[http://127.0.0.1:8080/index.html](http://127.0.0.1:8080/index.html)主演示页面中的用户界面。如果你想以编程方式进行转换,可以在浏览器中运行以下代码:
import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';

const compressionLevel = 1;
const splatAlphaRemovalThreshold = 5; // 255中的5
const sphericalHarmonicsDegree = 1;
GaussianSplats3D.PlyLoader.loadFromURL('<.ply或.splat文件路径>',
                                        compressionLevel,
                                        splatAlphaRemovalThreshold,
                                        sphericalHarmonicsDegree)
.then((splatBuffer) => {
    GaussianSplats3D.KSplatLoader.downloadFile(splatBuffer, 'converted_file.ksplat');
});

上述两种方法都会提示你的浏览器自动开始下载转换后的.ksplat文件。

第三种选择是使用附带的nodejs脚本:

node util/create-ksplat.js [.PLY或.SPLAT文件路径] [输出文件] [压缩级别 = 0] [alpha移除阈值 = 1]

目前支持的compressionLevel值为0120表示不压缩,1表示将缩放、旋转、位置和球谐系数值从32位压缩到16位。21类似,但球谐系数压缩到8位。


CORS问题和SharedArrayBuffer

默认情况下,Viewer类使用共享内存(通过由SharedArrayBuffer支持的类型化数组)与排序splat的Web Worker通信。这种机制存在潜在的安全问题,详见:https://web.dev/articles/cross-origin-isolation-guide。可以通过在`Viewer`构造函数中为`sharedMemoryForWorkers`参数传递`false`来禁用共享内存,但如果你想保持启用,则需要在加载应用程序时服务器发送的响应中包含几个额外的CORS HTTP头。如果没有设置这些头,你可能会在调试控制台中看到类似以下的错误:

"DOMException: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': SharedArrayBuffer transfer requires self.crossOriginIsolated."

对于本地演示,我创建了一个简单的HTTP服务器(util/server.js)来设置这些头:

response.setHeader("Cross-Origin-Opener-Policy", "same-origin");
response.setHeader("Cross-Origin-Embedder-Policy", "require-corp");

Apache中的CORS

对于Apache,你可以编辑.htaccess文件以允许CORS,添加以下行:

Header add Cross-Origin-Opener-Policy "same-origin"
Header add Cross-Origin-Embedder-Policy "require-corp"

此外,你可能需要要求安全连接到你的服务器,将所有通过http://的访问重定向到https://。在Apache中,可以通过在.htaccess文件中添加以下行来实现:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

Vite中的CORS

对于Vite,一个流行的选择是通过npm安装vite-plugin-cross-origin-isolation插件,然后在vite.config.js文件中添加以下内容:

import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    {
      name: "configure-response-headers",
      configureServer: (server) => {
        server.middlewares.use((_req, res, next) => {
          res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
          res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
          next();
        });
      },
    },
  ],
});

在issue #41中还提到了其他配置Vite来处理这个问题的方法。

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号