three.js 添加 EffectComposer 导致透明模型被遮挡

直接先说解决方法,在透明材质里面,添加: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的像素(如边缘抗锯齿的渐变透明像素)

深度缓冲区净化

被丢弃的像素不会写入深度缓冲区,从而避免了“虚假深度值”对其他物体的遮挡干扰。

边缘抗锯齿优化

原本导致闪烁的半透明边缘像素被剔除,材质轮廓更清晰(类似硬边裁剪效果)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值