I'm currently working on a metaball shader effect for a projectile system (Example
here).
Essentially what happens is I render preblurred particles to sf::RenderTexture, then render that texture to the main screen using a shader which does the thresholding part of the effect (Checks if fragment is brighter than certain value, if so returns specific colour). I have to do this for 5 different output colours, so rather than draw to 5 separate sf::RenderTextures, and then draw all of those separately, I figured I'd pack the data a bit more efficiently.
Since I only need an intensity value to be passed to the threshold shader, I can store that only in the red channel of the sf::RenderTexture, and then store a value in the green channel so the threshold shader can determine which of the 5 output colours it should use. To do this, I use 5 different sprites, like this:
Where red = alpha and goes from 0 to 255 the closer it gets to the center. Blue channel = 0 for every pixel, and green channel = 51 for this sprite (Each sprite is some multiple of 0.2, so I can multiply the green channel by 5 to get an integer between [0, 4])
Ultimately, it would mean I could process all 5 colours of projectiles in a single sf::RenderTexture and threshold shader pass.
However, the problem is, when the projectile sprites are drawn to the sf::RenderTexture, I have issues with the blending. Using the default BlendAlpha, the green tag values get blended, resulting in incorrect colours being picked. Using BlendNone writes the the top most (last drawn) projectile's green channel correctly, but messes with the blending of the red channel.
I've searched a bit, and unfortunately it seems I can't use a custom blend function to do BlendAlpha on the r/a channels and BlendNone on the g/b channels.
So instead, I figured I'd use BlendNone, but write a shader to blur the sprite with what is already there on the render texture. So, I pass the sf::RenderTexture::getTexture() to the shader, along with the sprite and do the following:
uniform sampler2D projectiletex;
uniform sampler2D bgtex;
void main()
{
vec4 spritepixel = texture2D(projectiletex, gl_TexCoord[0].xy);
vec4 bgpixel = texture2D(bgtex, gl_TexCoord[0].xy);
vec4 res;
res.r = spritepixel.r * spritepixel.a + bgpixel.r * (1.0 - spritepixel.a);
res.g = spritepixel.g;
res.b = 0.0;
res.a = spritepixel.a * spritepixel.a + bgpixel.a * (1.0 - spritepixel.a);
gl_FragColor = res;
}
However, this blends the entire render texture into the confines of the sprite, not just the region the sprite covers, resulting in each projectile becoming a square texture on the sf::RenderTexture, and without any of the blending I wanted. So I figured I perhaps I had to use gl_TexCoord[1].xy for one of the textures, but this does not work.
I've tried setting the gl_TexCoord[1] through a vertex shader, but it did not work (I doubt I did it right).
So basically, how do I use two textures (of different sizes) in the one shader. (Or if anyone knows a simpler way to blend the red channel but just copy the green channel, I'd love to hear it).
For the sake of completeness, here is the threshold shader that takes the sf::RenderTexture and draws on the main game screen:
#version 120
uniform sampler2D imagetex;
vec3 colours[5] = vec3[5](vec3(230.0/255.0, 0, 0),
vec3(1.0, 0.88, 0),
vec3(0, 0.16, 0.898),
vec3(0, 230.0/255.0, 0),
vec3(0.9, 0.9, 0.9));
void main()
{
vec4 pixel = texture2D(imagetex, gl_TexCoord[0].xy);
float threshold = pixel.r;
int currentColour = int(pixel.g * 5.0); // All green channels should be one of (0.0, 0.2, 0.4, 0.6 or 0.8)
//If fragment is "bright" enough, set it to the defined colour, otherwise discard
if(threshold > 0.5)
gl_FragColor = vec4(colours[currentColour], 1.0);
else
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
Thanks