如何在Pyglet场景中添加 bloom 后期处理步骤?
在Pyglet中实现Bloom后期处理
Bloom效果的实现依赖帧缓冲(FBO)进行离屏渲染,配合着色器完成亮部提取、模糊和合成三个核心步骤,以下是针对你提供的场景的完整实现方案:
核心步骤说明
- 离屏渲染场景:将原场景渲染到帧缓冲纹理,同时提取出亮度超过阈值的亮部区域
- 高斯模糊处理:对亮部纹理进行多轮水平+垂直方向的高斯模糊,模拟光晕效果
- 画面合成:将原场景纹理与模糊后的亮部纹理混合,输出最终带Bloom的画面
完整实现代码
import pyglet from pyglet.graphics.shader import Shader, ShaderProgram from pyglet.gl import * from pyglet.math import Mat4, Vec3 from pyglet.image import Texture, Framebuffer window = pyglet.window.Window(width=1280, height=720, caption="Pyglet Bloom Demo", resizable=True) # -------------------------- # 原场景着色器 # -------------------------- scene_vert_source = """ #version 330 in vec2 vertices; uniform mat4 vp; uniform mat4 model; void main() { gl_Position = vp * model * vec4(vertices, 0.0, 1.0); } """ scene_frag_source = """ #version 330 out vec4 fragColor; void main() { vec3 col = vec3(1.0,1.0,1.0); col *= 5.0; fragColor = vec4(col,1.0); } """ # -------------------------- # 亮部提取着色器 # -------------------------- extract_vert_source = """ #version 330 in vec2 vertices; out vec2 tex_coord; void main() { gl_Position = vec4(vertices, 0.0, 1.0); tex_coord = vertices * 0.5 + 0.5; } """ extract_frag_source = """ #version 330 in vec2 tex_coord; uniform sampler2D scene_tex; uniform float threshold; out vec4 fragColor; void main() { vec3 color = texture(scene_tex, tex_coord).rgb; float brightness = dot(color, vec3(0.2126, 0.7152, 0.0722)); fragColor = vec4(color * step(threshold, brightness), 1.0); } """ # -------------------------- # 高斯模糊着色器(水平/垂直) # -------------------------- blur_vert_source = """ #version 330 in vec2 vertices; out vec2 tex_coord; void main() { gl_Position = vec4(vertices, 0.0, 1.0); tex_coord = vertices * 0.5 + 0.5; } """ blur_frag_source = """ #version 330 in vec2 tex_coord; uniform sampler2D input_tex; uniform vec2 blur_direction; uniform float blur_strength; out vec4 fragColor; const int kernel_size = 9; float weights[kernel_size] = float[](0.0162162162, 0.0540540541, 0.1216216216, 0.1945945946, 0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162); void main() { vec2 tex_offset = 1.0 / textureSize(input_tex, 0); vec3 result = texture(input_tex, tex_coord).rgb * weights[4]; for(int i = 1; i < kernel_size / 2; ++i) { result += texture(input_tex, tex_coord + tex_offset * blur_direction * i * blur_strength).rgb * weights[4 + i]; result += texture(input_tex, tex_coord - tex_offset * blur_direction * i * blur_strength).rgb * weights[4 - i]; } fragColor = vec4(result, 1.0); } """ # -------------------------- # 最终合成着色器 # -------------------------- composite_vert_source = """ #version 330 in vec2 vertices; out vec2 tex_coord; void main() { gl_Position = vec4(vertices, 0.0, 1.0); tex_coord = vertices * 0.5 + 0.5; } """ composite_frag_source = """ #version 330 in vec2 tex_coord; uniform sampler2D scene_tex; uniform sampler2D bloom_tex; uniform float bloom_strength; out vec4 fragColor; void main() { vec3 scene_color = texture(scene_tex, tex_coord).rgb; vec3 bloom_color = texture(bloom_tex, tex_coord).rgb; fragColor = vec4(scene_color + bloom_color * bloom_strength, 1.0); } """ # 创建各类着色器程序 scene_program = ShaderProgram(Shader(scene_vert_source, "vertex"), Shader(scene_frag_source, "fragment")) extract_program = ShaderProgram(Shader(extract_vert_source, "vertex"), Shader(extract_frag_source, "fragment")) blur_program = ShaderProgram(Shader(blur_vert_source, "vertex"), Shader(blur_frag_source, "fragment")) composite_program = ShaderProgram(Shader(composite_vert_source, "vertex"), Shader(composite_frag_source, "fragment")) # 创建全屏四边形(用于后期处理渲染) fullscreen_quad = composite_program.vertex_list(4, GL_TRIANGLE_STRIP, vertices=('f', (-1, -1, 1, -1, -1, 1, 1, 1)) ) # -------------------------- # 帧缓冲初始化 # -------------------------- def create_fbo(width, height): tex = Texture.create_for_size(GL_TEXTURE_2D, width, height, GL_RGBA32F) fbo = Framebuffer() fbo.attach_texture(tex) return fbo, tex # 创建三个帧缓冲:原场景、亮部、模糊临时缓冲 scene_fbo, scene_tex = create_fbo(window.width, window.height) bright_fbo, bright_tex = create_fbo(window.width, window.height) blur_fbo, blur_tex = create_fbo(window.width, window.height) # 原场景的视图投影矩阵 view_mat = Mat4.from_translation(Vec3(x=0, y=0, z=-1)) proj_mat = Mat4.orthogonal_projection(left=0, right=1280, bottom=0, top=720, z_near=0.1, z_far=100) vp = proj_mat @ view_mat scene_program['vp'] = vp # 创建场景物体 batch = pyglet.graphics.Batch() vertices = [(x - 1) * 100 for x in (-0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5)] vertices2 = [(x + 1) * 100 for x in (-0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5)] scene_program.vertex_list_indexed(4, GL_TRIANGLES, batch=batch, indices=(0,1,2,2,3,0), vertices=('f', vertices)) scene_program.vertex_list_indexed(4, GL_TRIANGLES, batch=batch, indices=(0,1,2,2,3,0), vertices=('f', vertices2)) # Bloom参数配置 extract_threshold = 1.0 blur_strength = 1.5 bloom_strength = 1.2 @window.event def on_draw(): # 步骤1:渲染原场景到帧缓冲 scene_fbo.bind() glClearColor(0.0, 0.0, 0.0, 1.0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) batch.draw() scene_fbo.unbind() # 步骤2:提取亮部区域 bright_fbo.bind() glClearColor(0.0, 0.0, 0.0, 1.0) glClear(GL_COLOR_BUFFER_BIT) extract_program.use() extract_program['scene_tex'] = scene_tex extract_program['threshold'] = extract_threshold fullscreen_quad.draw() bright_fbo.unbind() # 步骤3:高斯模糊(水平+垂直各一次) # 水平模糊 blur_fbo.bind() glClearColor(0.0, 0.0, 0.0, 1.0) glClear(GL_COLOR_BUFFER_BIT) blur_program.use() blur_program['input_tex'] = bright_tex blur_program['blur_direction'] = (1.0, 0.0) blur_program['blur_strength'] = blur_strength fullscreen_quad.draw() blur_fbo.unbind() # 垂直模糊 bright_fbo.bind() glClearColor(0.0, 0.0, 0.0, 1.0) glClear(GL_COLOR_BUFFER_BIT) blur_program['input_tex'] = blur_tex blur_program['blur_direction'] = (0.0, 1.0) fullscreen_quad.draw() bright_fbo.unbind() # 步骤4:合成原场景与Bloom效果 window.clear() composite_program.use() composite_program['scene_tex'] = scene_tex composite_program['bloom_tex'] = bright_tex composite_program['bloom_strength'] = bloom_strength fullscreen_quad.draw() @window.event def on_resize(width, height): # 窗口大小变化时重新创建帧缓冲 global scene_fbo, scene_tex, bright_fbo, bright_tex, blur_fbo, blur_tex scene_fbo, scene_tex = create_fbo(width, height) bright_fbo, bright_tex = create_fbo(width, height) blur_fbo, blur_tex = create_fbo(width, height) # 更新投影矩阵 proj_mat = Mat4.orthogonal_projection(left=0, right=width, bottom=0, top=height, z_near=0.1, z_far=100) vp = proj_mat @ view_mat scene_program['vp'] = vp pyglet.app.run()
关键代码解释
- 帧缓冲创建:使用
create_fbo函数创建带纹理附件的帧缓冲,实现离屏渲染 - 亮部提取:通过标准亮度公式计算像素亮度,只保留超过阈值的像素,为后续模糊做准备
- 高斯模糊:采用9核高斯权重,分水平和垂直两次模糊,平衡模糊效果与性能
- 画面合成:将原场景纹理与模糊后的亮部纹理叠加,通过
bloom_strength参数控制光晕强度 - 窗口适配:在窗口 resize 事件中重新创建帧缓冲,保证渲染分辨率与窗口尺寸一致
内容的提问来源于stack exchange,提问作者Raga
相关产品推荐
相关产品推荐

