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

Three.js:如何将纹理白色部分设为透明实现遮罩裁剪效果?

实现Three.js纹理白色部分透明的遮罩裁剪效果

嘿,我之前也折腾过类似的需求,给你几个靠谱的解决方案,应该能解决你的问题:

一、先排查alphaTest失效的原因

你之前调alphaTest没成功,大概率是这几个坑:

  • 你的纹理没有alpha通道:如果只是普通的RGB纹理(白色是RGB(255,255,255)),alphaTest根本没东西可检测
  • 材质没开启transparent: true:即使有alpha通道,不开启透明属性也不会生效
  • 没关闭depthWrite: false:遮罩平面会写入深度缓冲区,导致背景物体被遮挡,看起来完全没透明效果

二、方案1:自定义着色器实现白色像素丢弃

如果你的纹理是纯黑白图(没有alpha通道),最直接的方式是用自定义着色器,判断像素颜色是否接近白色,直接丢弃这些像素:

// 加载遮罩纹理
const textureLoader = new THREE.TextureLoader();
const maskTexture = textureLoader.load('你的遮罩纹理路径');

// 自定义遮罩材质
const maskMaterial = new THREE.ShaderMaterial({
  uniforms: {
    maskTexture: { value: maskTexture }
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform sampler2D maskTexture;
    varying vec2 vUv;
    void main() {
      vec4 texColor = texture2D(maskTexture, vUv);
      // 这里的阈值可以根据你的纹理调整,确保白色被准确识别
      if (texColor.r > 0.9 && texColor.g > 0.9 && texColor.b > 0.9) {
        discard; // 丢弃白色像素,实现透明
      }
      // 非白色像素的颜色(只要不discard就会遮挡背景,颜色可随意设置)
      gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
    }
  `,
  transparent: true,
  depthWrite: false // 关键:关闭深度写入,避免遮挡背景物体
});

// 把材质应用到遮罩平面
const maskPlane = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), maskMaterial);
scene.add(maskPlane);

三、方案2:使用alphaMap(需预处理纹理)

如果你的纹理可以改成灰度反转图(白色变黑色,黑色变白色),可以用Three.js自带的alphaMap属性快速实现:

const maskTexture = textureLoader.load('反转后的遮罩纹理');
const maskMaterial = new THREE.MeshBasicMaterial({
  alphaMap: maskTexture,
  alphaTest: 0.5, // 阈值控制透明过渡的严格程度
  color: 0x000000,
  transparent: true,
  depthWrite: false
});

alphaMap会用纹理的红色通道作为alpha值,反转后的黑色(r=0)对应透明,白色(r=1)对应不透明,刚好符合你的需求。

四、方案3:模板缓冲区(Stencil Buffer)实现精准遮罩

如果需要更精准的遮罩(比如复杂不规则形状),模板缓冲区是更专业的选择,原理是先把遮罩形状写入模板缓冲,再让背景物体只在模板标记的区域渲染:

// 1. 开启渲染器的模板缓冲
renderer.stencilBuffer = true;

// 2. 创建遮罩专用材质(只写模板缓冲,不写颜色和深度)
const maskWriteMaterial = new THREE.MeshBasicMaterial({
  colorWrite: false,
  depthWrite: false,
  stencilWrite: true,
  stencilFunc: THREE.AlwaysStencilFunc, // 总是通过测试
  stencilZPass: THREE.ReplaceStencilOp, // 替换模板值为1
  stencilRef: 1
});

// 3. 先渲染遮罩到模板缓冲
scene.overrideMaterial = maskWriteMaterial;
renderer.render(scene, camera);
scene.overrideMaterial = null;

// 4. 给背景物体设置材质,只渲染模板值为1的区域
const bgMaterial = new THREE.MeshStandardMaterial({
  color: 0x44aa88,
  stencilWrite: false,
  stencilFunc: THREE.EqualStencilFunc, // 只渲染模板值等于1的区域
  stencilRef: 1,
  stencilZPass: THREE.KeepStencilOp
});

// 5. 正常渲染场景
renderer.render(scene, camera);

这三种方案里,自定义着色器是最灵活的,不需要修改纹理就能实现效果,推荐你先试试这个~

内容的提问来源于stack exchange,提问作者Michaël

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 10:27:32