bindFramebuffer

代码里两处 bindFramebuffer 分别干什么

先把代码里两处关键位置摘出来:

第一处:初始化阶段,构建 FBO

TypeScript 复制代码
const fbo = gl.createFramebuffer();
const pickTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, pickTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

//👉①
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, pickTex, 0);
//👉②
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
  1. gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)
  2. ①: 把新建的 fbo 绑定到帧缓冲绑定点,让后面的framebufferTexture2D作用到这个 fbo 对象上,把 pickTex 纹理挂载为它的颜色附件。挂载关系会永久保存在 fbo 对象内部。
  3. gl.bindFramebuffer(gl.FRAMEBUFFER, null)
  4. ②: 初始化挂载完成,切回浏览器默认屏幕帧缓冲

如果这里不切回 null,后面页面初始render()执行 drawArrays,渲染会跑到 pickTex 离屏纹理上,屏幕画布就是黑的,什么都看不见。
⚠️注意:解绑null不会销毁 fbo,也不会取消纹理挂载关系,只是把 "当前操作对象" 切回屏幕。后续点击拾取还可以再次 bind 这个 fbo。


第二处:点击鼠标,pickLine()拾取函数内部

复制代码
function pickLine(mouseX, mouseY) {
    //👉③
    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
    gl.clearColor(0,0,0,0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.useProgram(pickProg);
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
    gl.vertexAttribPointer(aPosPick, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(aPosPick);

    for(let id=0; id<LINE_COUNT; id++){
        const color = idToColor(id);
        gl.uniform3fv(uIdColorPick, color);
        gl.drawArrays(gl.LINES, id*2, 2);
    }

    const pixels = new Uint8Array(4);
    const glY = canvas.height - mouseY;
    gl.readPixels(mouseX, glY, 1,1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

    //👉④
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    const [r,g,b,a] = pixels;
    if(a === 0) return -1;
    return colorToId(r,g,b);
}
  1. gl.bindFramebuffer(gl.FRAMEBUFFER, fbo) ③: 鼠标点击触发拾取,再次激活 fbo 。 此时所有drawArrays绘制,不再画到屏幕,而是绘制输出到pickTex纹理;把每条线段的 ID 颜色渲染到离屏纹理。readPixels也必须在 fbo 绑定状态下,才能读取离屏纹理的像素。
  2. gl.bindFramebuffer(gl.FRAMEBUFFER, null) ④: 拾取逻辑全部完成,再次切回屏幕帧缓冲。 退出 pickLine 之后回到render(),正常绘制场景,输出到页面 canvas。

四处 bindFramebuffer 总结

表格

序号 位置 作用
初始化 绑定 fbo,执行framebufferTexture2D挂载纹理
初始化 解绑,切回屏幕,防止初始化渲染跑到离屏纹理
pickLine 拾取函数开头 激活 fbo,draw 绘制 ID 颜色、readPixels 读取离屏纹理像素
pickLine 拾取函数末尾 解绑,切回屏幕,后续正常渲染画到页面

核心逻辑:

  • bindFramebuffer (fbo) = "切换输出目标到离屏 FBO"
  • bindFramebuffer (null) = "切换输出目标回到浏览器屏幕画布"

这个代码有一个严重性能 bug(百万线段)

复制代码
// pickLine里面
for(let id=0; id<LINE_COUNT; id++){
    const color = idToColor(id);
    gl.uniform3fv(uIdColorPick, color);
    gl.drawArrays(gl.LINES, id*2, 2);
}

一百万次循环调用 drawArrays,百万次 drawcall,浏览器会巨卡甚至直接崩溃。

正确做法:顶点缓冲区里直接预存每一条线的 id 颜色属性,一次 drawArrays 全部提交,而不是循环一百万次逐个 draw。


简单一句话: 初始化阶段两次 bindFramebuffer 是为了给 FBO 挂上纹理,做完立刻切回屏幕; 拾取函数内部两次 bindFramebuffer 是点击的时候临时切到离屏做拾取,读完像素马上切回屏幕渲染画面。

相关推荐
几何心凉2 小时前
没有万能模型:我用 AiiOnly Token Plan,把多款大模型的长处拼进同一个项目
前端
kyriewen2 小时前
Claude Code 的额度今天缩水了17%——官方公告上写的是"永久提高25%"
前端·ai编程·claude
雪芽蓝域zzs3 小时前
vue解构平铺VS对象包裹
前端·javascript·vue.js
风骏时光牛马4 小时前
从零到实战,完整AI学习路线
前端
星辰徐哥4 小时前
本地视频预览别只自己看:把Remotion动效项目发给客户远程验收
docker·ai·node.js·html·音视频·react·remotion
三十而立洋4 小时前
深入理解 Monorepo:子包安装依赖
前端·前端工程化
IT_陈寒4 小时前
Java Stream处理大集合,我的内存怎么就炸了
前端·人工智能·后端
Captaincc5 小时前
AI 用量桌面端-桌面宠物自定义指南
前端·人工智能
OpenTiny社区5 小时前
码力全开,智启前端新生态|OpenTiny 登陆华为全联接大会2026
前端·github
妙码生花5 小时前
利用AI从零学Go并完成实战项目,完工总结:目录结构
前端·后端·go