bindFramebuffer 代码里两处bindFramebuffer分别干什么先把代码里两处关键位置摘出来第一处初始化阶段构建 FBOconst 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);gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)① 把新建的 fbo 绑定到帧缓冲绑定点让后面的framebufferTexture2D作用到这个 fbo 对象上把 pickTex 纹理挂载为它的颜色附件。挂载关系会永久保存在 fbo 对象内部。gl.bindFramebuffer(gl.FRAMEBUFFER, null)② 初始化挂载完成切回浏览器默认屏幕帧缓冲。如果这里不切回 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 id0; idLINE_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); }gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)③ 鼠标点击触发拾取再次激活 fbo。 此时所有drawArrays绘制不再画到屏幕而是绘制输出到pickTex纹理把每条线段的 ID 颜色渲染到离屏纹理。readPixels也必须在 fbo 绑定状态下才能读取离屏纹理的像素。gl.bindFramebuffer(gl.FRAMEBUFFER, null)④ 拾取逻辑全部完成再次切回屏幕帧缓冲。 退出 pickLine 之后回到render()正常绘制场景输出到页面 canvas。四处 bindFramebuffer 总结表格序号位置作用①初始化绑定 fbo执行framebufferTexture2D挂载纹理②初始化解绑切回屏幕防止初始化渲染跑到离屏纹理③pickLine 拾取函数开头激活 fbodraw 绘制 ID 颜色、readPixels 读取离屏纹理像素④pickLine 拾取函数末尾解绑切回屏幕后续正常渲染画到页面核心逻辑bindFramebuffer (fbo) “切换输出目标到离屏 FBO”bindFramebuffer (null) “切换输出目标回到浏览器屏幕画布”这个代码有一个严重性能 bug百万线段// pickLine里面 for(let id0; idLINE_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 是点击的时候临时切到离屏做拾取读完像素马上切回屏幕渲染画面。

相关新闻

最新新闻

会计专业大学期间考什么证

会计专业大学期间考什么证

会计专业考证不要只看证书名字,而要先想清楚:以后想进企业财务、会计师事务所、税务方向、银行金融,还是财务分析和业财融合岗位。从大学生就业和财务职业发展的角度看,会计专业需要建设的能力大致可以分成两部分:一部…

2026/8/25 15:04:45
基于 Android 的本地商城订单 App:SQLite + 多角色架构的技术实践(有源码和文档)

基于 Android 的本地商城订单 App:SQLite + 多角色架构的技术实践(有源码和文档)

摘要:本文以一款面向本地化的"比萨点餐"Android 商城 App(orderApp)为案例,围绕"纯本地 SQLite 多角色路由 ViewPager/Fragment"这一典型教学型商城架构展开,从需求拆解、技术选型、数据库设计、…

2026/8/25 15:04:45
FIRST 集合(首符号集)学习笔记

FIRST 集合(首符号集)学习笔记

文章目录FIRST 集合(首符号集)学习笔记一、这个知识点到底在干嘛?二、前置概念(必须先搞清楚)三、FIRST 集合的核心操作四、特殊情况:空串 ε五、实战演示六、避坑指南七、与其他集合的关系(预览…

2026/8/25 15:04:45
降价、造芯、机器人破纪录:AI竞赛从模型卷到了算力底层

降价、造芯、机器人破纪录:AI竞赛从模型卷到了算力底层

降价、造芯、机器人破纪录:AI竞赛从模型卷到了算力底层 这个八月最后一周,AI圈最热闹的不是谁又刷了benchmark,而是三件事同时发生:大模型价格一个月内被砍掉近四分之一,芯片自研成了巨头标配,人形机器人在…

2026/8/25 15:04:45
一场 MySQL 默认值引发的血案

一场 MySQL 默认值引发的血案

目录问题本质场景还原脏数据从何而来:MySQL 隐式默认值根因分析1. MySQL 隐式类型转换规则2. EXPLAIN 行为对比3. MyBatis Plus selectOne() 源码故障链路全景解决方案方案 A:入参前置校验 — 快速止血方案 B:显式类型约束 — 根源修复方案 B…

2026/8/25 15:04:45
面试高频考点 02:看门狗 / 启动模式 / 复位 / Bootloader 面试要点

面试高频考点 02:看门狗 / 启动模式 / 复位 / Bootloader 面试要点

适用人群:和我一样正在准备 2026 秋招的嵌入式方向同学——前几篇专栏把看门狗、启动流程、OTA 都讲过基础了,但面试官一追问"你项目里看门狗怎么用的"、“复位和上电有啥区别”、“Bootloader 跳转前要做什么”,还是容易卡壳 读完…

2026/8/25 14:59:45