如何分割网络图片为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
相关产品推荐
相关产品推荐

