【免费下载链接】insect-world
Interactive 3D insect field guide: 63 species across 14 orders, every body procedurally generated in the browser with TypeScript + Three.js — no model or texture files in the repo. Eight species step through a full life cycle; the hoverers beat their wings.
insect-world 是一个零模型文件的交互式 3D 昆虫图鉴:63 种昆虫、14 个目,全部由 TypeScript + Three.js 在浏览器里实时程序化生成。而所有虫体的几何,几乎都架在一套自研的放样(loft)建模工具箱之上——一个 1400 来行、住在 src/three/builders/kit.ts 里的文件。本文带你读懂这套地基是如何"以点建面、以面成虫"的。
为什么是放样?3D 程序化建模的最优解
先说结论:仓库里没有一个.glb模型文件,也没有一张贴图文件。每一只虫都是从"一串截面参数"现场算出来的网格(BufferGeometry)。
这带来两个好处:
- 体积归零:63 种虫 + 18 个生活史阶段(卵/幼虫/蛹/若虫),全部几何都来自代码,仓库几乎不含二进制资产;
- 参数可复用:画成虫的同一套代码,换个参数就能画出卵、幼虫和蛹。
实现路径很直白:昆虫身体(躯干、鞘翅、角、腿节、触角节)本质上都是**"沿一条路径,截面从粗变细再闭合"的管状体**——这正是放样(lofting)的经典定义。项目把这套算法做成了loft()一个函数,再在它上面搭了spindle()、segmentedAbdomen()、leg()、antenna()等十几个"高级积木"。63 只虫,都是积木的排列组合。
放样核心:loft()的四个关键设计
核心函数在 src/three/builders/kit.ts。输入是一组截面(Section),输出是一个封闭的网格:
export interface Section { at: THREE.Vector3 | [number, number, number] // 截面中心 ry: number // 上下半径(Y 方向) rz: number // 左右半径(Z 方向) roll?: number // 绕路径切线的扭转(弧度),默认 0 }截面:把虫体抽象成"一串椭圆环"
每个截面就是"路径上某一点的一个椭圆"。把 20 个截面串起来,就是一段身体;ry/rz不等就得到扁截面——独角仙那片"外侧高、内侧低"的半个穹顶鞘翅,就是这么压出来的(见 src/three/builders/rhinoceros-beetle.ts)。
平行传输:防止截面沿路径"打转"
最容易翻车的地方:相邻截面如果各自随意定向,细长的身体会像麻花一样扭起来。loft()的解法是切线定向 + 平行传输——先求每个截面处沿路径的切线tangent,再用上一段的参考向量推当前截面的横向标架(kit.ts#L392-L406)。这个设计还有一个隐性红利:它只依赖差值向量,所以整条腿平移到哪儿,放样结果都不变——这正是后来"腿部骨架化"(62 只虫同时学会走路)能成立的前提。
法线与封口:细节决定"像不像"
- 椭圆法线按半径倒数加权:扁截面的法线若按圆算,侧光下会发暗失真(kit.ts#L418-L422);
- 极点封口:首尾截面收到一个极点(半径 0 的截面自动退化成尖),一头一尾各补一个顶点就封好了(kit.ts#L436-L450)。
地基之上:63 只虫共享的"放样积木"
loft()本身是毛坯,真正的生产力来自包在它外面的参数化部件——物种文件只需给"尺寸 + 颜色 + 姿态":
| 函数 | 一句话职责 | 位置 |
|---|---|---|
loft() | 核心:一串椭圆截面 → 封闭网格 | kit.ts#L379 |
spindle() | 纺锤体:两端收尖的椭球,昆虫的头、胸、腹节几乎都是它 | kit.ts#L470 |
segmentedAbdomen() | 分节腹部:带环沟起伏的体节,蜂/蚁/蜻蜓腹部的辨识点 | kit.ts#L559 |
leg()/legPair() | 四段嵌套关节的昆虫足,62/63 个物种共用 | kit.ts#L718 |
antenna() | 六种触角类型:丝状、棒状、膝状、鳃叶状、栉齿状、刚毛状 | kit.ts#L882 |
wingVeins() | 扇形放射的翅脉细管——鉴定"目"级别的关键特征 | kit.ts#L1057 |
chitin()/elytra()/membrane() | 几丁质、鞘翅釉质、膜翅三档材质 | kit.ts#L244 |
finalize() | 收尾:居中、开阴影、算取景半径、收集可动骨架 | kit.ts#L1293 |
以独角仙为例,它的构建只有四步:spindle()画腹部和前胸 → 22 个截面loft()出两片隆起鞘翅 →antennaPair()加鳃叶状触角 →finalize()收尾(src/three/builders/rhinoceros-beetle.ts)。把同样的四步换个参数,就换了一只虫——这就是"地基"的威力:改loft()一处,63 只虫同时受益;加一只新虫,只写参数。
健壮性保证:让放样工具永不出 NaN
放样工具箱的地基测试(src/three/builders/tests/kit.test.ts)专门钉死了几类最容易炸的退化输入:
- 路径首尾重合:切线方向退化 → 强制兜底轴,不产生
NaN; - 竖直路径:参考向量与切线平行时自动换轴;
- 零半径截面:尖端退化不炸;
- 扁截面法线:逐顶点断言仍是单位向量;
- 输入不足 2 个截面:直接抛错,而不是静默产出坏几何。
这种"退化输入逐个钉死"的思路,加上全项目 4000 多个测试,构成了"所有物种都建在 loft 之上,这里出问题就是全线崩"的防线。
贴图也是"放样"出来的?零文件表面细节
顺带一提,表面微观层同样零文件:src/three/builders/surface.ts 用 Canvas 2D 在运行时现画刻点(甲虫头部的随机圆坑)与纵沟纹(鞘翅上的平行沟槽)法线图,种子化 PRNG 保证任何机器上生成同一张图。几何放样负责"形状",程序贴图负责"质感",两者合起来才是这只虫。
总结:一个 loft 函数,撑起一座昆虫馆
insect-world 的放样工具箱给"3D 程序化建模"提供了三个可直接抄作业的启示:
- 先做地基,再做生态:把
loft()的边角情况全部钉死后,上层 63 个物种文件几乎不再写几何算法; - 参数即物种:形态差异全部收敛为参数(半径曲线、扁度、姿态角),代码结构完全同构;
- 测试即门禁:退化输入、镜像对称、姿态不变性都有测试守着,"悄悄改坏"无处遁形。
想自己动手?按这个顺序读源码最省力:src/three/builders/kit.ts 的loft()→spindle()→leg(),再挑 src/three/builders/ladybird.ts 或 src/three/builders/rhinoceros-beetle.ts 看一只虫的完整装配,最后翻翻 docs/roadmap.md 了解这座昆虫馆是怎么一路扩建到 63 种的。
【免费下载链接】insect-world
Interactive 3D insect field guide: 63 species across 14 orders, every body procedurally generated in the browser with TypeScript + Three.js — no model or texture files in the repo. Eight species step through a full life cycle; the hoverers beat their wings.
相关推荐
insect-world是如何炼成的:一只模型文件都没有的63种昆虫3D图鉴
insect world是如何炼成的:一只模型文件都没有的63种昆虫3D图鉴 🪲 insect world 是一个可交互的 3D 昆虫图鉴:63 种昆虫覆盖
存储吃紧要删微信记录?WeChatMsg免费本地导出,把聊天永久留下来
存储吃紧要删微信记录?WeChatMsg免费本地导出,把聊天永久留下来 在家庭群里翻上半天,找不到妈妈说过的那句"记得吃饭";想查工作群半年前发出的报价,记录只
为什么仓库里没有模型文件:insect-world 3D昆虫图鉴的程序化生成架构
为什么仓库里没有模型文件:insect world 3D昆虫图鉴的程序化生成架构 insect world 是一个运行在浏览器里的可交互 3D 昆虫图鉴:63
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考