2D bloom/godray's effect?

Discussion in 'Game Design, Development And Publishing' started by Niels, Aug 28, 2019.

  1. Niels

    Niels Member

    Joined:
    Jun 22, 2016
    Posts:
    820
    How would something like this be archieved in gamemaker?



    I guess it's done with some sort of bloom shader that's set on a background layer, but beyond that I'm clueless.
     
  2. sitebender

    sitebender Member

    Joined:
    Sep 13, 2016
    Posts:
    813
    Shaders and surfaces. I'm uncertain about god rays, but the bloom is like a blur that blackens the darks and brightens the lights. It's also best if there are shadows or shaded backgrounds with illumination to make that bloom rather than brighter environments.

    Someone else can fill you in on god rays, it's more of a light angle vs obstructions to create shadows. There are quite a few similar god rays and blooms in the marketplace. It takes a while to master.
     
    Niels likes this.
  3. rIKmAN

    rIKmAN Member

    Joined:
    Sep 6, 2016
    Posts:
    4,453
     
    Niels likes this.
  4. MishMash

    MishMash Member

    Joined:
    Jun 20, 2016
    Posts:
    376
    A simple, but effective god ray shader can be implemented using simple ray-marching.
    There are a few common approaches, but generally, you can either implement something that for each pixel, walks towards the centre of the lightsource, adding colours as it goes.

    The idea is that you run the shader on a surface without a background, so that as you the ray marches across "empty" spots or white spots, you accumulate much more brightness than if you walk over the scene.
    Depending on whether you want colour bleeding or not, you can simply just sample the alpha channel (alpha 0 = fully white, alpha 1 = black - no contribution).

    Bit of simple pseudo code would look something like this:

    Code:
    uniform vec2 lightpos; // screenspace position of lightsource
    #define STEPS 24
    ...
    // Calculate vector towards light
    vec2 v_towards_light = (lightpos - v_vTexcoord)/STEPS ; // Get the unit vector that will reach the centre of our light source in the given number of steps
    vec4 accumulated_colour = vec4(0.0,0.0,0.0,0.0);
    vec2 sample_coord = v_vtexcoord; // Our current sample coordinate
    
    for(int i=0; i < STEPS ; i++){
               // Sample at current location, then step closer to the light
               vec4 sample = texture2D(gm_BaseTexture, sample_coord);
               accumulated_colour  += sample.rgb*(1.0-sample.a);  // Weight sample based on inverse alpha -- We want the gaps in the background to be "bright spots"
               sample_coord  += v_towards_light;
    }
    gl_FragColor = accumulated_colour*0.5  / float(SAMPLES); // Need to multiply the end result by some factor to reduce brightness. You would need to play around with this. (Also weighted by sample count)
    
    A more advanced version could weight the samples based on how close they are to the light -- you can basically keep on throwing in features like this to adjust the reuslt
    Code:
    uniform vec2 lightpos; // screenspace position of lightsource
    #define STEPS 24
    ...
    // Calculate vector towards light
    vec2 v_towards_light = (lightpos - v_vTexcoord)/STEPS ; // Get the unit vector that will reach the centre of our light source in the given number of steps
    vec4 accumulated_colour = vec4(0.0f,0.0f,0.0f,0.0f);
    vec2 sample_coord = v_vtexcoord; // Our current sample coordinate
    float intensity = 0.0f;
    
    for(int i=0; i < STEPS ; i++){
               // Increase sample contribution
               intensity += 1.0f/float(STEPS);
               // Sample at current location, then step closer to the light
               vec4 sample = texture2D(gm_BaseTexture, sample_coord);
               accumulated_colour  += sample.rgb*(1.0f-sample.a);
               sample_coord  += v_towards_light;
    }
    gl_FragColor = accumulated_colour*0.5  / float(SAMPLES); // Need to multiply the end result by some factor to reduce brightness. You would nede to play around with this
    
    If we wanted a version with colour bleeding, you could adjust the accumulation process to allow partial colour in:
    Code:
    uniform vec2 lightpos; // screenspace position of lightsource
    #define STEPS 24
    ...
    // Calculate vector towards light
    vec2 v_towards_light = (lightpos - v_vTexcoord)/STEPS ; // Get the unit vector that will reach the centre of our light source in the given number of steps
    vec4 accumulated_colour = vec4(0.0f,0.0f,0.0f,0.0f);
    vec2 sample_coord = v_vtexcoord; // Our current sample coordinate
    float intensity = 0.0f;
    
    for(int i=0; i < STEPS ; i++){
               // Increase sample contribution
               intensity += 1.0f/float(STEPS);
               // Sample at current location, then step closer to the light
               vec4 sample = texture2D(gm_BaseTexture, sample_coord);
               accumulated_colour  += sample.rgb*(1.5f-sample.a);
               sample_coord  += v_towards_light;
    }
    gl_FragColor = accumulated_colour*0.5  / float(SAMPLES); // Need to multiply the end result by some factor to reduce brightness. You would nede to play around with this
    
    This is the sort of thing where you really have to play around with all of your inputs and your process until you get the desired result. Its a relatively simple concept, but there are lots of small details you can just to change the softness/hardness, colours, brightness, intensity etc; of the result. The only key performance indicator is that the greater the value of SAMPLES, the higher quality the result, but the worse the performance.
     
    JeffJ and DukeSoft like this.
  5. NightFrost

    NightFrost Member

    Joined:
    Jun 24, 2016
    Posts:
    1,926
    Interesting stuff. I suppose the STEPS count should be kept well in check as surface size increases. I've noticed, when playing around with blur shaders, that looping getting out of hand is a great way to tank performance.
     
  6. MishMash

    MishMash Member

    Joined:
    Jun 20, 2016
    Posts:
    376
    Yep definitely, there are a few considerations here, total number of steps affects the computational performance, however, if your distance to the source is too large, then the gap between segments becomes relevant. If this is too large, then you will hit a texture cache crash, where too many samples occur outside of local texture space. (Ideally, lots of similar samples will share similar results within the cache).
    (Locally near by fragments will execute in SIMD, meaning that as they are likely to sample the same areas as they raymarch, the first fragment will pull a block of texture data into the cache, and others will be able to sample that as well. -- This is why performance is generally okay in this case, despite it being a raymarch technique).

    A more efficient version of this could involve not stepping towards the light completely, but rather within a radius of the light, so we could make our step-size half of what it is. Godrays would still retain direction in that instance.
    There are plenty of other ways to improve efficiency, but this should be a starting point.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice