OK I've been trying to figure this out and I just can't figure out what is going on. I've created a metaball renderer that uses pixel shaders to achieve the effect. I'm now trying to combine this renderer with standard sprite batch stuff, specifically I want to draw my metaballs OVER some sprites.
For some reason whenever I try this I end up with this sort of thing:
Obviously the purple colour is no good as I want all of that part to be transparent so I check it out in PIX and found that XNA seems to be clearing to this weird colour whenever I set a new render target. I thought a call to Clear(Color.TransparentBlack) after setting the render target would rectify this but it does not.
A bit of background on my metaball rendering stuffs:
I have a single screen sized offscreen render target and a screen sized texture. For each metaball I draw to the render target the influence per pixel from that ball and blend the result with the texture. The result being an "influence map" containing the summed influences for each metaball on each pixel. Of course this mean I'm constantly having to change render targets in order to save the contents of the render target to the texture after each pass.
Once I have the influence map I do one final pass which will colour the pixels based upon the influence values it samples from the influence texture.
Here's the C# code to illustrate what I'm on about:
// Setup some mip mapping states so the drawing doesn't look shit
graphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Linear;
graphicsDevice.SamplerStates[0].MinFilter = TextureFilter.Linear;
graphicsDevice.SamplerStates[0].MipFilter = TextureFilter.Linear;
graphicsDevice.SamplerStates[0].MipMapLevelOfDetailBias = 0.0f;
graphicsDevice.SamplerStates[0].MaxMipLevel = 0;
for (int i = 0; i < pixels.Length; i++)
{
pixels = Color.TransparentBlack.PackedValue;
}
graphicsDevice.Textures[0] = null;
influenceMap.SetData<uint>(pixels);
//influenceMap = new Texture2D(graphicsDevice, textureWidth, textureHeight);
//graphicsDevice.Clear(Color.Black);
// Create an influence map on the GPU
foreach (Metaball aBall in balls)
{
graphicsDevice.SetRenderTarget(0, offScreen);
graphicsDevice.Clear(Color.TransparentBlack);
graphicsDevice.VertexDeclaration = new VertexDeclaration(graphicsDevice, VertexPositionColor.VertexElements);
influenceMapEffect.Parameters["influenceMap"].SetValue(influenceMap);
influenceMapEffect.Parameters["metaball"].SetValue(new Vector3(aBall.X, aBall.Y, aBall.Radius));
influenceMapEffect.Begin();
influenceMapEffect.CurrentTechnique.Passes[0].Begin();
graphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, quad, 0, 4, indices, 0, 2);
influenceMapEffect.CurrentTechnique.Passes[0].End();
influenceMapEffect.End();
graphicsDevice.SetRenderTarget(0, null);
influenceMap = offScreen.GetTexture();
}
//graphicsDevice.Present();
graphicsDevice.SetRenderTarget(0, null);
// Draw the metaballs based upon the influence map
//graphicsDevice.Clear(Color.TransparentBlack);
graphicsDevice.VertexDeclaration = new VertexDeclaration(graphicsDevice, VertexPositionColor.VertexElements);
metaballEffect.Parameters["influenceMap"].SetValue(influenceMap);
metaballEffect.Begin();
metaballEffect.CurrentTechnique.Passes[0].Begin();
graphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, quad, 0, 4, indices, 0, 2);
metaballEffect.CurrentTechnique.Passes[0].End();
metaballEffect.End();
graphicsDevice.Present();
And the pixel shader for creating the influence map:
void PixelShader(inout float4 color : COLOR0, in float2 screenPos : VPOS)
{
float influenceOn = (metaball.z / (float)((screenPos.x - metaball.x) * (screenPos.x - metaball.x) + (screenPos.y - metaball.y) * (screenPos.y - metaball.y)));
float4 influenceSum = float4(influenceOn, influenceOn, influenceOn, 1.0f);
influenceSum += tex2D(textureSampler, (screenPos / ViewportSize));
//influenceSum.w = metaball.w;
color = influenceSum;
}
and the pixel shader for colouring the balls:
void PixelShader(inout float4 color : COLOR0, in float2 screenPos : VPOS)
{
float4 influenceSum = tex2D(textureSampler, screenPos / ViewportSize);
//color = influenceSum;
float4 ballColour;
ballColour = float4(0.0f, 0.9f, 0.4f, 1.0f);
color = float4(0, 0, 0, 0);
if (influenceSum.x >= 0.4f)
{
color += ballColour;
color.z *= ddx(influenceSum.x);
}
else if (influenceSum.x >= 0.15f && influenceSum.x < 0.4f)
{
color += float4((ballColour.x + 0.03) * influenceSum.x - 0.19, (ballColour.y + 0.1) * influenceSum.x - 0.19, (ballColour.z - 0.25) * influenceSum.x - 0.19, influenceSum.x - 0.19) * 4;
}
}
It's not a bug... it's a feature!