I have a pretty good glow shader going now thanks to some tips from people here.
However there are a few things that bother me that could maybe be better.
#1 Definition of the particles
A lot of times if the particles are moving a lot it just looks like a single sheet of color. The image below is zoomed in so you can see a bit more definition, but zoomed out and real time it just looks like a sheet of sorts
#2 The particles don't look like they are neon
I mean I don't want the effect to be huge but when they are grouped together I would hope for them to have some white highlighting in the glow.
It looks like this:
When it should look more like this (this was just a mockup by blending in an additive copy of it with more contrast) when particles are grouped together [However singular particles shouldn't have white highlights]:
Here is my current rendering process:
I have a couple FBO's with texture attachments:
- Drawing Frame: Never cleared, up-scaled for the purposes of anti-aliasing
- Smaller Frame: Cleared each frame, exact resolution of screen, Draws the drawing frame on it with the darkest parts cut out
- Horizontal Blur: Cleared each frame, does a horizontal blur (lower resolution than screen)
- Vertical Blur: Cleared each frame, blurs the horizontal blur vertically (lower resolution then screen)
- Front buffer: Draws the Smaller frame, and Vertical Buffers on top of each-other to the screen.
So A typical render might look like this (I apologize for the misalignment and cropping, the Xcode frame debugger allows you to peek at FBO's but not export the texture inside of them I had to use screenshots)
To combine the two textures I am using this
gl_FragColor = vec4(clamp(texture2D(s_texture, v_texcoord).xyz, 0.,
1.) + (clamp(texture2D(s_texture2, v_texcoord).xyz, 0., 1.) * 1.5),
1.0);
And for the two blurs I am using code like this, a lot of this is pre-computed but to make it easier to read I put how I computed it.
float Gaussian(float x, float deviation)
{
return (1.0 / sqrt(2.0 * 3.141592 * deviation)) * exp(-((x * x) / (2.0 * deviation)));
}
vec3 blurS15()
{
vec3 blr = vec3(0.0);
float scalart = (1 / screenResolution.x) * (1.0 / 7.0) * 14.0;
float deviationScale = 1.2;
float finalScalar = 1.1;
blr += texture2D(s_texture, (v_texcoord + vec2(7.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(7.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(6.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(6.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(5.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(5.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(4.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(4.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(3.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(3.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(2.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(2.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(1.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(1.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(0.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(0.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(-1.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(-1.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(-2.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(-2.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(-3.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(-3.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(-4.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(-4.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(-5.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(-5.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(-6.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(-6.0, 7.0 * deviationScale) ;
blr += texture2D(s_texture, (v_texcoord + vec2(-7.0 * scalart, 0.0))).xyz * finalScalar * Gaussian(-7.0, 7.0 * deviationScale) ;
return blr; //This gets put into gl_FragColor
}
Now while the issue could be in how I calculate the blur, and combine it I think it is more likely that the issue is in the way I render the particles. Basically each particle is rendered with a circle and a rectangle each frame. The dot marks where it is, and the rectangle connects it the the previous frame. Each new frame the previous frame is faded and then drawn over.
Here is a diagram of the process, each new hue group represents a new frame, sorry for the childish look of the colors.
I do this using two draw calls, one that draws the rectangles (GL_TRIANGLES with indices), and one that draws the circles (GL_POINTS with outsides alpha set to 0).
While this allows me to quickly simulate an accumulation buffer it comes with some drawbacks, if these can be dealt with without compromising speed then it would be nice to change them.
- No actual depth sorting, although the particles could easily have z values it is futile because opengles 2.0 will go black if you don't clear the depth buffer each frame, so it cant really work with an accumulation buffer.
- No additive drawing
- Because there are two separate draw calls, even with that setting on nothing seems to draw additively inside of the same draw call
- Even if I did the area where the circle and rectangle meet would be extra bright which is undesirable
The additive mode I tried was
glBlendFunc(GLenum(GL_ONE), GLenum(GL_ONE))
glEnable(GLenum(GL_BLEND));
To be specific when I said it didn't work I tried disabling it before fade, and then enabling it afterwards. It caused everything to look weird almost as if there were no trails. And there didn't seem to be highlights where particles intersect. However if I had it enabled the whole time then it did look like additive blending. Only the previous frame never faded because you can't draw black in add blend mode.
If you can think of any way I can get more highlighting especially when particles collide, or otherwise make the glow effect look better please let me know.
I am very curious if there is a way to still do depth sorting with an accumulation buffer/a way to not have to clear the depth buffer.