(Solved) Adding 2+ shaders to MRT Bloom shader system

Biosyn

Member
Hello all,

I have a shader control object which I'm using to apply more than one full screen shader in a single Draw GUI Begin event. The current system is using the multi-render target bloom developed by @The Reverend . The other shaders I'm trying to apply is a motion blur shader by @Xor, and a hue shader from KeeVee.

Right now, I've got two situations working:

1) MRT bloom + one more fullscreen shader (either the motion blur or the hue shader, but not both).
2) Both shaders but not MRT bloom.

I'm trying to get the MRT bloom to work + both shaders, but haven't been successful with this. Below is the code for the shader controller object. The Draw GUI Begin event is the most relevant part, and I've got the two situations above and my most recent attempt in region blocks near the end of the Draw GUI Begin event:

Create:
GML:
global.upos = shader_get_uniform(shd_moblur,"pos");//uniform for x,y
global.mob_x = 0.5
global.mob_y = 0.5
global.lr = 0.45
global.ur = 0.55

moblur_enable = false;

hue_pos_uni = shader_get_uniform(shd_hue, "Position");
hue_amt = 0.5;

//-----------------------------------------------------------------------------
#region SPRITE & SHADER (mandatory):
//-----------------------------------------------------------------------------

// Set shader type based on compile checks:
shader_type                = 0 // 0: no shader | 1: GLSL ES | 2: GLSL | 3: HLSL
if (shaders_are_supported()) {
    if (shader_is_compiled(shd_bloom_HLSL_MRT_luminance))        shader_type = 3;
    else if (shader_is_compiled(shd_bloom_GLSL_MRT_luminance))    shader_type = 2;
    else if (shader_is_compiled(shd_bloom_filter_luminance))    shader_type = 1;
}


// Luminance shader for ...
switch (shader_type) {
    case 1: // GLSL ES
        shader_bloom_lum        = shd_bloom_filter_luminance;
        u_bloom_threshold        = shader_get_uniform(shader_bloom_lum, "bloom_threshold");
        u_bloom_range            = shader_get_uniform(shader_bloom_lum, "bloom_range");
        break;
    case 2: // GLSL
        shader_bloom_lum        = shd_bloom_GLSL_MRT_luminance;
        u_bloom_layer_intensity    = shader_get_uniform(shader_bloom_lum, "bloom_layer_intensity");
        u_bloom_layer_threshold    = shader_get_uniform(shader_bloom_lum, "bloom_layer_threshold");
        u_bloom_layer_range        = shader_get_uniform(shader_bloom_lum, "bloom_layer_range");
        break;
    case 3: // HLSL
        shader_bloom_lum        = shd_bloom_HLSL_MRT_luminance;
        u_bloom_layer_intensity    = shader_get_uniform(shader_bloom_lum, "bloom_layer_intensity");
        u_bloom_layer_threshold    = shader_get_uniform(shader_bloom_lum, "bloom_layer_threshold");
        u_bloom_layer_range        = shader_get_uniform(shader_bloom_lum, "bloom_layer_range");
        break;  
}

if (shader_type >= 1) {
    shader_blur                = shd_blur_2_pass_gauss_lerp_MRT;
    u_blur_steps            = shader_get_uniform(shader_blur, "blur_steps");
    u_sigma                    = shader_get_uniform(shader_blur, "sigma");
    u_blur_vector            = shader_get_uniform(shader_blur, "blur_vector");
    u_texel_size            = shader_get_uniform(shader_blur, "texel_size");

    shader_bloom_blend        = shd_bloom_blend_MRT;
    u_bloom_intensity        = shader_get_uniform(shader_bloom_blend, "bloom_intensity");
    u_bloom_darken            = shader_get_uniform(shader_bloom_blend, "bloom_darken");
    u_bloom_saturation        = shader_get_uniform(shader_bloom_blend, "bloom_saturation");
    u_bloom_texture            = shader_get_sampler_index(shader_bloom_blend, "bloom_texture");

    bloom_texture            = -1;
    srf_ping                = -1;
    srf_pong                = -1;
    srf_moblur                = -1;
    srf_hue                    = -1;

    gui_w                    = display_get_gui_width();
    gui_h                    = display_get_gui_height();

    app_w                    = global.window_size_native_width * global.gfx_res; //!note: the divider value here must correlate with game's scale. Leave as 1 if not changing scale. But change if scale/zoom factors of screen involved
    app_h                    = global.window_size_native_height * global.gfx_res; //ditto

    texel_w                    = 1 / app_w;
    texel_h                    = 1 / app_h;

    application_surface_draw_enable(false);
}
#endregion
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
#region UNIFORM SETTERS:
//-----------------------------------------------------------------------------
// create depth based shader controllers for GLSL & HLSL
if (shader_type >= 2) {
    create_bloom_MRT_uniform_setter("ilyr_bg1",        0.9,0.1,0.1);
    create_bloom_MRT_uniform_setter("ilyr_main",    0,1,1);
}
#endregion

