I'm currently working on a metaball shader effect for a projectile system (Example here (http://philippseifried.com/blog/2011/07/24/flash-effects-creating-metaball-style-effects-in-as3/)).
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:
(http://img248.imageshack.us/img248/867/blobexample.png)
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
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.
In:
vec4 spritepixel = texture2D(projectiletex, gl_TexCoord[0].xy);
vec4 bgpixel = texture2D(bgtex, gl_TexCoord[0].xy);
The TexCoords are in normalized device coordinates (0-1.0). You just need to scale the screen coordinates down to size (and flip the y coord.)
ivec2 b_tex = textureSize2D(bgtex, 0) ;
vec2 coord = gl_FragCoord.xy/b_tex ;
coord.y = 1.0f-coord.y ;
vec4 spritepixel = texture2D(projectiletex, gl_TexCoord[0].xy);
vec4 bgpixel = texture2D(bgtex, coord);
I'm a bit confused on the specifics of what you're doing with the projectile texture.
Hrmmm, now I'm having another issue. If I only draw to the sf::RenderTexture, it ends up with garbage all over the texture, but if I do any drawing to the sf::RenderWindow, it works fine.
In my projectile manager:
sf::Shader* projectileShader = ShaderManager::Get()->getEffect(blendShaderIndex); //The blend shader is loaded earlier and then accessed using an index
if(projectileShader )
projectileShader ->setParameter("bgtex", projTexture->getTexture()); //projTexture is the sf::RenderTexture
sf::Vertex line[2] = {a, b};
line[0].color = sf::Color::Transparent;
line[1].color = sf::Color::Transparent;
for(unsigned int i = 0; i < projectiles.size(); i++)
{
projectiles[i]->render(); //Renders all projectiles onto the projTexture
//RenderWindow.draw(line, 2, sf::Lines); //Works when this line is uncommented
}
//Render projTexture onto game screen using threshold shader
In projectiles:
void Projectile::render()
{
sf::Shader* shader = ShaderManager::Get()->getEffect(blendShaderIndex);
if(shader)
{
//sf::Sprite projectile, defined elsewhere. Position, scaling, etc set here
shader->setParameter("projectiletex", sf::Shader::CurrentTexture);
sf::RenderStates state;
state.blendMode = sf::BlendNone;
state.shader = shader;
projTexture->draw(projectile, state);
}
}
So, now the problem is when the draw(sf::Lines) is not done, the projTexture gets garbage over it, but when draw(sf::Lines) is done, it works fine (but at a framerate cost).
Here is an example of what I mean:
(http://img163.imageshack.us/img163/8679/errorox.png)
The one on the left is with the draw(sf::Lines) in, and is how it is supposed to look (The different colours are from different threshold values). The one on the right is when I remove draw(sf::Lines).
Any help would be appreciated