Three.js教程
Shader
kiln-render
kiln-render

WebGPU 把 3GB 的医学 CT 数据渲染进了浏览器,显存只用 548MB

一个 3GB 的医学 CT 扫描,在浏览器里打开,旋转、缩放、实时调整透明度,几乎不卡。显存占用固定在 548MB,不随数据集大小变化。

这是 Kiln 的演示效果。它是一个完全跑在浏览器里的体积数据渲染框架,最近在 GitHub 上开源。

这件事为什么不容易做到

体积渲染(Volume Rendering)是医学影像和科学可视化里的标配需求——CT 扫描、显微镜扫描、地质数据,本质上都是一堆三维网格数据,需要把"里面"的结构也渲染出来,而不只是表面。

问题在于数据量。一个高精度 CT 扫描轻松超过 1GB,放进显存根本放不下。过去的解法要么是在服务器端渲染好再传图给前端,要么就是让用户装专用软件。

浏览器里做,一直是个烫手山芋。

Kiln 的做法

Kiln 的核心思路是:不把完整数据放进显存,只放当前视角需要看的部分。

整个流水线分成离线预处理和运行时两段:

离线阶段:原始体积数据被切成 64³ 体素的砖块,同时生成多个精度层级——离摄像机近的区域用精细层级,远处用粗糙层级。每块砖单独压缩存储,支持按需拉取单个砖块而不是整个文件。

运行时:GPU 里只维护一块固定大小的图集纹理(Atlas),相当于一个有限大小的缓存池。摄像机每移动一帧,系统就根据当前视角重新计算哪些砖块需要加载:视锥之外的直接跳过,视锥内的砖块按距离决定需要哪个精度层级,还没缓存的就发 HTTP Range 请求拉取对应数据。拉回来的数据在 Worker 线程里解压,再异步上传进图集。图集满了就按 LRU 原则淘汰最久没用的砖块,但最粗糙的那层始终保留,保证画面随时有兜底。

Shader 采样时通过一张间接表(Indirection Table)把逻辑坐标映射到图集里的实际位置,外部看起来就是一整块连续的体积数据,实际上随时只有一小部分在显存里。

你只是在拖动鼠标旋转模型,这条流水线在每一帧都在后台跑一遍。

作者公布了测试数字:蜥蜴 CT(2.1GB)在 M2 Mac 上首帧耗时 544ms,实际传输的数据量只有约 0.7MB,图集占用率 0.4%;山毛榉 CT(3GB+)首帧 1079ms,传输约 1.1–2.2MB,图集占用率 4%。3GB 的数据集,首帧只需要下载不到 2MB——因为任意视角下,视锥外的砖块、空气和背景区域(通过预计算的 min/max 统计提前识别)都不会触发请求。

演示用的两个数据集显存分别只占 274MB(8-bit)和 548MB(16-bit),数据集再大,这个数字不变。

WebGPU 在这里的作用

医学影像通常是 16-bit 精度,WebGL 没有原生支持,需要拆成两个 8-bit 通道再在 Shader 里合并,滤波精度有损失。WebGPU 直接支持 r16unorm 格式,硬件三线性插值原生可用。纹理上传也是异步的,WebGL 的 texSubImage3D() 每次调用都会阻塞 CPU,多块砖同时上传时瓶颈明显。这两点是 Kiln 选择 WebGPU 的实际原因。

砖块边界

每块砖存储时多留一圈像素——逻辑上是 64³,实际存的是 66³,边缘那 1 个体素用于跨砖边界的无缝插值。这个方案在 DVR 和 MIP 模式下工作良好,但等值面(ISO)对边界更敏感,目前还有已知的接缝问题。

渲染出来长什么样

支持四种渲染方式,面向不同的观察需求:

直接体积渲染(DVR) 是最常见的模式,通过调整颜色和透明度曲线,让不同密度的组织呈现不同颜色,骨骼、软组织、空腔可以同时显示在一张图里。 最大密度投影(MIP) 沿射线方向只取最高密度值,血管和骨骼这类高密度结构会特别突出,常用在血管可视化里。 等值面(ISO) 提取特定密度值对应的表面,渲染出来更接近普通 3D 模型的观感,带光照和阴影。 LOD 调试模式 用颜色标出当前帧不同区域加载的精度层级,主要是开发调试用。 界面上有传递函数编辑器,可以手动调整颜色和透明度曲线,也支持医学影像里常用的窗宽/窗位控制。

静止不动时,画面会逐帧叠加精细化(通过时间抖动和帧累积),动起来就立刻切换成交互模式,不会卡住。

数据格式和上手方式

Kiln 支持两种数据格式:

OME-Zarr 是现成可用的路径。这是医学和生命科学领域的通用开放格式,只要有一个公网可访问的 Zarr 数据集 URL,直接填进去就能加载,不需要预处理。AWS 上有一批开放的 SciVis 数据集可以直接拿来测试。

原始二进制文件 需要用项目提供的脚本先转换成 Kiln 自己的分片格式,然后部署到支持 HTTP Range 请求的服务器上(S3、CDN、甚至本地 Vite 开发服务器都可以)。

本地运行的话,需要先安装 Bun(一个 Node.js 的替代运行时):

bun install
bun run dev

浏览器要求 Chrome/Edge 113+、Safari 26+,或开启硬件加速的 Firefox 141+,这是 WebGPU 的硬性门槛,旧版本浏览器跑不了。

写在最后

Kiln 目前还处于早期,作者自己也标注了没有稳定的公共 API,是以独立查看器的形态运行的。等值面模式下砖块边界处有已知的接缝问题,还没修完。

但作为一个「WebGPU 能做到什么程度」的参考实现,它的工程方案是完整的,演示效果也能直接跑起来验证。如果你在做医学影像可视化、科学数据展示,或者单纯对浏览器端大体积数据渲染感兴趣,值得花时间看看。

GitHub:https://github.com/MPanknin/kiln-render (opens in a new tab)