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

Three.js中使用EffectComposer渲染目标纹理出现闪烁问题求助

问题描述

我希望将一个EffectComposer的纹理传入另一个,以实现多种特效的混合。代码基于Three.js的选择性Unreal Bloom后处理示例,使用new UnrealBloomPass()作为通道时一切正常,但替换为自定义ShaderPass后,渲染的纹理出现闪烁现象。

基础代码

const setup = () => {

    const lumPass = new ShaderPass(LuminosityShader, "tDiffuse");
    const lumComp = new EffectComposer(renderer);
    lumComp.setSize(device.width, device.height);
    lumComp.renderToScreen = false;
    lumComp.addPass(renderPass);
    lumComp.addPass(lumPass);

    // Final

    const outputPass = new OutputPass();
    const finalComp = new EffectComposer(renderer);
    const mixPass = new ShaderPass(
        new ShaderMaterial({
            uniforms: {
                baseTexture: new Uniform(null),
                lumTexture: new Uniform(lumComp.renderTarget2.texture),
            },
            vertexShader: texelVert.contents.value,
            fragmentShader: mixFrag.contents.value,
            defines: {},
        }),
        "baseTexture"
    );
    finalComp.setSize(device.width, device.height);
    finalComp.addPass(renderPass);
    finalComp.addPass(mixPass);
    // finalComp.addPass(outputPass);
};

const render = () => {
    requestAnimationFrame(render);
    lumComp.render();
    finalComp.render();
};

setup();
render();

顶点着色器

varying vec2 vUv;

void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

片元着色器

uniform sampler2D baseTexture;
uniform sampler2D lumTexture;

varying vec2 vUv;

void main() {
    gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( lumTexture, vUv ) );

}

两个着色器与示例完全一致,所以我认为问题不在着色器上。请问可能的问题原因是什么?


问题原因与解决方案

闪烁的核心原因是EffectComposer的双缓冲机制:EffectComposer默认用两个渲染目标(renderTarget1和renderTarget2)交替完成渲染,每帧结束后两个目标会交换角色。你直接固定引用lumComp.renderTarget2.texture,会导致每帧引用的纹理在"当前帧渲染结果"和"上一帧渲染结果"之间跳变,最终出现闪烁。

解决方案1:每帧更新纹理引用(推荐)

不要固定引用renderTarget2,而是在每帧渲染lumComp后,通过lumComp.readBuffer.texture获取当前的有效渲染结果,更新mixPass的uniform值:

const render = () => {
    requestAnimationFrame(render);
    lumComp.render();
    // 实时更新lumTexture为当前帧的有效纹理
    mixPass.material.uniforms.lumTexture.value = lumComp.readBuffer.texture;
    finalComp.render();
};

解决方案2:禁用双缓冲(不推荐)

如果你的后处理流程不需要双缓冲,可以在创建lumComp时自定义单渲染目标,关闭双缓冲机制:

const lumComp = new EffectComposer(renderer, new WebGLRenderTarget(device.width, device.height, {
    type: HalfFloatType,
    format: RGBAFormat,
    stencilBuffer: false,
    depthBuffer: false
}));
lumComp.setSize(device.width, device.height);
lumComp.renderToScreen = false;
lumComp.addPass(renderPass);
lumComp.addPass(lumPass);

此时lumComp.renderTarget.texture就是稳定的渲染结果,不会出现闪烁,但这种方式可能影响部分依赖双缓冲的后处理效果正确性。

解决方案3:用TexturePass传递纹理(规范做法)

可以在finalComp中添加TexturePass来引入lumComp的结果,避免直接操作uniform:

// 修改setup中的finalComp部分
const finalComp = new EffectComposer(renderer);
const texturePass = new TexturePass(null);
const mixPass = new ShaderPass(
    new ShaderMaterial({
        uniforms: {
            baseTexture: new Uniform(null),
            lumTexture: new Uniform(texturePass.uniforms.tDiffuse.value),
        },
        vertexShader: texelVert.contents.value,
        fragmentShader: mixFrag.contents.value,
        defines: {},
    }),
    "baseTexture"
);
finalComp.setSize(device.width, device.height);
finalComp.addPass(renderPass);
finalComp.addPass(texturePass);
finalComp.addPass(mixPass);

然后在渲染循环中更新texturePass的纹理:

const render = () => {
    requestAnimationFrame(render);
    lumComp.render();
    texturePass.uniforms.tDiffuse.value = lumComp.readBuffer.texture;
    finalComp.render();
};

补充说明

UnrealBloomPass没有出现闪烁,是因为它内部已经封装了双缓冲的纹理引用处理逻辑,而自定义ShaderPass需要你手动管理纹理的正确引用。


内容的提问来源于stack exchange,提问作者The Sloth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 07:50:59