#[set(everything)] const constants: { screen_size_inv: float2; current_mip_level: int; }; #[set(everything)] const tex: tex2d; #[set(everything)] const tex_sampler: sampler; struct vert_in { pos: float2; } struct vert_out { pos: float4; tex: float2; } const bloom_knee: float = 0.5; const bloom_threshold: float = 0.8; const epsilon: float = 0.000062; fun bloom_downsample_pass_vert(input: vert_in): vert_out { var output: vert_out; output.tex = input.pos.xy * 0.5 + 0.5; output.tex.y = 1.0 - output.tex.y; output.pos = float4(input.pos.xy, 0.0, 1.0); return output; } fun downsample_dual_filter(tex: tex2d, tex_coord: float2, texel_size: float2): float3 { var delta: float3 = float3(texel_size.xy, texel_size.x) * float3(0.5, 0.5, -0.5); var result: float3; result = sample_lod(tex, tex_sampler, tex_coord, 0.0).rgb * 4.0; result += sample_lod(tex, tex_sampler, tex_coord - delta.xy, 0.0).rgb; result += sample_lod(tex, tex_sampler, tex_coord - delta.zy, 0.0).rgb; result += sample_lod(tex, tex_sampler, tex_coord + delta.zy, 0.0).rgb; result += sample_lod(tex, tex_sampler, tex_coord + delta.xy, 0.0).rgb; return result * (1.0 / 8.0); } fun bloom_downsample_pass_frag(input: vert_out): float4 { var color: float4; color.rgb = downsample_dual_filter(tex, input.tex, constants.screen_size_inv); if (constants.current_mip_level == 0) { var brightness: float = max(color.r, max(color.g, color.b)); var softening_curve: float = brightness - bloom_threshold + bloom_knee; softening_curve = clamp(softening_curve, 0.0, 2.0 * bloom_knee); softening_curve = softening_curve * softening_curve / (4 * bloom_knee + epsilon); var contribution_factor: float = max(softening_curve, brightness - bloom_threshold); contribution_factor /= max(epsilon, brightness); color.rgb *= contribution_factor; } color.a = 1.0; return color; } #[pipe] struct pipe { vertex = bloom_downsample_pass_vert; fragment = bloom_downsample_pass_frag; }