Draw Begin:
Code:
if (shader_type >= 1) { // GLSL ES || GLSL || HLSL
    if (!surface_exists(srf_ping)) {
        srf_ping = surface_create(app_w, app_h);
        bloom_texture = surface_get_texture(srf_ping);
    }
    if (!surface_exists(srf_pong)) {
        srf_pong = surface_create(app_w, app_h);
    }

    if (!surface_exists(srf_moblur)) {
        srf_moblur = surface_create(app_w, app_h);
    }

    if (!surface_exists(srf_hue)) {
        srf_hue = surface_create(app_w, app_h);
    }
}

if (shader_type >= 2) { // GLSL || HLSL
    shader_set(shader_bloom_lum);
    surface_set_target_ext(0, application_surface);
    surface_set_target_ext(1, srf_ping);
    camera_apply(view_camera[0]);
}

Draw End:
Code:
if (shader_type >= 2) { // GLSL || HLSL
    shader_reset();
    surface_reset_target();
}

Draw GUI Begin (most relevant part):
Code:
if (shader_type == 0) exit; // no shader

// SET VALUES:
//-----------------------------------------------------------------------------
var blur_steps        = round(1 * 15) + 1;
var sigma            = max(0.66, 0.0001);
var bloom_intensity    = 1 * 2;
var bloom_darken    = 1 - 0;
var bloom_saturation= 1 * 2;

if (shader_type == 1) { // GLSL ES
    var bloom_threshold = 0.5//slider_get_value(5); //no effect, prob coz of GLSL ES (i.e. no layers)
    var bloom_range        = 0.5//slider_get_value(6); //no effect
}

// DRAW:
//-----------------------------------------------------------------------------

// 1st pass: Draw brights to bloom surface:
// AppSrf -> srf_ping
// GLSL ES only; GLSL & HLSL do this pass per layer using MRT
if (shader_type == 1) {
    shader_set(shader_bloom_lum);
        shader_set_uniform_f(u_bloom_threshold,        bloom_threshold);
        shader_set_uniform_f(u_bloom_range,            bloom_range);

        surface_set_target(srf_ping);
            draw_surface(application_surface, 0, 0); //feed app surf into srf_ping
        surface_reset_target();
}

// 2nd pass: blur horizontally
// srf_ping -> srf_pong
gpu_set_tex_filter(true);
shader_set(shader_blur);
    shader_set_uniform_f(u_blur_steps,        blur_steps);
    shader_set_uniform_f(u_sigma,            sigma);
    shader_set_uniform_f(u_blur_vector,        1, 0);
    shader_set_uniform_f(u_texel_size,        texel_w, texel_h);

    surface_set_target(srf_pong);
        draw_surface(srf_ping, 0, 0);
    surface_reset_target();

// 3rd pass: blur vertically
// srf_pong -> srf_ping
    shader_set_uniform_f(u_blur_vector,        0, 1);

    surface_set_target(srf_ping);
        draw_surface(srf_pong, 0, 0);
    surface_reset_target();

gpu_set_tex_filter(false);

// 4th pass: Blend bloom surface with app surface
// AppSrf & srf_ping -> screen
shader_set(shader_bloom_blend);
    shader_set_uniform_f(u_bloom_intensity, bloom_intensity);
    shader_set_uniform_f(u_bloom_darken, bloom_darken);
    shader_set_uniform_f(u_bloom_saturation, bloom_saturation);
    texture_set_stage(u_bloom_texture, bloom_texture);
    gpu_set_tex_filter_ext(u_bloom_texture, false);


//Handle additional surfaces
#region Bloom + one other
/*
    // surface_set_target(srf_moblur);
    surface_set_target(srf_hue);
    draw_surface(application_surface, 0, 0);
    surface_reset_target();

    //Final part
    // shader_set(shd_moblur);
    shader_set(shd_hue);

    // shader_set_uniform_f(global.upos,global.mob_x,global.mob_y)//x,y
    // draw_surface_stretched(srf_moblur, 0, 0, gui_w, gui_h); //draw the final result surface, srf_moblur
    shader_set_uniform_f(hue_pos_uni, hue_amt)//x,y
    draw_surface_stretched(srf_hue, 0, 0, gui_w, gui_h); //draw t

shader_reset();
*/

#endregion

