欢迎光临
我们一直在努力

SparkJS-完整技术文档

SparkJS 完整技术文档

Spark — 基于 THREE.js 和 WebGL2 的高级 3D Gaussian Splatting 渲染器 源文档: https://sparkjs.dev/docs/ 整理日期: 2026-05-08


目录

  • 快速开始
  • 概述
  • 2.0 新特性
  • 0.1 → 2.0 迁移指南
  • 系统设计
  • SparkRenderer
  • SplatMesh
  • PackedSplats
  • ExtSplats
  • 加载 Splat 文件
  • 程序化生成 Splats
  • Splat 编辑器
  • Level-of-Detail 入门
  • LoD 深度解析
  • Dyno 着色器图系统
  • Dyno 标准库
  • 控制
  • 性能调优
  • 社区资源

  • 1. 快速开始

    1.1 Quick Start

    复制以下代码到 index.html 文件,或直接在 Web Playground 中运行:

    <style> body {margin: 0;} </style>
    <script type="importmap">
    {
    "imports": {
    "three": "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.180.0/three.module.js",
    "@sparkjsdev/spark": "https://sparkjs.dev/releases/spark/2.0.0/spark.module.js"
    }
    }
    </script>
    <script type="module">
    import * as THREE from "three";
    import { SparkRenderer, SplatMesh } from "@sparkjsdev/spark";

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 1000);
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    const spark = new SparkRenderer({ renderer });
    scene.add(spark);

    const splatURL = "https://sparkjs.dev/assets/splats/butterfly.spz";
    const butterfly = new SplatMesh({ url: splatURL });
    butterfly.quaternion.set(1, 0, 0, 0);
    butterfly.position.set(0, 0, 3);
    scene.add(butterfly);

    renderer.setAnimationLoop(function animate(time) {
    renderer.render(scene, camera);
    butterfly.rotation.y += 0.01;
    });
    </script>

    1.2 NPM 安装

    npm install @sparkjsdev/spark

    1.3 开发构建

    构建 Spark(需要安装 Rust):

    npm install
    npm run build:wasm
    npm run dev

    这将在 http://localhost:8080/ 启动一个 Web 服务器,包含示例。

    1.4 Spark 2.0 快速开始

    Spark 2.0 使用 NewSparkRenderer(现已合并为 SparkRenderer)并添加到场景中:

    const spark = new SparkRenderer({ renderer: THREE.WebGLRenderer });
    scene.add(spark);

    启用 LoD 渲染:

    const splats = new SplatMesh({ url: "./my-splats.spz", lod: true });
    scene.add(splats);

    推荐预构建 LoD 树:

    npm run build-lod — my-splats.ply more-splats.spz –quality

    输出 .RAD 文件可直接加载,支持分页流式传输:

    const splats = new SplatMesh({ url: "./my-splats-lod.rad", paged: true });
    scene.add(splats);


    2. 概述

    3D Gaussian Splatting 已成为生成式 AI 和 3D 重建领域的前沿技术。通过将 3D 场景和对象表示为微小的高斯形状"斑点"(splats)集合,机器学习技术可以创建细节丰富、照片级逼真的 3D 内容,并实现实时渲染。

    Spark 是一个为 THREE.js 和 WebGL2 构建的动态 3DGS 渲染器,在任何 Web 浏览器(桌面、移动、WebXR)中运行。只需数行代码,任何使用 THREE.js 的用户都可以轻松地将 3DGS 添加到场景中。

    核心特性

    • 与 THREE.js 渲染管线集成:融合 splat 和网格对象渲染
    • 跨平台:支持 98%+ WebGL2 设备
    • 高性能:即使在低功耗移动设备上也能快速渲染
    • 多对象正确排序:多个 splat 对象同时渲染时保证正确的遮挡关系
    • 多格式支持:.PLY(含压缩)、.SPZ、.SPLAT、.KSPLAT
    • 多视点渲染:同时渲染多个视点
    • 完全动态:每个 splat 可被变换和编辑以支持动画
    • 实时编辑:splat 颜色编辑、位移、骨骼动画
    • 着色器图系统:在 GPU 上动态创建/编辑 splat
    • 高精度编码:ExtSplats 提供 32 字节/splat 的 float32 精度

    3. 2.0 新特性

    Spark 2.0 主要驱动力是支持海量动态 3D Gaussian Splat 世界。

    3.1 2.0 核心特性

    稳定的可调帧率

    • 计算视点相关的"切片"通过 LoD 树,选择最佳 N 个 splats
    • 固定 N(桌面 2.5M,移动 500K-1.5M)
    • 时间复杂度 O(N log N)

    复合 LoD 世界

    • 支持多 splat 对象同时 LoD 渲染
    • 联合遍历多棵树,最大化最小屏幕空间 splat 尺寸

    Splat 降采样

    • tiny-lod:快速紧凑算法(按需运行)
    • bhatt-lod:高质量算法(预处理推荐)

    可流式传输的 LoD 文件格式

    • .RAD(RADiance field)新格式
    • 支持 HTTP Range 请求流式传输
    • 从粗糙到精细的渐进式加载

    共享分页/流式 splat 缓冲区

    • LRU "splat 页表"管理 GPU 内存
    • 默认 16M splats 池,跨所有对象共享
    • 支持 100M+ splats 场景在移动设备上实时渲染

    高精度 splat 编码

    • ExtSplats:32字节/splat,float32 中心坐标

    3.2 其他特性

    • 多独立视点和渲染器:多个 SparkRenderer 实例
    • 即时 LoD:new SplatMesh({ url: "./my-splats.spz", lod: true })
    • LoD/非LoD 版本切换:nonLod: true + SplatMesh.enableLod
    • 按需创建 LoD:.createLodSplats()
    • 命令行 LoD 构建工具:build-lod
    • 可调 LoD 细节级别:全局和每个 SplatMesh 级别
    • 大文件支持:ReadableStream 加载 GB 级文件
    • 可链式 splat 修改器
    • 可自定义 splat 着色器代码
    • SparkXr:简易 AR/VR 包装器
    • 协方差 Splats(实验性):非均匀缩放
    • 线性混合蒙皮(实验性):骨骼动画的 LBS
    • Splat 传送门(实验性):动态 splat 传送门

    4. 0.1 → 2.0 迁移指南

    4.1 更新依赖

    {
    "dependencies": {
    "three": "0.180.0",
    "@sparkjsdev/spark": "^2.0.0"
    }
    }

    CDN 直接链接:

    <script type="importmap">
    {
    "imports": {
    "three": "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.180.0/three.module.js",
    "@sparkjsdev/spark": "https://sparkjs.dev/releases/spark/2.0.0/spark.module.js"
    }
    }
    </script>

    4.2 关键变更

    Spark 0.1Spark 2.0
    自动注入 SparkRenderer 必须手动创建 new SparkRenderer({ renderer }) 并 scene.add(spark)
    spark.newViewpoint() 创建独立 new SparkRenderer()
    sortRadial 按视点配置 直接在 SparkRenderer 上配置
    VRButton 替换为 SparkXr
    sort32、sort360 已废弃,32位排序和360度排序自动启用
    随机无排序渲染 已废弃
    Splat 纹理 已废弃

    4.3 临时回退

    如有需要可使用 OldSparkRenderer(原 Spark 0.1 的 SparkRenderer)。


    5. 系统设计

    5.1 渲染数据流

    SparkRenderer 是 Spark 中的关键组件,管理排序过程:

  • 遍历可见的 THREE.js 场景图
  • 编译场景中所有 splats 的完整列表
  • 通过 SparkViewpoint 从 GPU 读回距离
  • 在后台 worker 线程(SplatWorker)中使用桶排序
  • 在下一次 render() 调用时执行单次实例化几何绘制
  • 排序顺序落后渲染至少一帧,但通常不可察觉。

    const spark = new SparkRenderer({ renderer: webGlRenderer });
    scene.add(spark);

    5.2 可编程 Splats

    标准化管线提供:

    • 刚性变换
    • RGB/透明度调整
    • 球谐函数
    • 颜色编辑和扰动(通过 SplatEdit)
    • 双四元数骨骼动画(SplatSkinning)
    • 可通过 dyno 注入任意代码

    SplatMesh 派生自 SplatGenerator → THREE.Object3D,可放置在场景层次结构的任何位置。


    6. SparkRenderer

    6.1 创建

    const spark = new SparkRenderer({
    renderer: THREE.WebGLRenderer;
    });
    const scene = new THREE.Scene();
    scene.add(spark);

    6.2 构造函数参数

    必需参数

    参数描述
    renderer THREE.WebGLRenderer 实例。应使用 antialias: false(默认)

    重要可选参数

    参数默认值描述
    maxStdDev Math.sqrt(8) 渲染标准差范围
    sortRadial true true=径向距离排序,false=Z深度排序
    enableLod true 启用 LoD 渲染
    lodSplatCount 自动 目标 splat 数量
    lodSplatScale 1.0 LoD splat 数量缩放因子
    pagedExtSplats false 分页 splat 使用扩展编码
    covSplats false 启用协方差编码(非均匀缩放)
    enable2DGS false 启用 2D 高斯渲染
    premultipliedAlpha true 预乘 alpha
    vertexShader undefined 自定义顶点着色器
    fragmentShader undefined 自定义片段着色器
    target undefined 离线渲染目标配置

    6.3 主要方法

    方法描述
    dispose() 释放所有资源
    update({ scene, camera }) 手动更新 splat
    render(scene, camera) 渲染到画布
    renderTarget({ scene, camera }) 渲染到渲染目标
    readTarget() 读回渲染目标像素
    renderReadTarget({ scene, camera }) 渲染并读回
    renderCubeMap({ scene, worldCenter, … }) 渲染立方体贴图
    renderEnvMap({ scene, worldCenter, … }) 渲染环境贴图

    7. SplatMesh

    SplatMesh 是显示和操作 splat 集合的高级接口,类似于传统的三角形 THREE.Mesh(包含几何体 + 材质)。派生自 SplatGenerator → THREE.Object3D。

    7.1 创建

    const splats = new SplatMesh({
    url?: string; // 文件 URL (.ply/.spz/.splat/.ksplat/.sog/.zip/.rad)
    fileBytes?: Uint8Array; // 原始文件字节
    stream?: ReadableStream; // 流式加载
    streamLength?: number; // 流长度
    packedSplats?: PackedSplats; // 已有 PackedSplats
    maxSplats?: number; // 预留 splat 数量
    constructSplats?: (splats) => Promise<void>; // 构造函数
    onProgress?: (event) => void; // 进度回调
    onLoad?: (mesh) => Promise<void>; // 加载完成回调
    editable?: boolean; // 是否可编辑 (默认 true)
    raycastable?: boolean; // 是否参与射线检测 (默认 true)
    onFrame?: ({ mesh, time, deltaTime }) => void; // 每帧回调
    objectModifiers?: GsplatModifier[]; // 对象空间修改器
    worldModifiers?: GsplatModifier[]; // 世界空间修改器
    splatEncoding?: SplatEncoding; // 编码范围覆盖
    extSplats?: boolean | ExtSplats; // 扩展 splat 编码
    lod?: boolean | number; // 启用 LoD
    nonLod?: boolean; // 保留非 LoD 版本
    enableLod?: boolean; // 强制使用/不使用 LoD
    lodScale?: number; // 每网格 LoD 细节缩放
    paged?: boolean; // 启用分页流式传输
    });
    scene.add(splats);

    7.2 实例属性

    属性描述
    initialized Promise,可等待初始化完成
    isInitialized 是否初始化完成
    recolor THREE.Color,着色所有 splats
    opacity 全局透明度倍数
    skinning SplatSkinning 骨骼动画实例
    edits SplatEdit 列表
    splatRgba RgbaArray,覆盖 RGBA 值
    maxSh 最大球谐函数级别 (默认 3)
    enableViewToObject 每帧更新 viewToObject
    enableViewToWorld 每帧更新 viewToWorld
    enableWorldToView 每帧更新 worldToView

    7.3 方法

    方法描述
    dispose() 释放缓冲区
    pushSplat(center, scales, quaternion, opacity, color) 添加 splat
    forEachSplat(callback) 遍历所有 splat
    getBoundingBox(centers_only) 获取包围盒
    updateGenerator() 更新处理管线
    raycast(raycaster, intersects) 射线检测

    7.4 射线检测示例

    const raycaster = new THREE.Raycaster();
    canvas.addEventListener("click", (event) => {
    raycaster.setFromCamera(new THREE.Vector2(
    (event.clientX / canvas.width) * 2 1,
    (event.clientY / canvas.height) * 2 + 1,
    ), camera);
    const intersects = raycaster.intersectObjects(scene.children);
    const splatIndex = intersects.findIndex((i) => i.object instanceof SplatMesh);
    });

    8. PackedSplats

    PackedSplats 是 Gaussian splat 的集合,以精确 16 字节/splat 的格式打包,最大化内存和缓存效率。

    8.1 字节布局

    每个 PackedSplat 占用 16 字节(4 × uint32):

    偏移 (字节)字段大小描述
    0 R 1 红色通道 (uint8 → 0.0–1.0)
    1 G 1 绿色通道
    2 B 1 蓝色通道
    3 A 1 Alpha/透明度通道
    4–5 center.x 2 X 坐标 (float16)
    6–7 center.y 2 Y 坐标 (float16)
    8–9 center.z 2 Z 坐标 (float16)
    10 quat oct.U 1 八面体四元数 U 分量 (uint8)
    11 quat oct.V 1 八面体四元数 V 分量 (uint8)
    12 scale.x 1 X 缩放 (log编码到 uint8)
    13 scale.y 1 Y 缩放
    14 scale.z 1 Z 缩放
    15 quat angle (θ) 1 编码四元数旋转角度 (θ/π·255)

    8.2 编码细节

    • RGBA:uint8 sRGB 值,0…255 → 0…1
    • 中心坐标:float16,10位尾数(约 0.1% 精度),范围达 32K
    • 缩放:对数编码 ln(scale),范围 e-12…e9,约 7% 步长
    • 旋转:八面体编码轴方向 (U/V 各 8 位) + 旋转角度 (0…π, 8 位)

    8.3 球谐函数 (SH) 布局

    缓冲区每 splat 字数存储值量化
    sh1 2 (8 字节) 9 值 (3 系数 × RGB) Sint7
    sh2 4 (16 字节) 15 值 (5 系数 × RGB) Sint8
    sh3 4 (16 字节) 21 值 (7 系数 × RGB) Sint6

    8.4 JavaScript API

    // 通过接口设置
    packedSplats.setSplat(index, center, scales, quaternion, opacity, color);

    // 直接设置 Uint32 数组
    import { utils } from "@sparkjsdev/spark";
    utils.setPackedSplat(packedSplats.packedArray, index, x, y, z, scaleX, scaleY, );
    utils.setPackedSplatQuat(packedSplats.packedArray, index, quatX, quatY, quatZ, quatW);

    // 解包
    const { center, scales, quaternion, color, opacity } = utils.unpackSplat(packedSplats.packedArray, index);

    // 遍历
    packedSplats.forEachSplat((index, center, scales, quaternion, opacity, color) => {
    utils.setPackedSplatScales(packedSplat.packedArray, index, 0.005, 0.01, 0.015);
    packedSplat.setSplat(index, center, scales, quaternion, opacity, color);
    });

    8.5 GLSL 工具函数

    // 打包 splat 为 uvec4
    uvec4 packSplat(vec3 center, vec3 scales, vec4 quaternion, vec4 rgba);

    // 从 uvec4 解包 splat
    void unpackSplat(uvec4 packed, out vec3 center, out vec3 scales, out vec4 quaternion, out vec4 rgba);

    8.6 实例方法

    方法描述
    dispose() 释放渲染目标和纹理
    ensureSplats(numSplats) 确保数组容量
    getSplat(index) 解包 splat 为 THREE.js 组件
    setSplat(index, center, scales, quaternion, opacity, color) 设置所有组件
    pushSplat(center, scales, quaternion, opacity, color) 追加 splat
    forEachSplat(callback) 遍历
    getTexture() 返回 DataArrayTexture
    getEmpty() 返回未初始化纹理
    createLodSplats() 创建 LoD

    9. ExtSplats

    ExtSplats 使用双倍宽度编码 32 字节/splat(vs PackedSplats 的 16 字节),以保留更多精度同时保持 GPU 友好布局。

    9.1 32 字节布局

    extArrays[0] 包含字节 0…15,extArrays[1] 包含字节 16…31:

    偏移字段大小描述
    0-3 center.x 4 float32
    4-7 center.y 4 float32
    8-11 center.z 4 float32
    12-13 opacity 2 float16
    14-15 reserved 2 保留
    16-17 color.r 2 float16
    18-19 color.g 2 float16
    20-21 color.b 2 float16
    22-23 ln(scale.x) 2 float16
    24-25 ln(scale.y) 2 float16
    26-27 ln(scale.z) 2 float16
    28-31 quaternion 4 打包八面体+角度 (10/10/12 位)

    9.2 API

    import { utils } from "@sparkjsdev/spark";

    utils.encodeExtSplat(extSplats.extArrays, index, x, y, z, sx, sy, sz, qx, qy, qz, qw, opacity, r, g, b);
    const { center, scales, quaternion, color, opacity } = utils.decodeExtSplat(extSplats.extArrays, index);

    Dyno 中使用:

    const gsplat = dyno.readExtSplat(extSplats.dyno, index);


    10. 加载 Splat 文件

    Spark 支持大多数流行的 splat 文件格式。

    10.1 可自动检测的格式 (.ply, .spz, .sog/.zip, .rad)

    // 简单加载
    const splats = new SplatMesh({ url: "./butterfly.ply" });
    scene.add(splats);

    // 无扩展名但可从内容自动检测
    scene.add(new SplatMesh({ url: "plyBin/0123456789abcdef" }));

    10.2 通过 PackedSplats/ExtSplats 加载

    const packedSplats = new PackedSplats({ url: "./clone.ply" });

    const splats1 = new SplatMesh({ packedSplats });
    scene.add(splats1);

    const splats2 = new SplatMesh({ packedSplats });
    scene.add(splats2);

    10.3 通过 SplatLoader

    const loader = new SplatLoader();
    loader.loadAsync(url, (event) => {
    if (event.type === "progress") {
    const progress = event.lengthComputable
    ? `${((event.loaded / event.total) * 100).toFixed(2)}%`
    : `${event.loaded} bytes`;
    console.log(`下载进度: ${progress}`);
    }
    })
    .then((packedSplats) => {
    const splatMesh = new SplatMesh({ packedSplats });
    splatMesh.quaternion.set(1, 0, 0, 0); // OpenCV → OpenGL
    splatMesh.position.set(0, 0, 1);
    splatMesh.scale.setScalar(0.5);
    scene.add(splatMesh);
    })
    .catch((error) => console.warn(error));

    10.4 额外格式 (.splat, .ksplat)

    这些格式无法从内容自动检测,需通过 URL 扩展名或显式设置 fileType:

    // URL 扩展名推断
    const splats = new SplatMesh({ url: "./butterfly.splat" });
    scene.add(splats);

    // 显式设置
    scene.add(new SplatMesh({
    url: "splatBin/0123456789abcdef",
    fileType: SplatFileType.SPLAT,
    }));


    11. 程序化生成 Splats

    11.1 基础方法

    const splats = new PackedSplats();
    const center = new THREE.Vector3(0, 0, 0);
    const scales = new THREE.Vector3(0.1, 0.1, 0.1);
    const quaternion = new THREE.Quaternion();
    const opacity = 1.0;
    const color = new THREE.Color();
    splats.pushSplat(center, scales, quaternion, opacity, color);

    使用构造函数回调:

    const splats = new PackedSplats({
    construct: (splats) => {
    for (let i = 0; i < NUM_SPLATS; ++i) {
    // 计算 splat #i
    splats.pushSplat(center, scales, quaternion, opacity, color);
    }
    },
    });

    const mesh = new SplatMesh({ packedSplats: splats });
    scene.add(mesh);

    11.2 内置构造函数

    Grid 网格

    import { constructGrid } from "@sparkjsdev/spark";

    const grid = new SplatMesh({
    constructSplats: (splats) => constructGrid({
    splats,
    extents: new THREE.Box3(
    new THREE.Vector3(10, 10, 10),
    new THREE.Vector3(10, 10, 10),
    ),
    }),
    });

    XYZ 坐标轴

    import { constructAxes } from "@sparkjsdev/spark";

    const axes = new SplatMesh({
    constructSplats: (splats) => constructAxes({ splats }),
    });

    球体

    import { constructSpherePoints } from "@sparkjsdev/spark";

    const sphere = new SplatMesh({
    constructSplats: (splats) => constructSpherePoints({
    splats,
    maxDepth: 4, // 递归深度(splat 数量指数增长)
    }),
    });

    文本光栅化

    const splats = textSplats({
    text: string;
    font?: string; // 默认 "Arial"
    fontSize?: number; // 默认 32
    color?: THREE.Color;
    dotRadius?: number; // 默认 0.8
    textAlign?: "left" | "center" | "right";
    lineHeight?: number; // 默认 1.0
    });

    图像转 Splats

    const image = imageSplats({
    url: string;
    dotRadius?: number;
    subXY?: number; // 子采样因子
    forEachSplat?: (width, height, index, center, scales, quaternion, opacity, color) => number | null;
    });

    粒子效果

    staticBox

    在 3D 包围盒内生成随机"静态"splats。

    snowBox

    生成随时间确定性移动的粒子轨迹,支持雪/雨等效果:

    const snowControls = generators.snowBox({
    box: THREE.Box3, // 包围盒
    numSplats: number, // splat 数量
    density: number, // 密度 (默认 100)
    anisoScale: THREE.Vector3, // 各向异性缩放
    minScale: number, // 最小缩放 (默认 0.001)
    maxScale: number, // 最大缩放 (默认 0.005)
    fallDirection: THREE.Vector3, // 下落方向
    fallVelocity: number, // 下落速度
    wanderScale: number, // 漂移缩放
    wanderVariance: number, // 漂移方差
    color1: THREE.Color, // 颜色1
    color2: THREE.Color, // 颜色2
    opacity: number, // 透明度 (默认 1)
    });

    // 预设
    const mySnow = { DEFAULT_SNOW, density: 500 };
    const myRain = { DEFAULT_RAIN };


    12. Splat 编辑器

    Spark 提供通过 SDF(有符号距离场)编辑 splat 的能力。

    12.1 创建编辑操作

    const edit = new SplatEdit({
    name?: string;
    rgbaBlendMode?: SplatEditRgbaBlendMode; // 默认 MULTIPLY
    sdfSmooth?: number; // SDF 形状之间混合 (默认 0.0)
    softEdge?: number; // 软边缘 (默认 0.0)
    invert?: boolean; // 反转 (默认 false)
    sdfs?: SplatEditSdf[]; // SDF 形状数组
    });
    scene.add(edit);

    12.2 RGBA 混合模式

    模式描述
    MULTIPLY RGBA 分量乘法混合(默认)
    SET_RGB 忽略 Alpha,直接设置 RGB
    ADD_RGBA 将 SDF 的 RGBA 值加到 splat 上(可产生超饱和效果)

    12.3 SDF 形状

    const shape = new SplatEditSdf({
    type?: SplatEditSdfType; // 默认 SPHERE
    invert?: boolean;
    opacity?: number; // 默认 1.0
    color?: THREE.Color; // 默认 Color(1, 1, 1)
    displace?: THREE.Vector3; // 默认 Vector3(0,0,0)
    radius?: number; // 默认 0.0
    });
    edit.add(shape);

    SDF 形状类型

    类型描述参数
    ALL 影响空间所有点
    PLANE 无限平面 position, rotation
    SPHERE 球体 position, radius
    BOX 盒子(可选圆角) position, rotation, sizes, radius
    ELLIPSOID 椭球体 position, rotation, sizes
    CYLINDER 圆柱体 position, rotation, size_y
    CAPSULE 胶囊体 position, rotation, size_y
    INFINITE_CONE 无限圆锥 position, rotation, radius=angle

    12.4 多 SDF 形状混合

    RGBA-XYZ 值使用指数"softmax"函数混合所有 SDF 形状。可通过更新 SplatEdit 和 SplatEditSdf 对象创建实时动画和特效(如风中波纹、火焰、水面效果等)。

    13. Level-of-Detail 入门

    13.1 两种简单方法

    方法一:使用 lod 标志

    const splats = new SplatMesh({ url: "./my-splats.spz", lod: true });
    scene.add(splats);

    在后台 WebWorker 中创建 LoD 版本(每 1M splats 约 1-3 秒),最多支持约 30M 输入 splats。

    方法二:预构建 LoD 树(推荐)

    npm run build-lod — my-splats.ply more-splats.spz –quality

    输出 my-splats-lod.rad,可直接加载:

    const splats = new SplatMesh({ url: "./my-splats-lod.rad" });

    即时加载(流式传输):

    const splats = new SplatMesh({ url: "./my-splats-lod.rad", paged: true });

    13.2 处理大坐标

    启用 ExtSplats 扩展 splat 编码(32字节/splat,float32 中心坐标):

    // 加载 splat 时
    new SplatMesh({ url: "./my-splats.spz", extSplats: true });

    // 流式/池化 splats
    const spark = new SparkRenderer({ renderer, pagedExtSplats: true });

    13.3 调优 LoD 细节和性能

    调整 splat 数量预算

    • SparkRenderer.lodSplatCount:基础 splat 数量(自动根据平台设置)
    • SparkRenderer.lodSplatScale:splat 数量倍数(默认 1.0)

    形状 LoD splat 选择(中央凹渲染)

    • SparkRenderer.behindFoveate:背后 foveation 缩放(默认 0.2)
    • SparkRenderer.coneFov0:全分辨率锥角(默认 90°)
    • SparkRenderer.coneFov:降低分辨率锥角(默认 120°)
    • SparkRenderer.coneFoveate:锥边缘 foveation 缩放(默认 0.4)

    单个 SplatMesh 预算偏置

    • SplatMesh.lodScale:每个对象细节缩放(2.0 = 2x 细节)
    • SplatMesh.behindFoveate / coneFov0 / coneFov / coneFoveate:覆盖全局参数

    13.4 build-lod 命令行工具

    npm run build-lod — my-splats.ply more-splats.spz [..options]

    主要选项:

    • –quick:快速紧凑的 tiny-lod 方法(默认)
    • –quality:高质量 bhatt-lod 方法(推荐离线使用)
    • –max-sh=#:限制最大球谐函数编码 (0…3)
    • –rad-chunked:输出分块 RAD 文件用于流式传输

    使用 –rad-chunked 后会生成小头文件 .rad 和块文件 .radc,Spark 自动获取所需块。

    # 直接调用 Rust
    cd rust/build-lod
    cargo build –release
    cargo run –release — my-splats.ply more-splats.spz [..options]


    14. LoD 深度解析

    14.1 LoD 高斯 Splat 树

    “高斯 splat 树"概念上类似 MipMap:高细节 splat 被降采样为更粗糙的 splat,表示为树节点和子节点。通过合并所有 splat 到单个"根 splat”,形成树/层次结构。

    Spark LoD 渲染器在后台 worker 线程中计算树的"切割",找到满足以下条件的前沿节点集合:

    • 屏幕空间大小相似
    • 不小于一个屏幕像素
    • 最大 splat 数量为 N

    算法 O(N log N) 时间复杂度。

    14.2 Splat 降采样

    合并 splat 时:

    • 按照 Kerbl 等人的文献计算中心和 3D 协方差
    • 权重 = 透明度 × 表面积(椭球模型)
    • 颜色 = 加权和

    Spark 透明度函数

    传统高斯透明度 A 最多为 1.0,合并大量 splat 后曲线非高斯。Spark 使用替代函数:

    • D ≤ 1.0:opacity(x) = D * e^(-0.5 * x²)
    • D > 1.0:opacity(x) = e^(-0.5 * D * (|x| – (D-1))²)

    D 从存储的 uint8 解码:0…1 → 0…2,若 > 1.0 则映射到 1…5。

    14.3 Quick LoD 算法 (tiny-lod)

    基于体素八叉树:

  • 将每个 splat 放入其"自然"体素
  • 从最细层级开始,合并同体素内多个 splat
  • 逐层上升,每个体素一个 splat
  • 直到所有 splat 在 2×2×2 体素内
  • 最终合并为根节点
  • 默认使用 1.5(非 2.0)作为指数基底,产生更平滑的 LoD 过渡。

    14.4 Splat 树遍历

    从根开始,优先队列按 pixel_scale(视点角大小)排序:

  • 队列顶部 splat 的 pixel_scale < 屏幕像素大小 → 停止
  • 无子节点 → 加入输出集合
  • 替换为子节点会超过最大 splat 预算 → 停止
  • 14.5 多 Splat 树遍历

    扩展算法支持多棵 splat 树,每个根加入初始优先队列,全局平衡 splat 数量。

    14.6 LoD Worker

    树遍历在后台 WebWorker 中运行,Rust 实现编译为 WebAssembly。单个专用 lodWorker 实例执行序列化 RPC 遍历。

    14.7 预构建 SPZ LoD Splat 树

    使用 build-lod 工具输出 .RAD 文件。RAD 文件支持:

    • 流式传输
    • 可配置编码
    • 从粗糙到精细的渐进加载

    15. Dyno 着色器图系统

    dyno 着色器图系统是 Spark 的架构支柱之一,允许使用 JavaScript(和可选的 GLSL)创建自定义计算图,编译为 GLSL 在 GPU 上运行。

    15.1 核心概念

    • Dyno:具有多个类型化输入和输出的函数块
    • DynoVal<T>:值在 GPU 计算图中的类型化引用
    • DynoType:GPU 类型(内置如 "int", "float", "vec4" 或自定义如 Gsplat)

    15.2 主要用途

    Spark 在两个地方使用 Dyno:

  • 从 SplatGenerator/SplatMesh 动态生成 splat
  • 计算 splat 距离度量用于 CPU 回读和排序
  • 15.3 DynoBlock 示例

    const myGsplatGenerator = dyno.dynoBlock(
    { index: "int" }, // 输入
    { gsplat: Gsplat }, // 输出
    ({ index }) => { // 闭包:输入 → 输出
    let gsplat = dyno.readPackedSplat(myPackedSplats, index);
    const opacity = dyno.dynoConst("float", 1.0);
    gsplat = dyno.combineGsplat({ gsplat, opacity });
    return { gsplat };
    },
    );

    15.4 自定义类型

    export const defineGsplat = unindent(`
    struct Gsplat {
    vec3 center;
    uint flags;
    vec3 scales;
    int index;
    vec4 quaternion;
    vec4 rgba;
    };
    const uint GSPLAT_FLAG_ACTIVE = 1u << 0u;
    `
    );

    15.5 值类型

    类描述
    DynoLiteral 常量 GLSL 字面量
    DynoConst JavaScript 值常量
    DynoUniform 可每帧更新的统一变量
    DynoOutput Dyno 块的输出

    15.6 Dyno 构造函数选项

    选项描述
    inTypes 输入名 → DynoType 的映射
    outTypes 输出名 → DynoType 的映射
    inputs 输入值映射
    update 每次执行前调用的更新函数
    globals 全局 GLSL 定义
    statements GLSL 语句
    generate 生成 globals + statements + uniforms

    16. Dyno 标准库

    Spark dyno 系统提供覆盖 GLSL ES 3.0 大部分内置函数的标准库。

    约定:PascalCase 类名,camelCase 辅助函数。例如 new dyno.Add({ a, b }) 等价于 dyno.add(a, b)。

    16.1 数据类型转换

    函数描述
    bool(value) 转布尔
    int(value) / uint(value) 转整数
    float(value) 转浮点
    bvec<N>(value) / ivec<N>(value) / uvec<N>(value) / vec<N>(value) 转向量
    mat<N>(value) 转矩阵
    floatBitsToInt(value) / floatBitsToUint(value) float 位重解释
    intBitsToFloat(value) / uintBitsToFloat(value) int 位重解释
    pack/unpack 系列 Snorm/Unorm/Half 打包

    16.2 逻辑运算

    and, or, xor, not, lessThan, lessThanEqual, greaterThan, greaterThanEqual, equal, notEqual, any, all, select, compXor

    16.3 数学运算

    add, sub, mul, div, mod, modf, neg, abs, sign, floor, ceil, trunc, round, fract, pow, exp, exp2, log, log2, sqr, sqrt, inversesqrt, min, max, clamp, mix, step, smoothstep, isNan, isInf

    16.4 三角函数

    radians, degrees, sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, asinh, acosh, atanh

    16.5 线性代数

    length, distance, dot, cross, normalize, faceforward, reflectVec, refractVec, split, combine, projectH, extendVec, swizzle, compMult, outer, transpose, determinant, inverse

    16.6 纹理查找

    textureSize, texture, texelFetch

    16.7 变换

    函数描述
    transformPos(position, { scale?, scales?, rotate?, translate? }) 位置变换
    transformDir(dir, { scale?, scales?, rotate? }) 方向变换
    transformQuat(quat, { rotate? }) 四元数旋转

    16.8 统一变量

    dynoBool(value) // 布尔
    dynoFloat(value) // 浮点
    dynoInt(value) // 整数
    dynoVec<N>(value) // 浮点向量
    dynoMat<N>(value) // 矩阵
    dynoSampler2D(texture) // 采样器

    16.9 哈希和随机数

    使用 PCG 随机数生成器:

    函数描述
    pcgMix(value) 混合值到 uint32 种子
    pcgNext(state) 推进 PCG
    pcgHash(state) 哈希为 uint32
    hash(value) / hash2/3/4(value) 哈希为 uint/uvec
    hashFloat(value) / hashVec2/3/4(value) 哈希为 float/vec

    16.10 Splat 数据操作

    函数描述
    numPackedSplats(packedSplats) 获取 splat 数量
    readPackedSplat(packedSplats, index) 按索引读取
    readPackedSplatRange(packedSplats, index, base, count) 范围读取
    splitGsplat(gsplat) 拆分为组件
    combineGsplat(gsplat) 从组件创建
    gsplatNormal(gsplat) 获取法线
    transformGsplat(gsplat, { scale?, rotate?, translate?, recolor? }) 变换

    Gsplat 结构体字段

    字段类型描述
    center vec3 中心位置
    flags uint 标志 (0x1 = active)
    scales vec3 缩放
    index int 数组索引
    quaternion vec4 四元数旋转
    rgba vec4 颜色

    17. 控制

    17.1 SparkControls

    const controls = new SparkControls({
    canvas: HTMLCanvasElement;
    });

    renderer.setAnimationLoop((time) => {
    renderer.render(scene, camera);
    controls.update(camera);
    });

    SparkControls 内部使用 FpsMovement 和 PointerControls。

    17.2 FpsMovement

    FPS 风格移动控制(键盘+鼠标或手柄):

    const fpsMovement = new FpsMovement({
    moveSpeed?: number; // 默认 1.0
    rollSpeed?: number; // 默认 2.0
    stickThreshold?: number; // 默认 0.1
    rotateSpeed?: number; // 默认 2.0
    capsMultiplier?: number; // 默认 10.0
    shiftMultiplier?: number; // 默认 5.0
    ctrlMultiplier?: number; // 默认 0.2
    xr?: THREE.WebXRManager; // XR 手柄支持
    });

    17.3 PointerControls

    指针/鼠标/触摸控制:

    const pointerControls = new PointerControls({
    canvas: HTMLCanvasElement;
    rotateSpeed?: number; // 默认 0.002
    slideSpeed?: number; // 默认 0.006
    scrollSpeed?: number; // 默认 0.0015
    reverseRotate?: boolean; // 反转旋转
    reverseSlide?: boolean; // 反转滑动
    moveInertia?: number; // 默认 0.15
    rotateInertia?: number; // 默认 0.15
    doublePress?: ({ position, intervalMs }) => void;
    });

    17.4 GUI 配置

    import GUI from "lil-gui";

    const gui = new GUI({ title: "Settings + Controls" }).close();
    const controlOptions = { reversePointerFps: false, reversePointerPan: false };
    gui.add(controlOptions, "reversePointerFps")
    .name("Reverse Pointer FPS")
    .onChange((value) => { pointerControls.reverseRotate = value; });


    18. 性能调优

    18.1 Splat 预算建议

    平台推荐 splat 数量
    Quest 3 ≤ 1M
    Android 手机 1-2M
    iPhone 1-3M
    桌面电脑 1-5M(高端可达 10-20M+)

    18.2 关键参数调优

    maxStdDev

    // 减小值提升性能,如 VR 推荐 Math.sqrt(5)
    spark.maxStdDev = Math.sqrt(5);

    关闭抗锯齿

    const renderer = new THREE.WebGLRenderer({ antialias: false }); // 默认值

    MSAA 对 splat 渲染无益且显著增加开销。

    像素比

    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

    高 DPI 渲染可能不必要,考虑是否有足够 splats 来支持。


    19. 社区资源

    Discord

    加入 Spark Discord

    React 示例

    • spark-react-basic:基本 canvas + THREE.js + Spark
    • spark-react-r3f:React Three Fiber 声明式集成
    • spark-react-router:React Router v7 + SSR
    • spark-react-nextjs:Next.js App Router 集成

    GitHub

    https://github.com/sparkjsdev/spark

    在线工具

    • Web Playground
    • 在线查看器
    • 示例
    赞(0)
    未经允许不得转载:171主机测评 » SparkJS-完整技术文档
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址