img2threejs 出几何骨架、材质光照留给 Three.js 手调

文章导读
img2threejs 这类单图生成三维的工具,输出物里最稳的部分通常是顶点、UV 和对象层级;材质和光照多半只是按原图猜出来的一版贴图加基础色,直接放进 Three.js 场景里,常见问题就是整体发灰、明暗层次消失、塑料感重。更可行的做法是把流程切成两段:转换阶段只追求几何骨架干净、尺度与朝向正确;材质颜色、粗糙度、金属度、贴图通道和灯光全部在 Three.js 里重做。判断标准也不复杂——如果
📋 目录
  1. Ⅰ 在导入后的场景里分拣几何可留与材质需换的部分
  2. Ⅱ 把几何骨架提取成干净的层级
  3. Ⅲ 重设材质通道:颜色、粗糙度、金属度、法线
  4. Ⅳ 补光照:环境光、方向光与阴影范围的设置
  5. Ⅴ 把重做后的参数固化成可复用配置
A A

img2threejs 这类单图生成三维的工具,输出物里最稳的部分通常是顶点、UV 和对象层级;材质和光照多半只是按原图猜出来的一版贴图加基础色,直接放进 Three.js 场景里,常见问题就是整体发灰、明暗层次消失、塑料感重。更可行的做法是把流程切成两段:转换阶段只追求几何骨架干净、尺度与朝向正确;材质颜色、粗糙度、金属度、贴图通道和灯光全部在 Three.js 里重做。判断标准也不复杂——如果原图的观感依赖透明、发光、强反射或织物质感,转换阶段基本还原不了,不如先放弃材质,等进了引擎再手调。

适用场景:用单图生成展示或交互用的基础模型,几何比材质更重要。操作动作:转换阶段只保留几何与 UV,进 Three.js 后整片替换材质并重新布光。验证方式:切到无光照材质看几何,单开基色贴图看颜色,开关阴影看明暗是否贴合原图。风险边界:透明、发光、织物这类外观无法靠一张基础色贴图还原,需要按最终渲染目标决定是否换模型或补多视角。

在导入后的场景里分拣几何可留与材质需换的部分

先别急着调观感,第一步是把导入后的对象按「能复用」和「必须替换」分开。判断顺序建议是:几何属性 → 贴图引用 → 材质参数。几何只要顶点、UV、法线齐全就可以留下;材质部分默认全部视为待替换,哪怕看起来还行,也先记下来再决定。

内容转换阶段确定进 Three.js 后手调
几何顶点、UV、法线、层级、单位与朝向只做归一化、合并、居中
基础色贴图当作参考图,不当作最终色重新指定 map 并设置 colorSpace
粗糙度 / 金属度通常是固定猜测值按材质类型重设标量或贴图
法线贴图有烘焙结果时保留强度、通道方向按需修正或删除
光照与阴影一般没有有效信息完全重建,阴影相机跟随包围盒

对象树检查可以直接遍历打印,不依赖任何编辑器:

scene.traverse((o) => {
  if (!o.isMesh) return;
  const m = Array.isArray(o.material) ? o.material[0] : o.material;
  console.log(o.name, o.type,
    Object.keys(o.geometry.attributes).join(','),
    m && m.map ? (m.map.image && m.map.image.src) || m.map.name : 'no-map');
});

网格命名建议在导入后立刻统一前缀:主体用 body_、配件用 prop_、环境或底座用 env_。命名稳定之后,后面的材质替换和层级合并才有可靠的匹配依据。贴图引用路径的查看方式有两种:运行时看 material.map.image.src 或 material.map.name;静态检查则打开 glTF 文件,看 images 数组里的 uri 字段。如果 uri 是内嵌 base64 或绝对路径,导入后贴图容易丢失,需要先落到相对路径再重新加载。

把几何骨架提取成干净的层级