#region Both - no bloom
surface_set_target(srf_hue)
shader_set(shd_hue)
shader_set_uniform_f(hue_pos_uni, hue_amt)//x,y
draw_surface(application_surface, 0, 0)

surface_reset_target();
shader_reset();

surface_set_target(application_surface);
shader_set(shd_moblur);
shader_set_uniform_f(global.upos,global.mob_x,global.mob_y)//x,y
draw_surface(srf_hue, 0, 0);

surface_reset_target();
shader_reset();

draw_surface_stretched(application_surface, 0, 0, gui_w, gui_h);
#endregion

#region Bloom + both shaders [doesn't work]
/*
    surface_set_target(srf_hue);
    shader_set(shd_hue);
    shader_set_uniform_f(hue_pos_uni, hue_amt)//x,y

    draw_surface(application_surface, 0, 0);
    surface_reset_target();
    shader_reset();

    surface_set_target(srf_moblur);
    shader_set(shd_moblur);
    shader_set_uniform_f(hue_pos_uni, hue_amt)//x,y
    draw_surface(application_surface, 0, 0);
    surface_reset_target();

    draw_surface_stretched(srf_moblur, 0, 0, gui_w, gui_h); //draw t
    shader_reset();
*/
#endregion
Thanks for any input.
 
Last edited:

Xor

@XorDev
A lot to look over, so I may have missed something, but can you draw the result of one shader to a surface and draw that with the next shader? If not have you considered combining the two shaders?
 

Biosyn

Member
Thanks for the reply Xor.

Yes, with the current setup, I can have the motion blur and hue shaders drawn on surfaces one after the other - one using its own surface, and the other using the application surface.

This part refers to that action:

GML:
#region Both - no bloom
surface_set_target(srf_hue)
shader_set(shd_hue)
shader_set_uniform_f(hue_pos_uni, hue_amt)//x,y
draw_surface(application_surface, 0, 0)

surface_reset_target();
shader_reset();

surface_set_target(application_surface);
shader_set(shd_moblur);
shader_set_uniform_f(global.upos,global.mob_x,global.mob_y)//x,y
draw_surface(srf_hue, 0, 0);

surface_reset_target();
shader_reset();

draw_surface_stretched(application_surface, 0, 0, gui_w, gui_h); 
#endregion
The problem of course is that when I do this, the bloom MRT shader doesn't work, but the motion blur and hue shaders both work (hence the code region titled as "Both - no bloom".

I'd like to avoid combining any shaders since the goal is to setup a system where multiple shaders can be applied to the entire screen easily and flexibly. I think using surfaces is the ideal approach here, because, as I understand it, in combining shaders, there would be a need to combine each new shader that was intended to be applied to the screen. With surfaces, it's a more straight forward approach of simply creating a new surface for a new shader.

EDIT 1: Also, apologies since I failed to mention that the motion blur shader was actually developed by you. OP has been corrected.

EDIT 2: I can provide the yyz export if it helps.
 
Last edited:

Xor

@XorDev
In case it's useful to anyone else, here's the code we found:

GML:
//Apply bloom to application surface (on motion blur surface)
surface_set_target(srf_moblur);
shader_set(shader_bloom_blend);
shader_set_uniform_f(u_bloom_intensity, bloom_intensity);
shader_set_uniform_f(u_bloom_darken, bloom_darken);
shader_set_uniform_f(u_bloom_saturation, bloom_saturation);
texture_set_stage(u_bloom_texture, bloom_texture);
gpu_set_tex_filter_ext(u_bloom_texture, false);
draw_surface(application_surface, 0, 0);
surface_reset_target();   
    
//Apply motion blur to motion surface (on hue surface)
surface_set_target(srf_hue);
shader_set(shd_moblur);
shader_set_uniform_f(global.upos,global.mob_x,global.mob_y);
draw_surface_stretched(srf_moblur, 0, 0, gui_w, gui_h);
surface_reset_target();
    
//Apply hue shift to hue surface.
shader_set(shd_hue);
shader_set_uniform_f(hue_pos_uni, hue_amt);
draw_surface_stretched(srf_hue, 0, 0, gui_w, gui_h);
shader_reset();
Essentially we're just drawing one shader effect to a surface, then using that surface to apply another shader and drawing that to the next surface. You can do this as many times as necessary and in some cases, you can use just two surfaces for all the effects. Just by "ping-ponging" or alternating back and forth between them with different shader passes.
 

Biosyn

Member
Just tested it out and it works perfectly!

Didn't realize the bloom blend shader needed to be applied in that way to the motion blur surface.

Thanks again.
 
  • Like
Reactions: Xor
Top