小院

小院 · 文章

用 three.js 和 Blender 脚本,在浏览器里建一座寺院

小院是一个纯静态网页:一个 index.html,一组 ES Module,three.js 通过 importmap 加载,没有构建步骤,没有后端,没有账号。这篇讲它是怎么做出来的,给想做类似东西的人。

尺度先定下来

一切从一个数开始:小和尚身高一米八,场景里一个单位就是一米。大雄宝殿按这个尺度定为四十米宽、四十八米深、台基两米、地面到脊顶约二十九米,中门九米二宽。其余三十一座殿的位置和体量,都是对着一张寺院平面图、以大雄宝殿为基准换算出来的,写在一个数据文件里:每座殿一行,坐标、朝向、宽深、墙高、几重檐、什么屋顶。改布局只改这个文件。

大殿用 Blender 脚本建,其余殿在浏览器里现场生成

大雄宝殿是全寺的主角,用 Blender 的 Python 脚本建模:重檐歇山顶、斗拱、彩画额枋、台基、栏杆、台阶、御路,全部由脚本参数化生成,烘了环境光遮蔽,导出 glb。这样改一个参数就能重新生成,不用手动建模。

其余三十一座殿不值得每座单独建模,就在浏览器里现场生成:给定宽深、墙高、几重檐、屋顶类型(歇山、悬山、硬山、攒尖),代码拼出楼身、屋顶、斗拱和匾额。同类构件合并成一次绘制,否则三十一座殿的瓦当、斗拱会有上万个绘制调用。

殿内也是生成的:墙、门、柱子、藻井、蒲团、神像。神像用的是抽象造型,不画五官。观音像例外,是把一个 AI 生成的高模用脚本减面、烘贴图做成的。

贴图全部用 canvas 画

除了观音像烘出来的两张贴图,站里没有一张图片文件。瓦、砖、木纹、彩画、经文屏板、藻井,全部在页面加载时用 2D canvas 画出来再交给 three.js。好处是没有版权问题、体积为零、改起来快;代价是首次进入要多花几百毫秒画图。

树、天空、后期

树是程序化的:香樟、银杏、桂花、竹子,四种,近处完整、远处精简两级。天空和太阳是程序化的天光环境,傍晚的色温是调出来的。后期用了环境光遮蔽、光晕和暗角,三者加起来决定了「傍晚的院子」这个气质。

小和尚

人物是自己写的骨骼层级:双脚、四肢反向动力学、走跑步态、跟着腿摆的袍子、连续弯曲的袖子。礼拜的三拜动作(起身、俯身、伏地、翻掌)按阶段算出各关节的角度。寻路用网格 A*,全寺是一张六百乘七百六十六的格子图,五百多个碰撞矩形,点地面就能绕开树、石灯和房子走过去。

声音

没有音频文件。木鱼、珠子碰撞、远处的钟、磬,全部用 WebAudio 现场合成。没有持续的背景音乐,殿里安静,偶尔有一声远钟。

性能和加载

做到后面主要在和性能打交道:太阳阴影每个显示帧只更新一次;殿宇按空间分块合批,保留剔除;念珠模式完全盖住画面后暂停背后的 3D 绘制;人物几何缓存,矩阵按需更新。三个 glb 模型用 meshopt 压缩,从十八兆压到五兆半;three.js 自托管,不依赖 CDN。

没有的东西

没有账号、支付、聊天、排行、联机。殿里的灰色陪坐者是本地假人。这些不是没来得及做,是不打算做。小院想做的只是一个安静的地方,技术上能少一样就少一样。

做法都写在上面了。想做类似东西的人,从「一个单位等于一米」开始就对了。

本文讲的是通行的说法,不引用具体寺庙和人物;小院里的殿与像均为原创造型,不作任何承诺。

进院子,看看它跑起来的样子