层级脏是后面所有调参痛苦的来源。整理规则可以先按这几条执行:根节点归一到原点,用包围盒计算并记录实际尺寸;统一单位与上方向,别让一部分模型以厘米、另一部分以米存在;把只在展示中静态存在的重复小件合并,把需要独立变换或独立材质的部分拆开。合并与拆分的判断依据是「是否共享材质、是否需要单独动画、是否影响阴影轮廓」,三者都满足静态同材质时再合并,否则保留独立节点。

  1. 确认没有孤立空节点和零面积面片。
  2. 确认每个 mesh 都有 UV,法线已计算,没有依赖反缩放。
  3. 记录包围盒尺寸、原点位置和上方向,写进配置。
  4. 合并后重新检查材质索引,避免合错材质组。
  5. 导出前把命名冻结,后续脚本按名字匹配,不再依赖对象顺序。

重设材质通道:颜色、粗糙度、金属度、法线

发灰最常见的原因不是光照不够,而是色彩空间设置错了:基色贴图需要标记为 sRGB,粗糙度、金属度和法线属于数据通道,必须保持线性。塑料感则多半来自金属度被错设成非零、粗糙度又偏低。可以先给一个保守起点,再用页面上的调试面板逐项改。

img2threejs 出几何骨架、材质光照留给 Three.js 手调
baseTex.colorSpace = THREE.SRGBColorSpace;
roughTex.colorSpace = THREE.NoColorSpace;
normalTex.colorSpace = THREE.NoColorSpace;

const mat = new THREE.MeshStandardMaterial({
  color: 0xffffff,
  map: baseTex,
  roughness: 0.9,   // 先接近全哑光,用来排除贴图问题
  metalness: 0.0,
  normalMap: normalTex,
  normalScale: new THREE.Vector2(0.8, 0.8),
  roughnessMap: roughTex,
  metalnessMap: metalTex,
  envMapIntensity: 0.5
});

验证方式按顺序来:先用 MeshNormalMaterial 确认几何和法线没有翻转;再只挂基色贴图、把粗糙度设成 1、金属度设成 0,看颜色是否正常;最后才逐个加回粗糙度、金属度和法线贴图,每加一项对比一次页面效果。如果单挂基色仍然发灰,问题基本在色彩空间或贴图本身,而不是灯光。

补光照:环境光、方向光与阴影范围的设置

光照阶段不要一次堆很多灯。半球光做环境打底,一盏方向光做主光,需要时再加一盏弱补光。阴影相机范围按模型包围盒给,避免阴影糊或者直接被裁掉。

const hemi = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);

const key = new THREE.DirectionalLight(0xffffff, 2.0);
key.position.set(3, 5, 2);
key.castShadow = true;
key.shadow.mapSize.set(1024, 1024);

const box = new THREE.Box3().setFromObject(root);
const size = box.getSize(new THREE.Vector3()).length();
key.shadow.camera.left = -size;
key.shadow.camera.right = size;
key.shadow.camera.top = size;
key.shadow.camera.bottom = -size;
key.shadow.camera.near = 0.1;
key.shadow.camera.far = size * 4;
key.shadow.bias = -0.0005;

renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;

和原图对比验证时,把原图作为纯平面参考图并排放在画布旁,或者临时设为背景,而不是凭记忆调。检查三件事:主光方向是否和原图亮面一致、暗部有没有被半球光抬得太平、接触阴影的位置是否正确。阴影出现条纹就调 shadow.bias,边缘发虚就提高 mapSize 或收紧阴影相机范围。

把重做后的参数固化成可复用配置

调完之后不要只留在代码里。把几何归一化、材质标量、光照和渲染设置导成一个对象存盘,下次遇到同类单图直接覆盖默认值,再只改差异项。

const preset = {
  geometry: { unitScale: 1, upAxis: 'Y', mergeByMaterial: true },
  material: { roughness: 0.9, metalness: 0.0, envMapIntensity: 0.5, normalScale: 0.8 },
  light: { hemi: 0.6, keyIntensity: 2.0, keyPosition: [3, 5, 2], shadowMapSize: 1024 },
  render: { toneMapping: 'ACESFilmic', exposure: 1.0 }
};

复用时需要重新调整的点包括:包围盒尺寸变化带来的阴影相机范围、贴图相对路径、UV 布局是否与新模型一致、金属度与粗糙度是否仍适配新图,以及曝光值是否因为背景色变化而偏亮或偏暗。几何归一化和渲染管线的设置通常可以原样带走,材质标量建议每次按新的参考图确认一遍,不要默认沿用。