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

复用WebGLRenderTarget作为材质时出现全黑问题求助

Three.js FBO作为Uniform传入后材质全黑问题

我是Shader新手,用Three.js结合ShaderMaterial开发时遇到一个问题:用FBO创建材质并作为uniform传入片元着色器,原本应该得到随机分布的颜色材质,但把this.fbo.texture设为uniform参数值后,材质变成全黑。

相关代码

JavaScript部分

setupFBO(){
    this.size = 128;
    this.fbo = this.getRenderTarget();

    this.fboScene = new THREE.Scene();
    this.fboCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, -1, 1);
    this.fboCamera.position.set(0, 0, 0.5);

    let geometry = new THREE.PlaneGeometry(2, 2);

    this.data = new Float32Array(this.size * this.size * 4);

    for(let i = 0; i < this.size; i++){
        for(let j = 0; j < this.size; j++){
            let index = (i + j * this.size) * 4;
            let theta = Math.random() * Math.PI * 2; 
            let r = 0.5 + 0.5 * Math.random(); 

            this.data[index + 0] = r * Math.cos(theta);
            this.data[index + 1] = r * Math.sin(theta);
            this.data[index + 2] = 1.0;
            this.data[index + 3] = 1.0;
        }
    }

    this.fboTexture = new THREE.DataTexture(this.data, this.size, this.size, THREE.RGBAFormat, THREE.FloatType);
    this.fboTexture.magFilter = THREE.NearestFilter;
    this.fboTexture.minFilter = THREE.NearestFilter;
    this.fboTexture.needsUpdate = true;

    this.fboMaterial = new THREE.ShaderMaterial({
        uniforms: {
            uPositions: {value: this.fboTexture},
            time: {value: 0}
        },
        vertexShader: simVertex,
        fragmentShader: simFragment
    })
   
    let FBOmesh = new THREE.Mesh(geometry, this.fboMaterial);
   
    this.fboScene.add(FBOmesh);
    this.renderer.setRenderTarget(null);
    this.renderer.setRenderTarget(this.fbo);
    this.renderer.render(this.fboScene, this.fboCamera);
    this.renderer.setRenderTarget(null);
    
    ////here is the problem
    //this.fboMaterial.uniforms.uPositions.value = this.fbo.texture;  //here is the problem

    //this.renderer.setRenderTarget(null);
    //this.renderer.setRenderTarget(this.fbo);
    //this.renderer.render(this.fboScene, this.fboCamera);

    //this.renderer.setRenderTarget(null);
    //this.renderer.render(this.fboScene, this.fboCamera);
}

getRenderTarget(){
    const renderTarget = new THREE.WebGLRenderTarget(this.canvas.width, this.canvas.height, {
        minFilter: THREE.NearestFilter,
        magFilter: THREE.NearestFilter,  
        format: THREE.RGBAFormat,
        type: THREE.FloatType,
        stencilBuffer: false
    });
    return renderTarget;
}

顶点着色器(simVertex.glsl)

uniform float time;
varying vec2 vUv;
varying vec3 vPosition;
float PI = 3.141592653589793238;

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

片元着色器(simFragment.glsl)

uniform float time;
uniform float progress;
uniform sampler2D uPositions;  
varying vec2 vUv;
varying vec3 vPosition;

void main(){
    vec4 pos = texture2D(uPositions, vUv);
    gl_FragColor = vec4(pos.xyz, 1);
}

奇怪的现象

如果先修改uniform值再执行渲染操作,结果是正常的:

this.fboMaterial.uniforms.uPositions.value = this.fbo.texture;

this.renderer.setRenderTarget(null);
this.renderer.setRenderTarget(this.fbo);
this.renderer.render(this.fboScene, this.fboCamera);

this.renderer.setRenderTarget(null);
this.renderer.render(this.fboScene, this.fboCamera);

但我觉得两种操作顺序应该得到相同结果,毕竟先把渲染结果存在this.fbo里再重置了RenderTarget。而且换用新的FBO(比如fbo1)就不会有问题,怀疑是render操作修改了相关值,这是为什么?


解答

这是因为WebGL不允许在同一个渲染周期内,同时将一个帧缓冲作为渲染目标和采样纹理使用。

第一种操作顺序的问题在于:

  1. 先把场景渲染到this.fbo,此时this.fbo是当前的渲染目标
  2. 紧接着把this.fbo.texture设为uniform,然后再次渲染到this.fbo——这时候你试图读取正在被写入的帧缓冲,WebGL会触发"帧缓冲读写冲突",导致采样结果异常(全黑是常见表现)

而第二种顺序是先把uniform设为this.fbo.texture,再执行渲染:

  • 第一次渲染到this.fbo时,虽然uniform指向它,但此时this.fbo还没有有效数据,不过因为是首次写入,WebGL会正常执行写入操作;第二次渲染时,this.fbo已经有了上一次的渲染数据,此时读取是合法的(因为当前渲染目标是屏幕,读取this.fbo.texture没有冲突)

换用新FBO没问题的原因也很简单:两个不同的帧缓冲,一个作为写入目标,另一个作为采样源,完全不存在读写冲突。

解决方法

  1. 使用双缓冲FBO:准备两个FBO,交替作为渲染目标和采样纹理——这是GPGPU(通用GPU计算)里的标准做法,彻底避免读写冲突
  2. 确保在读取FBO纹理时,它不是当前的渲染目标:比如在渲染到FBO后,先切换回默认帧缓冲(setRenderTarget(null)),再进行读取操作;如果需要循环迭代计算,必须用两个FBO交替

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 05:40:35