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
相关产品推荐
相关产品推荐

