← 全部作品

2026 — 设计 / WebGL / 前端

浮屿工坊

一座可以"画"出来的水彩小岛:树、房子、城堡、溪桥和动物都是笔刷,落笔那一瞬间水彩才慢慢显色。

我不想再做一次"选模型、点地面、出现"的编辑器。那样的操作是准确的,但没有手感——像在表格里填数据,不像在纸上画画。

浮屿工坊换了个前提:先把笔递给你,岛是画出来的。

浮屿工坊首屏:水彩岛屿、左侧笔刷栏与右侧岛屿手账

三条没让过的规则

比例是真的。 树 7–11 米,城堡主楼 8–11 米,兔子 0.55 米,小鹿 1.55 米,地形 192 × 192 米。做成 Q 版会更可爱,但水彩的可信度来自重量:当你把一只 0.55 米高的兔子放在 10 米高的房子旁边,画面立刻有了尺度,颜色也才站得住。

一笔就是一条命令。 撤销恢复的不只是位置,还有对象 ID、随机种子、旋转和地形高度;连续落笔时命令立即提交,不等显色动画放完。否则画快了就会丢笔。

纸纹只是配角。 很多人做"水彩风"第一反应是加一层全屏纸纹滤镜,结果是所有东西糊在一起。这里的水彩感来自物体材质本身:世界坐标的颜料场、水洗大色块、颜料颗粒、方向性干笔,纸纹只负责最后一点粗糙。

笔刷其实是一颗种子

七种笔刷——树、房屋、城堡、溪桥、动物、地形、擦除——没有一个是"复制粘贴一个模型"。

树木用 EZ-Tree 的分支算法生成,同一颗种子永远长出同一棵树;房屋在三种真实住宅模型里按种子选型、转向、贴合坡度;地形笔刷直接改高度场,然后把已有的树、房子、动物重新贴回地面。

所以岛屿可以被完整地存成一个 JSON:种子、类型、坐标、旋转、地形高度。换台电脑导入,长出来的还是同一座岛。

落笔时颜色不是"跳"出来的,而是用大约 0.52 秒像水彩一样渗出来。这个时长调过很多次:太快没有画的感觉,太慢会打断连续落笔。

把八种材质拉到同一种语言

地形、树干、叶片、房屋、石塔、动物、水面、云,本来是完全不同的东西,材质各写各的就会"拼贴感"很重。

这里共用一条管线:在世界坐标里采样一张 RGB 颜料场,用三平面投影避免 UV 接缝,再叠加水洗、颗粒、干笔和一层很轻的彩铅轮廓;只在灯窗这类高光区域做选择性柔光,并且可以一键关掉。

雪是另一层共享 uniform:按世界空间法线在朝上的表面逐渐累积。叶子保留 alpha test,所以透明的地方不会变成实心雪片——这种细节不做,一场雪下来整棵树就废了。

模型是真的,尺寸也是真的

住宅用的是 Gorgious 的三套中世纪房屋(BlendSwap,CC0),保留作者原本的米制尺寸和入口朝向,006 和 009 连地下室都没抬到地上;用 Meshopt 压到 2.43 / 1.34 / 1.65 MB。

兔子和鹿是 Tripo 生成的,保留原始几何与贴图,只按真实体型归一化。所有模型、纹理都随站点本地托管,运行时不依赖任何外部素材服务。

环境是一个 60 秒的循环

太阳六十秒走完一天,界面主题跟着日光在浅色和深色之间切换,也可以手动锁死。风影响树和水面;雨是长雨线,雪会累积;水面用反射相机每几帧更新一次,而不是每帧——这是拿一点点延迟换帧率。

声音是 WebAudio 现场生成的:背景音乐、落笔音效、风雨环境声,没有版权音乐文件。浏览器不让自动播放,就等第一次点击再淡入。

雪后:积雪覆盖屋顶、枝干与地形,叶片仍保留透明边缘

数字

项目 数值
首屏 JS 5.2MB(gzip 3.3MB)
整站部署体积 约 25MB
初始场景 44 棵树 / 7 栋房屋 / 1 座城堡 / 1 座溪桥 / 7 只动物
地形 192 × 192 米
落笔显色 0.52 秒
昼夜循环 60 秒
部署 Cloudflare Pages(静态,无后端)

一点遗憾

体积。 25MB 对首屏并不友好,兔子 5.1MB、鹿 4.1MB、塔楼几何 5.2MB 是三个大头。下一步想按需加载:先给一座空岛,模型在镜头靠近时再进来;几何用 Draco、纹理换 KTX2,目标压到 8MB 以内。

还只能"放",不能"改"。 现在可以画、可以擦、可以抬地形、可以撤销,但不能选中一栋房子改颜色或换一套模型。对象级的编辑是下一步最想补的东西。

性能没有全设备结论。 页面右下角有实时 FPS、绘制次数和三角形数,我只在几台机器上看过,不承诺所有设备 60 帧。降分辨率、关柔光、减少连续高密度落笔都能救。

致谢

EZ-Tree(MIT,Daniel Greenheck)提供了树的生成与贴图;三套住宅来自 Gorgious 在 BlendSwap 的 CC0 作品;兔子和鹿由 Tripo 生成。资源与修改记录都写在项目的 CREDITS.md 里。

→ 打开浮屿工坊