渲染到纹理
默认情况下,绘制结果直接写到画布上。帧缓冲对象(FBO)让你改为画进一张纹理,随后这张纹理就可以像普通贴图一样被采样。几乎所有后期效果(泛光、景深、色调映射、描边)都建立在这一步之上。
glsl
// JS:
// gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
// gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);创建 FBO 的步骤:
- 创建一张空纹理:
texImage2D(..., null)只分配存储,不上传数据。 - 把它作为
COLOR_ATTACHMENT0挂到帧缓冲上。 - 用
gl.checkFramebufferStatus确认状态是FRAMEBUFFER_COMPLETE。 - 画布尺寸变化时,重新分配纹理大小。
如果场景需要深度测试,还要再挂一个深度渲染缓冲(DEPTH_ATTACHMENT)。本例是纯 2D 加法混合,所以省略了。
两遍渲染
- 第 1 遍:绑定 FBO,先画网格背景,再用
drawArraysInstanced一次画出几百个发光圆点。gl_InstanceID决定每个圆点的轨道和颜色,blendFunc(ONE, ONE)让重叠处变得更亮。 - 第 2 遍:
bindFramebuffer(null)切回画布,画一个全屏三角形,在片元着色器里读取第 1 遍的纹理。
后期效果
- 模糊:对周围 5×5 个样本做高斯加权平均。真实项目会拆成水平、垂直两遍(可分离卷积),把开销从 n² 降到 2n。
- 色差:对 R、G、B 三个通道分别沿径向偏移不同的距离采样,模拟镜头的色散,越靠近边缘越明显。
- 暗角:按到中心的距离逐渐压暗。
glsl
vec2 off = (vUV - 0.5) * uAberration * 0.02;
vec3 c = vec3(blurred(vUV + off).r, blurred(vUV).g, blurred(vUV - off).b);拖动“before | after”,左侧显示原始的 FBO 纹理,右侧是处理后的效果。
注意采样时使用的
uTexel = 1 / 尺寸:后期着色器里所有“偏移几个像素”的操作,都要换算成 UV 单位。核心 API
gl.createFramebuffergl.framebufferTexture2Dgl.checkFramebufferStatusgl.bindFramebuffergl.drawArraysInstancedgl_InstanceIDgl.blendFunctexture()动手试试
- 1把 blur 拉到 12,观察 5×5 核在大半径下出现的分层伪影。
- 2在后期着色器里加一个灰度开关:dot(c, vec3(0.299, 0.587, 0.114))。
- 3把模糊拆成水平和垂直两遍,需要两个 FBO 来回切换(ping-pong)。
还没有输出。console.log 的内容会显示在这里。
运行中