我不想再做一次"选模型、点地面、出现"的编辑器。那样的操作是准确的,但没有手感——像在表格里填数据,不像在纸上画画。
浮屿工坊换了个前提:先把笔递给你,岛是画出来的。

三条没让过的规则
比例是真的。 树 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 里。