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

无需FBO,如何在WebGPU中实现bloom效果?

在WebGPU中实现Bloom效果

WebGPU没有直接对应WebGL中FBO的组件,而是将其职责拆分为纹理(Texture)、纹理视图(TextureView)和渲染通道(RenderPass)。Bloom的核心逻辑(亮区提取→多轮降采样模糊→多轮上采样模糊→原画面混合)不变,只是需要用WebGPU的API重新实现每一步:

1. 创建中间渲染纹理

首先要创建一系列用于存储中间结果的纹理,这些纹理需要同时支持作为渲染目标和采样纹理,所以创建时要指定usage包含RENDER_ATTACHMENT和TEXTURE_BINDING:

function createRenderTexture(device, width, height) {
  return device.createTexture({
    size: { width, height, depthOrArrayLayers: 1 },
    format: 'rgba16float', // 浮点格式保留亮区细节,避免精度丢失
    usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
    sampleCount: 1,
  });
}
// 根据需求创建原场景纹理、亮区纹理、多组降采样/上采样纹理
const sceneTexture = createRenderTexture(device, canvas.width, canvas.height);
const brightPassTexture = createRenderTexture(device, canvas.width, canvas.height);
// 降采样纹理数组,尺寸每次减半
const downSampleTextures = [];
let currentWidth = canvas.width;
let currentHeight = canvas.height;
for (let i = 0; i < 4; i++) { // 示例做4轮降采样
  currentWidth = Math.floor(currentWidth / 2);
  currentHeight = Math.floor(currentHeight / 2);
  downSampleTextures.push(createRenderTexture(device, currentWidth, currentHeight));
}
// 上采样纹理数组,对应降采样的逆过程
const upSampleTextures = [...downSampleTextures].reverse().slice(1);
upSampleTextures.push(createRenderTexture(device, canvas.width, canvas.height));

2. 提取场景亮区

编写亮区提取的片元着色器,将原场景中亮度超过阈值的像素保留,其余置黑:

@group(0) @binding(0) var sceneTexture: texture_2d<f32>;
@group(0) @binding(1) var sceneSampler: sampler;

struct VertexOutput {
  @location(0) uv: vec2f,
};

@fragment
fn fs(input: VertexOutput) -> @location(0) vec4f {
  let color = textureSample(sceneTexture, sceneSampler, input.uv);
  // 用标准亮度公式计算像素亮度
  let brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
  let threshold = 1.0; // 可根据需求调整阈值
  // 只保留亮度超过阈值的部分
  return vec4(color.rgb * step(threshold, brightness), 1.0);
}

然后创建RenderPass,将输出目标绑定到brightPassTexture的视图,执行渲染得到亮区纹理。

3. 多轮降采样高斯模糊

对亮区纹理进行多次降采样,每次将纹理尺寸减半,同时应用高斯模糊:

  • 每次降采样都需要创建对应的RenderPass,绑定上一级纹理作为输入,当前级更小的纹理作为输出目标。
  • 降采样着色器结合高斯模糊,示例如下:
@group(0) @binding(0) var inputTexture: texture_2d<f32>;
@group(0) @binding(1) var inputSampler: sampler;
@group(0) @binding(2) var textureSize: vec2f; // 当前输入纹理的尺寸

struct VertexOutput {
  @location(0) uv: vec2f,
};

@fragment
fn fs(input: VertexOutput) -> @location(0) vec4f {
  let texelSize = 1.0 / textureSize;
  // 9邻域高斯采样权重
  let weights = vec4(0.227027, 0.1945946, 0.1216216, 0.054054);
  let offsets = vec2(0.0, 1.38461538) * texelSize;
  
  var color = textureSample(inputTexture, inputSampler, input.uv) * weights.x;
  color += textureSample(inputTexture, inputSampler, input.uv + offsets) * weights.y;
  color += textureSample(inputTexture, inputSampler, input.uv - offsets) * weights.y;
  color += textureSample(inputTexture, inputSampler, input.uv + offsets * 2.0) * weights.z;
  color += textureSample(inputTexture, inputSampler, input.uv - offsets * 2.0) * weights.z;
  color += textureSample(inputTexture, inputSampler, input.uv + offsets * 3.0) * weights.w;
  color += textureSample(inputTexture, inputSampler, input.uv - offsets * 3.0) * weights.w;
  
  return color;
}

循环执行每一轮降采样,将上一步的输出作为当前步的输入。

4. 多轮上采样并混合

从最小的降采样纹理开始,逐步上采样到原尺寸,每次上采样后和上一级的降采样纹理混合(保留细节):

  • 上采样着色器同样应用高斯模糊,并且在采样时考虑尺寸缩放后的UV偏移。
  • 每一轮上采样的RenderPass,绑定当前要上采样的纹理和对应的上一级降采样纹理作为输入,输出到更大尺寸的纹理。

5. 混合原场景与Bloom纹理

最后创建RenderPass,将原场景纹理和最终的上采样模糊纹理作为输入,在片元着色器中按比例混合:

@group(0) @binding(0) var sceneTexture: texture_2d<f32>;
@group(0) @binding(1) var bloomTexture: texture_2d<f32>;
@group(0) @binding(2) var sampler: sampler;

struct VertexOutput {
  @location(0) uv: vec2f,
};

@fragment
fn fs(input: VertexOutput) -> @location(0) vec4f {
  let sceneColor = textureSample(sceneTexture, sampler, input.uv);
  let bloomColor = textureSample(bloomTexture, sampler, input.uv);
  // 直接叠加或根据需求调整混合比例
  return vec4(sceneColor.rgb + bloomColor.rgb, 1.0);
}

执行该RenderPass,将结果输出到画布的纹理视图,完成Bloom效果渲染。

关键注意事项

  • 纹理格式:优先选择浮点格式(如rgba16float)存储中间结果,避免亮区细节因精度丢失被截断。
  • 采样器设置:降采样和上采样时使用线性采样(FilterMode.Linear),让模糊过渡更平滑。
  • 循环复用逻辑:降采样和上采样的RenderPass可以通过循环批量创建,减少重复代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 13:04:59