把一张玩具箱的图片交给模型,让它写出 Three.js 代码,第一次预览往往就能看出箱子的轮廓。问题出现在下一步:转到背面只剩一块平板,箱盖绕着中央翻转,放进自己的场景后又悬在地面上。这些问题说明,截图相似和可用的三维资产需要分别验收。
img2threejs 提供了一套围绕参考图生成、检查和改进 Three.js 资产的工作流程。它适合愿意查看代码、调整结构,并把结果接入网页的人。下面以带箱盖的箱子为例,讨论该怎样给它任务,以及拿到代码后先检查什么。项目资料核对于 2026 年 9 月 23 日;本文的几何示例单独做过数值验证,没有把它冒充为一次完整的 img2threejs 生成实测。
图片里缺少的部分,需要你先作决定
单张图片通常不能提供物体背面、内部和真实尺寸。即使能看见一个圆柱,也难以仅凭透视判断它究竟多长。让模型自行补全这些部分,得到的会是一种合理猜测;如果任务涉及商品尺寸、装配或制造,这种猜测就不能充当测量数据。
网页插画的要求可以宽松一些。假设箱子只出现在产品介绍页的首屏,用户只能左右轻微拖动,那么正面轮廓、配色与箱盖动作比内部结构更重要。若用户可以自由旋转,背面和底部就要完整;若点击箱子能取出物品,内部空间、遮挡和开盖角度也需要明确。
因此,提供参考图时最好附上一段用途说明:资产用于网页展示,宽度按两个场景单位处理,底面落在地面,箱盖向后打开,背面允许采用简化设计。这里的两个单位是项目约定,不是从照片测出的米数。多张参考图之间如果有差异,也要说明以哪张为准,避免模型把不同版本的细节拼在一起。
项目当前的主仓库将输出定位为可检查的程序化资产代码。生成过程中可以使用几何体、材质和着色器等手段,最终仍要由宿主环境加载和运行。仓库中的展示或占位示例不能证明任意输入都能直接用于正式项目。
先给轮廓过关,再增加表面细节
img2threejs 的架构说明把制作过程拆成轮廓、结构、形态、材质、表面、光照、交互与优化等阶段。对使用者来说,阶段划分的价值在于能暂时排除不相关的问题。
第一轮只需要灰色箱体和箱盖。把摄像机放到与参考图接近的位置,检查宽高比、盖子厚度和整体重心。此时不要因为“看起来不精致”而要求加木纹、金属包边和大量螺丝。错误比例一旦藏在复杂细节里,后续每次修正都可能牵动更多节点。
第二轮再确认结构关系。箱盖应当是可独立控制的部分,锁扣是否跟随箱盖,提手能否单独旋转,都可以通过对象层级表达。给节点起有意义的名字,也方便交互代码定位;如果按钮只能依靠“第三个子对象”找到箱盖,生成器稍微重排节点就会破坏交互。
材质和光照应当放到结构之后检查。把场景临时换成均匀光照,仍能看出箱子的主要凹凸,说明形状主要由几何表达。若换一盏灯就完全认不出原图,可能只是强阴影制造了细节。对需要在不同页面复用的资产,过度依赖固定灯光会增加接入成本。
一个箱盖示例,检查旋转中心是否正确
下面使用父级 Group 表达铰链。箱体宽 2、高 1、深 0.8,箱盖绕后侧上沿旋转。为了集中解释层级关系,箱体暂时使用实心长方体,没有制作内壁、碰撞体或真实铰链。
import * as THREE from 'three';
export function createBox() {
const root = new THREE.Group();
const material = new THREE.MeshStandardMaterial({ color: 0x98704d });
const body = new THREE.Mesh(new THREE.BoxGeometry(2, 1, 0.8), material);
body.position.y = 0.5;
root.add(body);
const hinge = new THREE.Group();
hinge.name = 'lidHinge';
hinge.position.set(0, 1, -0.4);
root.add(hinge);
const lid = new THREE.Mesh(new THREE.BoxGeometry(2, 0.1, 0.8), material);
lid.position.set(0, 0.05, 0.4);
hinge.add(lid);
return { root, hinge };
}
const { root, hinge } = createBox();
hinge.rotation.x = -Math.PI / 2;
root.updateMatrixWorld(true);
箱盖的局部位置相对于 hinge。旋转 hinge 时,箱盖围绕后侧边缘抬起;如果直接旋转一个位于箱盖中心的 Mesh,看到的就是盖子在自身中央翻转。Three.js 的对象层级文档解释了局部变换与世界变换的关系;这里采用常见的父节点方法,方便在不同项目中检查。
本文用 Node.js 25.5.0、Three.js 0.180.0 检查了这段构造代码:闭合时箱体底面在 y=0,开盖前后 hinge 的世界位置不变,开到九十度时箱盖中心向上移动。浮点运算的判断采用误差容许范围。这只验证坐标关系,不能据此声称材质效果、浏览器帧率或生成质量已通过测试。复制到网页时,你仍需要场景、相机、灯光、渲染器和更新循环。
如果业务要求箱子真的能装东西,就要把实心箱体换成底板和四面侧壁,并检查打开时箱盖是否穿过后壁。结构简化应当写在验收记录里,防止后来接手的人误把演示模型当成完整容器。
接入已有页面时,检查工厂函数以外的依赖
拿到返回 Group 的函数后,先在一个只有相机与灯光的空场景加载。确认没有偷偷创建第二个渲染循环,也没有依赖原示例页面的全局变量。随后再放进业务页面,分别观察整体缩放、地面位置和阴影范围。
尺寸最好在资产边界统一处理。例如场景把一米约定为一个单位,那么箱子的尺寸应该在生成需求中说明,或者由调用者在根节点统一缩放。不要同时修改几何尺寸、内部节点比例和页面容器缩放,否则后续碰撞检测与相机取景很难复现。
还要检查资源是否随调用次数增长。若每次打开弹窗都会新建纹理和材质,关闭弹窗时仅把 Group 从场景移除,可能留下 GPU 资源。共享材质则需要由持有者管理生命周期,不能删除一个箱子时顺手释放仍供其他箱子使用的材质。这部分可以对照 Three.js 的资源清理指南处理。
项目中的 Python 检查脚本和宿主的图像观察、浏览器预览承担不同工作。脚本能够检查某些结构约定,视觉审查仍需要看渲染结果;仅仅通过脚本,不足以证明参考图还原、遮挡关系或交互体验合格。安装说明里某个组件依赖少,也不代表完整生成过程不需要其他工具。
修改需求时,把变化限制在可比较的范围
假设第一轮箱子比例合格,但箱盖开启时挡住了页面标题。先确认是相机取景问题还是模型运动范围问题:把动画停在最大角度,检查包围盒与文字区域的关系。如果只是页面布局冲突,可以调整资产容器或相机;如果开盖方向违背设计,就修改铰链,而不是把整个模型压扁来腾出空间。
向生成工具反馈时,写清当前表现和期望位置。例如“箱盖打开到九十度时,后沿保持不动,盖子位于箱体后上方”,比“动画自然一点”更容易复查。保留上一轮代码,在相同相机与光照下比较。一次同时改轮廓、材质和动画,会让你难以判断哪个修改解决了问题。
交付接口也应保持稳定。调用方如果已经通过 lidHinge 控制开盖,下一次生成就应保留这个节点名称,或者明确给出接口变更。美术调整不应悄悄改变外部代码依赖的对象层级。对于需要随机纹理或细节的实现,还可要求固定随机种子,让同一版本的截图能够复现。
把验收截图拍在真正会出问题的位置
给资产留一组固定观察角度:正面、侧面、背面,以及交互结束的位置。同一组截图使用相同相机参数,修改前后才有可比性。只挑最接近参考图的角度,会漏掉背面缺失和薄片结构。
交互测试可以围绕用户动作安排:连续开合十次,快速重复点击,动画中途离开页面,然后再次进入。检查状态是否回到约定位置,有没有创建多余监听器。十次只是便于复现的检查次数,不是稳定性认证;出现异常后还要继续定位原因。
移动端则应在真实目标尺寸下看轮廓。桌面上精细的螺丝,在手机首屏里可能只是抖动的亮点。降低这类细节通常比盲目提高分辨率更有效,但是否改善性能仍要查看实际设备的帧时间、绘制调用和内存,而不能凭模型文件大小推断。
最终交付时,保留参考图用途说明、生成代码、依赖版本和验收截图。另列出尚未制作的部分,例如“箱体内部未建模”“不含碰撞检测”。这样页面需要增加拾取、阴影或开箱动作时,开发者可以判断要改哪一层,不必从一张漂亮截图重新猜测整个资产的结构。











