
代碼里兩處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 對象內(nèi)部。gl.bindFramebuffer(gl.FRAMEBUFFER, null)② 初始化掛載完成切回瀏覽器默認屏幕幀緩沖。如果這里不切回 null后面頁面初始render()執(zhí)行 drawArrays渲染會跑到 pickTex 離屏紋理上屏幕畫布就是黑的什么都看不見。??注意解綁null不會銷毀 fbo也不會取消紋理掛載關系只是把 “當前操作對象” 切回屏幕。后續(xù)點擊拾取還可以再次 bind 這個 fbo。第二處點擊鼠標pickLine()拾取函數(shù)內(nèi)部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)③ 鼠標點擊觸發(fā)拾取再次激活 fbo。 此時所有drawArrays繪制不再畫到屏幕而是繪制輸出到pickTex紋理把每條線段的 ID 顏色渲染到離屏紋理。readPixels也必須在 fbo 綁定狀態(tài)下才能讀取離屏紋理的像素。gl.bindFramebuffer(gl.FRAMEBUFFER, null)④ 拾取邏輯全部完成再次切回屏幕幀緩沖。 退出 pickLine 之后回到render()正常繪制場景輸出到頁面 canvas。四處 bindFramebuffer 總結(jié)表格序號位置作用①初始化綁定 fbo執(zhí)行framebufferTexture2D掛載紋理②初始化解綁切回屏幕防止初始化渲染跑到離屏紋理③pickLine 拾取函數(shù)開頭激活 fbodraw 繪制 ID 顏色、readPixels 讀取離屏紋理像素④pickLine 拾取函數(shù)末尾解綁切回屏幕后續(xù)正常渲染畫到頁面核心邏輯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); }一百萬次循環(huán)調(diào)用 drawArrays百萬次 drawcall瀏覽器會巨卡甚至直接崩潰。正確做法頂點緩沖區(qū)里直接預存每一條線的 id 顏色屬性一次 drawArrays 全部提交而不是循環(huán)一百萬次逐個 draw。簡單一句話 初始化階段兩次 bindFramebuffer 是為了給 FBO 掛上紋理做完立刻切回屏幕 拾取函數(shù)內(nèi)部兩次 bindFramebuffer 是點擊的時候臨時切到離屏做拾取讀完像素馬上切回屏幕渲染畫面。