# Tiki3d

> 一个投入生产的 3D 高斯泼溅（Gaussian Splatting）制作与播放平台：在浏览器里把真实场地当作照片级点云来浏览、编辑，并编排导览。

- URL: https://oliverz.dev/zh/work/tiki3d
- Author: Oliver Zhang (https://oliverz.dev)
- Language: zh-Hans
- Category: 3D Web / 高斯泼溅
- Year: 2026
- Role: Web3D 与全栈工程师
- Client: Soarscape Ltd
- Stack: PlayCanvas, 3D Gaussian Splatting, WebGL, TypeScript, Scene Editor
- Live: https://c.tiki3d.com/

## 项目背景

Soarscape 是一家 3D 测绘公司，有自己的算力机房，也有自己的软件。问题就出在这套软件只能跑在 Windows 上。创始人想把业务做到海外，需要一个能拿给市场看的东西，而竞争对手也在同一时间朝同一个方向走。要装才能用的桌面软件，拿去当推介材料并不好使。

他找我来做 Tiki3d，也就是浏览器版的产品：上传一份高斯泼溅采集，清理干净，编排一段镜头导览，发出一个链接。3D Web 这部分由我从头负责到尾，从渲染管线、制作工具到客户看到的查看器，前后大约四个月上线。

![查看器在 WebGPU 上运行一个 210 万个 splat 的场景](https://oliverz.dev/works/tiki3d/scene-viewer.png?v=18a45de9 "查看器在 WebGPU 上运行一个 210 万个 splat 的场景，LOD 和 splat 预算作为用户控件开放出来")

## 有意照搬 PlayCanvas

查看器在做任何高级功能之前，首先得手感对。轨道旋转的阻尼、缩放曲线、松开鼠标后镜头怎么停稳，这些没调好，再逼真的场景也会让人觉得是坏的。我见过有人说一个渲染器「卡」，其实帧率没问题，只是缓动不对。

我没有靠猜去调这些常量，而是去读 PlayCanvas 查看器的源码，把决定镜头手感的那部分直接移植进我们的代码库。他们团队已经解决了这个问题，代码就摆在那里，而我的任务是四个月内交付一个产品。与其重新发现一遍，我宁可直接继承一个已经解决的问题。模型编辑器也是同样的思路：选择工具照着官方编辑器的行为重写，没有另起一套自己的交互约定。

这个决定的代价是实打实的。我们背着一份不是自己写的代码，也没法靠升级版本号来更新，PlayCanvas 一改镜头内部实现，我们就得手动 diff。这个取舍是我想清楚之后才做的。

## 直接在点云上做选择工具

模型编辑器提供 Lasso（套索）、Brush（笔刷）、Polygon（多边形）三种选择方式，外加 Delete 和 Hide。按住 Shift 是加选，按住 Ctrl 是减选，笔刷大小可以用滑块调。这些操作都直接作用在 splat 数据本身上，中间没有代理网格。

![在 splat 点云上用笔刷选择](https://oliverz.dev/works/tiki3d/model-editor.png?v=90a76555 "黄色是当前选区。Shift 加选，Ctrl 减选，「Save as New」（另存为新模型）会写出一个单独的模型，不会覆盖原始采集")

编辑采集数据不光鲜，却天天都要做。真实扫描拿回来，总有一道想去掉的围栏、一扇不能公开的邻居家窗户，或者某次没扫好留下的浮空噪点。这些都得有人拿着笔刷去处理，算法替代不了。

我最在意的是「Save as New」这个按钮。一下删掉一百万个 splat 是破坏性的操作，测绘人员删过头了，不应该连原始采集都丢掉。存成一个新模型，原始数据就保持不动，大家才敢放手去试，而不是小心翼翼。

## 测量，这才是整个商业逻辑

市场团队要的是一段好看的导览。测绘人员要知道两个东西之间相距多远，这个数字决定了这个平台是不是个玩具。

![两个选点之间测得 10.96 米](https://oliverz.dev/works/tiki3d/scene-measurement-tool.jpeg?v=3ced8802 "在点云上点两个点，读出距离。同样是那个 210 万 splat 的场景，没有用任何代理几何体")

点击测量直接在 splat 点云上进行，所以距离来自采集到的几何数据，而不是一个简化过的替身。正是这个功能，让 Tiki3d 从一个查看器变成了测绘团队愿意付钱的东西。

## 动画是数据，不是录像

场景可以播放镜头导览，导览的制作功能是我在场景编辑器里做的。一段导览由若干组组成，每组包含镜头位置和 lookat 目标，每一步都有自己的过渡设置，比如 ease-in-out 这类缓动。

把导览存成结构化数据而不是烘焙成视频，带来了两个好处。一是查看器和编辑器读的是同一种格式，作者在编辑器里拖着预览的，就是客户最终看到的。二是导览只是一小段 JSON，和动辄很大的采集数据相比微不足道，改一个镜头运动，存储和重新导出几乎没有成本。

## 格式、预算，以及枯燥的可靠性工作

查看器和编辑器支持分块资源（我们自己的格式）、SOG、SOG-LOD 和 PLY。可以把多个模型合并到一个场景里，在编辑器里单独调整每个模型的位置和旋转。

这里有两个决定，再来一次我还会这么做。第一是上传校验：文件在上传时就检查，有问题的采集在上传那一步就被拒绝，而不是等到后面在渲染循环里报错，那时候的报错对用户来说毫无意义。第二是把性能参数开放出来。查看器会显示当前设备、LOD 预设和 splat 预算，硬件比较弱的操作人员可以调低预算，保住一个能用的场景，而不是眼看着标签页崩掉。Colorize LOD 作为调试视图一直留在构建里，因为用眼睛看 LOD 的问题，比靠推理快得多。

![模型库，每个模型都能单独编辑和删除](https://oliverz.dev/works/tiki3d/model-list.png?v=713c558d "模型带有 Ready 状态，界面可以区分已经处理完的采集和还在处理中的采集")

## 如果重来，我会改什么

移植过来的镜头代码是我一直放不下的地方。它让我们按时上线，现在也成了整个系统里最难移植的部分。如果重做 Tiki3d，我会从一开始就用我们自己的接口把这部分行为包起来，让借来的实现待在一个可以整体替换的盒子里，而不是散落在查看器的各处。

其余的我都会保留，尤其是把编辑和测量当作一等功能来做的决定。网页上的大多数 splat 项目，做到在浏览器里渲染出照片级效果就停了。让人能裁剪、测量、编排这个场景，才让 Tiki3d 成为 Soarscape 能拿到国际市场上的产品，而不只是又一个 demo。
