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

Three.js技术咨询:能否为平面/立方体分区域应用双纹理?

在Three.js平面/立方体上实现分区域纹理

当然可以在同一个平面或立方体上应用多种纹理!针对你想把平面上下半部分分开用不同纹理的需求,我给你分享两种高效的实现方式:

方法一:自定义着色器(ShaderMaterial)—— 推荐动态场景使用

这种方法性能最优,适合需要动态调整纹理或有交互需求的场景。核心思路是在片元着色器中根据UV坐标的Y值判断当前像素处于平面的上半还是下半区域,然后分别采样对应的纹理。

直接修改你现有代码的material部分即可,完整示例如下:

var geometry = new THREE.PlaneGeometry( 2, 2 );
var loader = new THREE.TextureLoader();

// 加载两种目标纹理
var textureTop = loader.load('textures/stone.png');
var textureBottom = loader.load('textures/your-second-texture.png'); // 替换成你的第二张纹理路径

// 保持你原本的纹理重复配置
textureTop.wrapS = THREE.RepeatWrapping;
textureTop.wrapT = THREE.RepeatWrapping;
textureTop.repeat.set(4, 4);

textureBottom.wrapS = THREE.RepeatWrapping;
textureBottom.wrapT = THREE.RepeatWrapping;
textureBottom.repeat.set(4, 4);

// 自定义着色器材质
var material = new THREE.ShaderMaterial({
  uniforms: {
    textureTop: { value: textureTop },
    textureBottom: { value: textureBottom }
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform sampler2D textureTop;
    uniform sampler2D textureBottom;
    varying vec2 vUv;
    
    void main() {
      vec4 color;
      // 根据UV的Y值划分上下区域(0.5是中间分界点)
      if(vUv.y > 0.5) {
        // 调整上半区UV,让纹理按设置的repeat参数正常重复
        color = texture2D(textureTop, vec2(vUv.x, (vUv.y - 0.5) * 2.0));
      } else {
        // 调整下半区UV,保证纹理重复效果一致
        color = texture2D(textureBottom, vec2(vUv.x, vUv.y * 2.0));
      }
      gl_FragColor = color;
    }
  `
});

var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

这里特意调整了UV坐标,确保两种纹理在各自区域内都能按照你设置的repeat参数正常重复,不会出现拉伸或错位的问题。

方法二:Canvas合成纹理—— 适合静态场景使用

如果你的纹理不需要动态变化,也可以先在Canvas上绘制好上下分区域的纹理,再把Canvas作为Three.js的纹理使用。这种方法更直观,不需要写着色器代码。

示例代码如下:

var geometry = new THREE.PlaneGeometry( 2, 2 );
var loader = new THREE.TextureLoader();

// 用Promise确保两种纹理都加载完成再处理
Promise.all([
  new Promise(resolve => loader.load('textures/stone.png', resolve)),
  new Promise(resolve => loader.load('textures/your-second-texture.png', resolve))
]).then(([textureTop, textureBottom]) => {
  // 创建Canvas元素,设置合适的分辨率
  const canvas = document.createElement('canvas');
  canvas.width = 1024;
  canvas.height = 1024;
  const ctx = canvas.getContext('2d');

  // 绘制上半部分纹理
  ctx.drawImage(textureTop.image, 0, canvas.height/2, canvas.width, canvas.height/2);
  // 绘制下半部分纹理
  ctx.drawImage(textureBottom.image, 0, 0, canvas.width, canvas.height/2);

  // 将Canvas转为Three.js纹理
  const combinedTexture = new THREE.CanvasTexture(canvas);
  combinedTexture.wrapS = THREE.RepeatWrapping;
  combinedTexture.wrapT = THREE.RepeatWrapping;
  combinedTexture.repeat.set(4, 4);

  // 创建材质和网格
  var material = new THREE.MeshBasicMaterial({ map: combinedTexture });
  var mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);
});

这种方法的好处是不用接触着色器,调试起来更简单,但如果需要频繁更换纹理,性能会比着色器方法差一些。

如果是给立方体的某个面做分区域纹理,只需要确保该面的UV坐标范围是0-1,然后套用同样的逻辑处理就行啦~

内容的提问来源于stack exchange,提问作者Matías Cánepa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:36:14