Changing the blend of a particle system

Luke Peña

Member
TLDR: How do I change the blend of a particle system using the part_system_drawit function? How do I avoid harsh outlines with the part_system_drawit function on a surface?

So right now I'm drawing particle systems to surfaces using the part_system_drawit function. But I'm having a few issues. These are the roadblocks I hit in order:

  • When particles drawn this way fade, they have a black outline for some reason, as if their particle "surface" is clearing to black. For these particles, it worked to draw them with additive blending. That solved it for a while.
  • I have a day night cycle, and these white particles need to tint slightly in the night. My initial plan was to use draw_set_color before using part_system_drawit, but apparently the drawit function has blending built into it. You can't change its color with that.
  • I tried using shaders to tint it, and this is the only one that kind of works. The problem is that the moment I start tinting them, because they are drawn with additive blending, they begin to disappear.
  • So I can used shaders to darken particles drawn to a surface with the drawit function, but it has to be normal blending. But with normal blending and the drawit function, harsh black outlines are added to semi-transparent particles.

Does anybody have experience with this function? This is the first project I've used it on, and I can't seem to get it to work with me.

EDIT: So I could solve this if I understood a simple thing.
I found out that the reason for that dark outline was because the particles are fading while being drawn on a surface. And when I draw anything to a surface with an alpha value between 0 and 1 (like .5), it doesn't blend that alpha value on top of an opaque background - it punches a hole through the surface and makes the entire surface transparent.

In other words, the alpha for any given pixel on a surface is determined by the alpha top pixel drawn there. If I could figure out why that is and draw my surfaces "normally", then this issue would be resolved.
 
Last edited:

Neptune

Member
I'm not really sure, but draw_clear_alpha(c_black,0); may be what you need!
Maybe try messing with all of these:
Code:
part_type_alpha1(particle_index,1);
part_type_blend(particle_index,0);
draw_clear_alpha(c_black,0);
 

Luke Peña

Member
I'm not really sure, but draw_clear_alpha(c_black,0); may be what you need!
Maybe try messing with all of these:
Code:
part_type_alpha1(particle_index,1);
part_type_blend(particle_index,0);
draw_clear_alpha(c_black,0);
Hmmm. The problem with that is that part_type_ functions are for defining a particle while draw_clear_black is a drawing function. Clearing a surface after defining a particle type wouldn't change anything, because the particle hasn't been emitted yet. :/

I have an idea. I'll test it and post back here.

EDIT:

So it's definitely the surface transparency issue. What I did was I created a shader that simply sets the alpha to 1.0 and then used that to draw the surface. So now the only problem is that I can't have any intentional transparency in the surface unless I subtract it first. Which is a problem, because I'm turning the surface into a texture. Hmmmmm.
 
Last edited:

CMAllen

Member
Look up alpha blending. When you draw to any surface other than the application surface, you're using that surface's alpha channel. You need to understand this because the application surface does not have an alpha channel. But the alpha channel is treated like any other color channel as far as the GPU is concerned -- everything gets multiplied together. So when you draw a red glow over a completely transparent black surface, the black is still multiplied into the red glow, growing more pronounced as the transparency of the glow increases. This problem becomes even more complicated when you start drawing partially transparent pixels over other partially transparent pixels. The alpha channel is still multiplied, when the expected behavior is closer to additive.

Now, unfortunately there is no one perfect solution to these problems. Each solution is instead a combination varying techniques. You can fill the background of your surface with a color similar to whatever you're drawing, for example. That lessens the overall impact of color bleed. In the case of drawing partially transparent sprites over what are supposed to be fully opaque finished areas, you can turn off writing to the alpha channel, thereby disabling alpha multiplication. If you need to combine sprites together into a final opaque sprite (such as pieces of a character's body), you can draw the whole sprite first in bm_add mode, then switch to back to bm_normal and disable writing to the alpha channel, then draw all the sprites again. Like I said, it's messy, and there's no one perfect solution.
 

Luke Peña

Member

Thanks for all of the info! I took a look at some of the options in the articles that Nocturne posted, and I noticed that my shaders solution (forcing all pixel alphas to 1.0) may end up being the best. Here is a screenshot of the game.

How it works is that I have a surface for the water where shading on the waves, particles for wakes, and the shadow of the boat are drawn. That surface is then turned into a texture for the code that draws the wave to use on some primitives that distort with the surface of the water. So when the surface of the water is being draw, it triggers the shader so that the top of the water has an alpha of 1.0 and the bottom has an alpha of 0.8. This is because I still want to be able to see anything lurking directly beneath the water and also to be able to partially see the underside of anything floating on the surface.
 
Top