7.1 后处理管线与泛光


7.1 后处理管线与泛光

本节摘要:后处理的全部机制可以浓缩成一个动作——把渲染结果从屏幕改道到纹理,再用全屏四边形采样加工。本节搭起这条骨架,并实现最经典的泛光:亮部提取、降采样模糊、加法合成的三段式。

学习目标

读完本节,你应当能够:解释渲染目标与全屏四边形的协作机制;说出后处理按 pass 拆分的原因;实现三段式泛光并解释每段的视觉贡献。

渲染到纹理:后处理的全部秘密

常规渲染把像素直接写进屏幕背后的帧缓冲。后处理的第一步是改道:创建一张离屏纹理(渲染目标,OpenGL 里由帧缓冲对象 FBO 挂载),让场景照常渲染,只是落点换成这张纹理。此时"整帧画面"成了普通素材,与第 5 章的任何贴图无异——可以采样任意坐标,包括邻居。

第二步是"叠回来":渲染一个铺满视口的四边形(全屏三角形亦可),它的片元着色器把刚才那张纹理采样出来输出。因为 UV 铺满 0 到 1,屏幕上每个像素恰好对应纹理上每个像素——一次draw call 完成整幅图像的搬运。要加工图像,只需在这个采样搬运的途中插入数学:

// 全屏 pass 的顶点着色器:三个顶点撑出一个覆盖全屏的大三角形 #version 330 core layout (location = 0) in vec2 aPos; // 预设坐标:(-1,-1) (3,-1) (-1,3) out vec2 vUv; void main() { vUv = aPos * 0.5 + 0.5; // 裁剪空间坐标顺手转成 UV gl_Position = vec4(aPos, 0.0, 1.0); }
// 最朴素的片元着色器:原图直出。一切后处理都在这段里加戏 #version 330 core in vec2 vUv; out vec4 FragColor; uniform sampler2D uScene; void main() { FragColor = texture(uScene, vUv); }

复杂效果为什么要拆多个 pass?因为有的运算需要"上一趟的输出"当"这一趟的输入":模糊一趟只能覆盖一个小邻域,多趟叠出大范围;每个 pass 的输出都是一张新纹理,输入输出泾渭分明。引擎里的"后处理栈"就是一串 pass 的编排。

图:泛光的后处理流程:亮部抽取、多级模糊、叠加合成

图:泛光的后处理流程:亮部抽取、多级模糊、叠加合成

泛光三段式实现

泛光(Bloom)模拟的是"亮到镜头都扛不住"的物理现象:高光溢出边界、晕染四周。实现拆成三段,各司其职。

**第一段:亮部提取。**从场景图里筛出足够亮的区域,暗部归零:

// 亮部提取 pass #version 330 core in vec2 vUv; out vec4 FragColor; uniform sampler2D uScene; uniform float uThreshold; // 亮度门槛,常见取值 1.0 附近(HDR 语境) void main() { vec3 c = texture(uScene, vUv).rgb; float l = dot(c, vec3(0.2126, 0.7152, 0.0722)); // 感知亮度 float k = smoothstep(uThreshold, uThreshold + 0.5, l); FragColor = vec4(c * k, 1.0); // 软阈值:过线的全保,临界渐弱 }

用 smoothstep 做软阈值比硬性 if 更聪明:临界亮度渐变淡出,泛光边缘不生硬。这也是第 4 章"权重思维"的直接复用。

**第二段:降采样模糊。**把亮部图缩到一半甚至四分之一分辨率,跑高斯模糊(下一节给出实现)。降采样是免费的大模糊:分辨率减半一次,模糊范围等效翻倍,而开销骤降——泛光的柔和光晕根本用不着全分辨率。

**第三段:加法合成。**把模糊后的泛光图叠回场景:

// 合成 pass #version 330 core in vec2 vUv; out vec4 FragColor; uniform sampler2D uScene; uniform sampler2D uBloom; uniform float uIntensity; // 泛光强度,0.2 到 1.0 常见 void main() { vec3 scene = texture(uScene, vUv).rgb; vec3 bloom = texture(uBloom, vUv).rgb; FragColor = vec4(scene + bloom * uIntensity, 1.0); // 加法:光上叠光 }

加法合成符合物理直觉——两束光照到同一处,能量相加。强度参数是画面口味旋钮:写实场景压低,霓虹赛博放开。多级泛光(同时叠几个模糊半径)会更有层次,机制不变,多跑几趟降采样模糊再逐层叠加即可。

⚠️ 常见坑:泛光在低动态范围(LDR)管线下很难看——亮度封顶在 1.0,"比亮更亮"无从谈起,阈值只能切出灰白大片。可信的泛光需要 HDR 渲染(帧纹理用浮点格式,亮度可超过 1),配合下一节的色调映射才成立。两件事是一体的,分开做都会怀疑人生。

变式练习:暗角与扫描线

骨架跑通后,用两个小效果练习"往 pass 里加戏"。暗角(vignette)模拟镜头边缘透光衰减,全屏效果里最便宜的氛围担当:

void main() { vec3 c = texture(uScene, vUv).rgb; float d = length(vUv - 0.5); // 到画面中心的距离 c *= 1.0 - smoothstep(0.45, 0.75, d) * uStrength; // 越靠边越压暗 FragColor = vec4(c, 1.0); }

又是老朋友:中心化、距离场、smoothstep 软边——第 4 章的图形套路在整屏坐标系上原样复用。扫描线则是把"距离场"换成"周期场":sin(vUv.y * uResolution.y * 3.14) 配一个随时间递增的偏移,老式显示器质感就有了。

两个练习共同验证一件事:后处理着色器的思考单位是"全屏 UV 上的逐像素函数"。掌握这个视角后,效果库的扩充速度取决于你想得出什么函数,而不是会不会搭管线——管线你已经会了。

本节要点回顾

  • 改道即解锁:渲染目标让整帧画面变成可采样的素材,全屏四边形是搬运与加工的载体;
  • pass 即工序:每个 pass 读上一趟输出、写下自己的产物,复杂效果就是 pass 编排;
  • 泛光三段:软阈值亮部提取、降采样模糊、加法合成,视觉与性能都在线;
  • HDR 是前提:可信的泛光依赖浮点帧缓冲与亮度超 1,与色调映射配套使用。

泛光的模糊环节还是黑盒。下一节把高斯模糊拆开讲透,并把管线的收尾——色调映射——补齐。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U