You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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()

关键代码解释

  1. 帧缓冲创建:使用create_fbo函数创建带纹理附件的帧缓冲,实现离屏渲染
  2. 亮部提取:通过标准亮度公式计算像素亮度,只保留超过阈值的像素,为后续模糊做准备
  3. 高斯模糊:采用9核高斯权重,分水平和垂直两次模糊,平衡模糊效果与性能
  4. 画面合成:将原场景纹理与模糊后的亮部纹理叠加,通过bloom_strength参数控制光晕强度
  5. 窗口适配:在窗口 resize 事件中重新创建帧缓冲,保证渲染分辨率与窗口尺寸一致

内容的提问来源于stack exchange,提问作者Raga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 08:52:03