直接先说解决方法,在透明材质里面,添加:alphaTest,详情看结尾
先上三张图
这是在相机仰视情况下看到的,请注意我的透明材质的模型
接下来是平视情况下:
相机逐渐俯视的情况下:
情况说明:为了实现点击有光效描边的情况下,我添加了 EffectComposer
以下为相关代码:
// 描边效果控制器(不依赖具体场景/相机)
import * as THREE from 'three'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
export class OutlineEffect {
constructor(renderer) {
// 只持有与渲染器相关的资源
this.composer = new EffectComposer(renderer)
this.outLinePass = null
this.isEnabled = false
// 保存原始渲染器的色调映射和曝光参数
// this.originalToneMapping = renderer.toneMapping;
// this.originalToneMappingExposure = renderer.toneMappingExposure;
}
// 初始化需要场景和相机(外部传入)
initialize(scene, camera) {
const renderPass = new RenderPass(scene, camera)
// 关键:同步色调映射和曝光到 RenderPass
// renderPass.toneMapping = this.originalToneMapping;
// renderPass.toneMappingExposure = this.originalToneMappingExposure;
// 强制更新渲染通道的材质
// renderPass.materialToneMapped = true; // 确保材质使用色调映射
this.composer.addPass(renderPass)
this.outLinePass = new OutlinePass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
scene,
camera
);
......
}
// 更新选中对象
setTargets(objects) {
if (this.outLinePass) {
this.outLinePass.selectedObjects = [objects];
console.log('当前选中的对象', this.outLinePass.selectedObjects);
}
}
// 渲染方法
render() {
if (this.isEnabled) {
this.composer.render();
}
}
}
以下则是 animate.js,将原生的渲染器注释,使用了 EffectComposer 的,就出现了透明模型遮挡的问题
function animate() {
const time = clock.getElapsedTime()
requestAnimationFrame(animate)
controls.update()
// rendererModule.renderer.render(scene, cameraInstance) //这里是原先使用的渲染器
outlineEffect.render() //这里为了实现光效描边效果使用的
rendererModule.css3dRender.render(scene, cameraInstance)
}
解决:我在材质里面添加了: alphaTest: 0.05, 就解决了遮挡的效果
如果使用 alphaTest 也不行的话,把depthWrite和depthTest 都开起来尝试一下
// 材质库
import * as THREE from "three";
// 其他模型未选中材质(透明)
export const unCheckedBaseMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.3,
side: THREE.DoubleSide,
// depthWrite: false, // 关闭深度写入
// depthTest: true, // 启用深度测试
alphaTest: 0.05, // 防止边缘闪烁
// wireframe: true //辅助线
});
注意:这个方法适用于大部分模型,在模型载入时候,可以给特定模型添加,以下为示例
this.loader.load('./model/V2.3.glb', (gltf) => {
scene.add(gltf.scene)
// 延迟处理阴影设置,避免阻塞主线程
gltf.scene.traverse((child) => {
if (child.isMesh) {
// 阴影
child.castShadow = true;
child.receiveShadow = true;
// 解决树叶不可见的情况
if (child.name.indexOf('branch') !== -1) {
child.material = new THREE.MeshStandardMaterial({
map: child.material.map,
transparent: true,
alphaTest: 0.1, // 关键:剔除低透明度像素
side: THREE.DoubleSide, // 双面渲染
depthWrite: false // 禁用深度写入
});
}
}
})
})
问题根源(不确定是否完全正确):透明像素的深度写入
在未设置 alphaTest 时:
1 半透明像素参与深度测试
透明材质中所有像素(包括完全透明、半透明、不透明像素)都会参与深度计算。例如,材质边缘抗锯齿产生的半透明像素(alpha=0.1~0.5)会被误认为“有效像素”写入深度缓冲区
2 深度缓冲区的误导
这些半透明像素的深度值被记录后,后续渲染其他物体时,WebGL会误以为这些位置已经被占据(即使它们实际是透明的),导致后续物体被错误遮挡
3 视角依赖的异常
俯视时,透明物体的半透明像素可能覆盖其他物体的深度值;仰视时,深度值关系变化,导致分界线出现。
在未设置 alphaTest:0.05 后:
1 阈值筛选逻辑
WebGL会直接丢弃所有透明度(alpha值)低于0.05的像素,使其不参与渲染流程(包括深度测试和颜色混合)
保留alpha≥0.05的像素(如材质主体部分)
丢弃alpha<0.05的像素(如边缘抗锯齿的渐变透明像素)
2 深度缓冲区净化
被丢弃的像素不会写入深度缓冲区,从而避免了“虚假深度值”对其他物体的遮挡干扰。
3 边缘抗锯齿优化
原本导致闪烁的半透明边缘像素被剔除,材质轮廓更清晰(类似硬边裁剪效果)。