You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何分割网络图片为Three.js对象纹理及解决图片参数获取问题

嘿,作为HTML/JS和Three.js的新手,你的这两个问题其实都是入门阶段非常典型的小坑,我来给你一步步拆解解决方法~

问题1:分割网络图片并作为纹理应用到Three.js对象上

分割图片作为Three.js纹理,最高效的方式是通过控制纹理的UV坐标来实现(不需要实际裁剪图片文件),当然也可以用Canvas先裁剪再生成纹理,下面分别讲两种方法:

方法一:通过纹理的offset和repeat属性实现(推荐)

原理是让纹理只显示原图的某一块区域,核心是设置offset(偏移量)和repeat(重复比例):

  • 假设把图片分成cols列、rows行
  • 每一块的宽度占原图的1/cols,高度占1/rows
  • 对应第x列(从0开始)、第y行的块,偏移量是x/cols(水平)和y/rows(垂直)

示例代码:

// 初始化Three.js场景(基础代码省略,假设你已经有scene、camera、renderer)
const textureLoader = new THREE.TextureLoader();

// 加载网络图片(注意跨域问题,图片服务器需允许跨域)
textureLoader.load('你的图片URL', (texture) => {
  // 配置分割参数:2列2行,取第1列第0行的块
  const cols = 2;
  const rows = 2;
  const targetCol = 1;
  const targetRow = 0;

  // 设置纹理参数,只显示目标块
  texture.offset.set(targetCol / cols, targetRow / rows);
  texture.repeat.set(1 / cols, 1 / rows);
  texture.wrapS = THREE.ClampToEdgeWrapping; // 防止边缘出现重复纹理
  texture.wrapT = THREE.ClampToEdgeWrapping;

  // 创建应用该纹理的对象(比如平面)
  const geometry = new THREE.PlaneGeometry(5, 5); // 平面尺寸可根据需要调整
  const material = new THREE.MeshBasicMaterial({ map: texture });
  const mesh = new THREE.Mesh(geometry, material);
  
  scene.add(mesh);
  renderer.render(scene, camera);
});

方法二:用Canvas裁剪图片后生成纹理

如果你需要实际裁剪出图片的某一块(比如保存为新图片),可以先通过Canvas绘制裁剪区域,再用Canvas生成Three.js纹理:

const img = new Image();
img.crossOrigin = 'anonymous'; // 跨域设置
img.onload = () => {
  // 创建Canvas,设置裁剪区域尺寸
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const pieceWidth = img.width / 2; // 分成2列,每列宽度
  const pieceHeight = img.height / 2; // 分成2行,每行高度
  
  canvas.width = pieceWidth;
  canvas.height = pieceHeight;
  
  // 绘制裁剪后的图片(取第1列第0行的块)
  ctx.drawImage(img, pieceWidth * 1, pieceHeight * 0, pieceWidth, pieceHeight, 0, 0, pieceWidth, pieceHeight);
  
  // 用Canvas生成Three.js纹理
  const texture = new THREE.Texture(canvas);
  texture.needsUpdate = true; // 手动更新纹理
  
  // 创建对象应用纹理(省略场景添加代码)
  const geometry = new THREE.PlaneGeometry(5, 5);
  const material = new THREE.MeshBasicMaterial({ map: texture });
  const mesh = new THREE.Mesh(geometry, material);
};
img.src = '你的图片URL';
问题2:获取网络图片宽高时img.width/img.height始终为undefined

这个问题的核心原因非常明确:你在图片还没加载完成的时候就去读取宽高了!

浏览器加载网络图片是异步操作,设置img.src后,浏览器会在后台下载图片,而代码会继续往下执行,此时图片还没加载完成,自然没有width和height属性。

正确的解决方法:监听图片的load事件

必须在图片完全加载完成后,再读取宽高和处理Three.js对象,示例代码如下:

document.getElementById('submit').onclick = function(){
  const imageURL = document.getElementById('imgUrlInput').value; // 假设你有输入框存图片URL
  const img = new Image();
  
  // 关键1:跨域设置(如果图片来自其他域名,必须加这个,且服务器允许跨域)
  img.crossOrigin = 'anonymous';
  
  // 关键2:监听load事件,图片加载完成后再执行后续逻辑
  img.onload = function() {
    // 这里才能正确获取图片宽高!
    console.log('图片宽度:', img.width, '图片高度:', img.height);
    
    // 用宽高调整BoxGeometry的尺寸(除以100是缩小比例,避免模型太大)
    const geometry = new THREE.BoxGeometry(img.width / 100, img.height / 100, 2);
    
    // 用加载好的图片生成纹理
    const texture = new THREE.Texture(img);
    texture.needsUpdate = true; // 从已加载的Image创建纹理,需要手动更新
    
    // 创建材质和立方体对象
    const material = new THREE.MeshBasicMaterial({ map: texture });
    const box = new THREE.Mesh(geometry, material);
    
    // 添加到场景(假设你已经初始化了scene)
    scene.add(box);
  };
  
  // 可选:监听错误事件,处理图片加载失败的情况
  img.onerror = function() {
    alert('图片加载失败,请检查URL或跨域设置!');
  };
  
  // 最后设置src,确保onload事件已经绑定
  img.src = imageURL;
};

额外注意点:跨域问题

如果图片来自其他域名,必须设置img.crossOrigin = 'anonymous',同时图片服务器的响应头要包含Access-Control-Allow-Origin(允许你的域名或所有域名访问),否则不仅无法获取宽高,还无法将图片作为Three.js纹理使用(会触发跨域错误)。


内容的提问来源于stack exchange,提问作者Nicholas Charles

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:59:07