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

THREE.js如何实现Canvas纹理在立方体表面无重复连续渲染?

问题:如何让Canvas内容在立方体面上连续渲染而非重复?

我尝试制作一个可通过Canvas元素自定义绘制内容的立方体,目前的效果是Canvas图像在每个面上重复显示。现在希望内容能连续渲染,比如当前“Hello World”文本在面边缘被截断后,能完整延伸到下一个面。请问使用简单的BoxGeometry和MeshBasicMaterial能否实现该需求?

以下是目前的实现代码:

const ratio = devicePixelRatio;
drawingCanvas.width = 600 * ratio;
drawingCanvas.height = 600 * ratio;
drawingCanvas.style.width = `${600}px`;
drawingCanvas.style.height = `${600}px`;

const ctx = drawingCanvas.getContext("2d");
if (ctx) {
  ctx.scale(ratio, ratio);

  ctx.fillStyle = "white";
  ctx.fillRect(0, 0, 600, 600);

  ctx.fillStyle = "blue";
  ctx.font = "80px Arial";
  ctx.fillText("Hello World", 600 / 2 - 15, 600 / 2 - 15);
}

const map = new THREE.CanvasTexture(
  drawingCanvas,
  THREE.UVMapping,
  THREE.ClampToEdgeWrapping,
  THREE.ClampToEdgeWrapping
);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(200, 200, 200),
  new THREE.MeshBasicMaterial({
    map,
  })
);
解答

可以实现,但需要调整UV坐标和Canvas纹理的布局,让纹理能覆盖立方体的所有面并连续显示内容,具体步骤如下:

  1. 调整Canvas尺寸和内容绘制逻辑
    默认的Canvas尺寸对应单个面,要实现跨面连续,需要把Canvas改成立方体展开图的尺寸。比如立方体每个面尺寸为200x200,展开图的宽度为4个面(左+前+右+后)、高度为3个面(顶+前+底),以此调整Canvas大小,并把内容绘制到跨面的位置:
const faceSize = 200;
const canvasWidth = faceSize * 4 * devicePixelRatio;
const canvasHeight = faceSize * 3 * devicePixelRatio;

drawingCanvas.width = canvasWidth;
drawingCanvas.height = canvasHeight;
drawingCanvas.style.width = `${faceSize * 4}px`;
drawingCanvas.style.height = `${faceSize * 3}px`;

const ctx = drawingCanvas.getContext("2d");
if (ctx) {
  ctx.scale(devicePixelRatio, devicePixelRatio);
  ctx.fillStyle = "white";
  ctx.fillRect(0, 0, faceSize * 4, faceSize * 3);

  ctx.fillStyle = "blue";
  ctx.font = "80px Arial";
  // 让文本从前面区域延伸到右面区域
  ctx.fillText("Hello World", faceSize + 100, faceSize + 100);
}
  1. 自定义BoxGeometry的UV坐标
    默认BoxGeometry每个面的UV都是[0,0]到[1,1],导致每个面单独显示完整纹理。需要手动修改每个面的UV,对应展开图的位置:
const geometry = new THREE.BoxGeometry(200, 200, 200);
const uvAttribute = geometry.attributes.uv;

// 按面顺序(右、左、顶、底、前、后)定义对应展开图的UV区域
const uvMaps = [
  // 右面:对应展开图x范围2/4~3/4,y范围1/3~2/3
  [new THREE.Vector2(2/4,1/3), new THREE.Vector2(3/4,1/3), new THREE.Vector2(3/4,2/3), new THREE.Vector2(2/4,2/3)],
  // 左面:x范围0/4~1/4,y范围1/3~2/3
  [new THREE.Vector2(0/4,1/3), new THREE.Vector2(1/4,1/3), new THREE.Vector2(1/4,2/3), new THREE.Vector2(0/4,2/3)],
  // 顶面:x范围1/4~2/4,y范围0/3~1/3
  [new THREE.Vector2(1/4,0/3), new THREE.Vector2(2/4,0/3), new THREE.Vector2(2/4,1/3), new THREE.Vector2(1/4,1/3)],
  // 底面:x范围1/4~2/4,y范围2/3~3/3
  [new THREE.Vector2(1/4,2/3), new THREE.Vector2(2/4,2/3), new THREE.Vector2(2/4,3/3), new THREE.Vector2(1/4,3/3)],
  // 前面:x范围1/4~2/4,y范围1/3~2/3
  [new THREE.Vector2(1/4,1/3), new THREE.Vector2(2/4,1/3), new THREE.Vector2(2/4,2/3), new THREE.Vector2(1/4,2/3)],
  // 后面:x范围3/4~4/4,y范围1/3~2/3
  [new THREE.Vector2(3/4,1/3), new THREE.Vector2(4/4,1/3), new THREE.Vector2(4/4,2/3), new THREE.Vector2(3/4,2/3)],
];

// 逐个更新面的UV坐标
uvMaps.forEach((faceUVs, faceIndex) => {
  for (let i = 0; i < 4; i++) {
    const uv = faceUVs[i];
    uvAttribute.setXY(faceIndex * 4 + i, uv.x, uv.y);
  }
});
uvAttribute.needsUpdate = true;
  1. 调整纹理设置
    保持纹理的包裹方式为ClampToEdgeWrapping,避免内容重复:
const map = new THREE.CanvasTexture(drawingCanvas);
map.wrapS = THREE.ClampToEdgeWrapping;
map.wrapT = THREE.ClampToEdgeWrapping;
map.repeat.set(1, 1);

最后创建Mesh:

const cube = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ map }));

完成后,Canvas上的内容会按照立方体展开图的布局连续显示在各个面上,文本可以跨面完整延伸。


内容的提问来源于stack exchange,提问作者Diogo Mendonça

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 19:46:28