Shaders [SOLVED] Apply Shaders effects to particles

S

spaceman_games

Guest
Hey Gang,

Not sure if this is a loaded question or not because I'm still learning shaders.

However is it possible to apply a shader effect to a particle? I read that it could be done through surfaces?

Thanks
 

Relic

Member
S

spaceman_games

Guest
Sweet, I got it working, had to read the manual pretty hard to understand the logic order.

However one issue is that my particles are being drawn onto the surface over a black box, is there a way to have transparency within the surface so it just shows my particles. otherwise this system may not work for me.

Here are the before and After Shots
https://imgur.com/gallery/nq8kHkd

 
Last edited by a moderator:
Do you mean you are first clearing the surface with the color black? Or that drawing the particles using the shader is adding the color black to the surface?
 
S

spaceman_games

Guest
Here are my events. I'm clearing the surface in the draw begin event. It creates the surface in a black box, I just want the particles. The shader works, however, also making my particles look odd in the box, like my last reply. https://imgur.com/gallery/nq8kHkd the image is a screengrab from the top of my game. other assets are below it.

Create Event
Code:
/// @description Create Event

scr_aberration_create();

global.particle_surface = false;
Draw End event

Code:
/// @description Draw End event

draw_surface(application_surface,x,y,);

#region // Aberration
    if (shader = 1) {
      shader_set(sh_aberration_wild);
      shader_set_uniform_f(shakex, global.shake_x);
      shader_set_uniform_f(shakey, global.shake_y);
    }
  
if (!surface_exists(global.particle_surface)) {
    global.particle_surface = surface_create(display_get_width(),display_get_height());
}

if surface_exists(global.particle_surface){
    
   part_system_automatic_draw(global.ps, false);
 
   surface_set_target(global.particle_surface);
 
   part_system_drawit(global.ps);
 
   surface_reset_target();
   }
 
   if surface_exists(global.particle_surface) draw_surface(global.particle_surface,0,0);

    shader_reset();
#endregion
Draw Begin Event

Code:
///Draw Begin Event
if surface_exists(global.particle_surface)
{
surface_set_target(global.particle_surface); //set the surface to draw to
draw_clear_alpha(c_black,0);
surface_reset_target(); //set the surface to draw to
}
 
Because you're drawing both the particles and the particle surface using the shader, you are going to be applying whatever the shader does twice. I suspect this is not what you were intending to do. I don't know if it would explain your problem, because I have no idea what your shader is doing.

It also doesn't look like you're clearing the surface each frame, meaning whatever you draw this frame will be added to what is already on the surface.

It's probably best to turn off automatic drawing of the particle system just once, instead of every frame.

If your particles contain any semi-transparency, they will not draw correctly to a surface using the normal blending mode. You would need to use premultiplied alpha to fix this particular issue.
 
S

spaceman_games

Guest
@flyingsaucerinvasion Ah, thank you, I fix those issues and it worked like a charm.

For the premultiplied alpha, is this done within the draw event using blend modes? Or rather in my shader? Still struggling to get the alpha working properly.

Here is my fragment Shader it's a simple color aberration effect.

Code:
//
// Simple passthrough fragment shader
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform float shakex;
uniform float shakey;

void main()
{
    gl_FragColor = vec4(texture2D(gm_BaseTexture, vec2(v_vTexcoord.x - shakex*0.142, v_vTexcoord.y - shakey*0.0948)).r, texture2D(gm_BaseTexture, vec2(v_vTexcoord.x + shakex*0.0943, v_vTexcoord.y - shakey*0.05829)).g, texture2D(gm_BaseTexture, vec2(v_vTexcoord.x - shakex*0.042, v_vTexcoord.y + shakey*0.0624)).b, 1.0);
}
 
Premultiplied alpha can be done one of two ways...

With either method, the blending mode needs to be changed to draw_set_blend_mode_ext(bm_one, bm_inv_src_alpha);

With the first method, you can use the "premutiply alpha" option in the sprite editor. I believe this cannot be reversed.

So the better option is to use a shader. In a fragment shader, all you have to do is mutliply the rgb channels by the alpha channel.

Code:
        vec4 Colour = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
        Colour.rgb *= Colour.a;
        gl_FragColor = Colour;
Premutiplied alpha needs to be used when drawing semi-transparent things onto a surface without locking the alpha channel. Alpha will be added in the same way as photoshop's normal blending mode.
 
S

spaceman_games

Guest
Bada Bing Bada Boom, Mr. @flyingsaucerinvasion has got the means for articulation! It worked.

Had to look at it for a min to understand how to merge it with the code I had.

I understand how shaders & surfaces work much better now. Thank you so much for the help you're amazing! Cheers m8

Code:
//
// Simple passthrough fragment shader
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
uniform float shakex;
uniform float shakey;

void main()
{

     
vec4 Colour = v_vColour * vec4(texture2D(gm_BaseTexture, vec2(v_vTexcoord.x - shakex*0.142, v_vTexcoord.y - shakey*0.0948)).r,texture2D(gm_BaseTexture,vec2(v_vTexcoord.x + shakex*0.0943, v_vTexcoord.y - shakey*0.05829)).g, texture2D(gm_BaseTexture,vec2(v_vTexcoord.x - shakex*0.042, v_vTexcoord.y + shakey*0.0624)).b, 1.0);
Colour.rgb *= Colour.a;
gl_FragColor = Colour;
       

}
 
Top